From 681d98b40c66f964b5fe3b9ac1d886297ef59dd1 Mon Sep 17 00:00:00 2001 From: Tennessee Carmel-Veilleux Date: Mon, 29 Jan 2024 10:20:11 -0500 Subject: [PATCH] [nrf fromtree] Make TestEventTrigger delegate testable and extensible (#31724) * Make TestEventTrigger delegate testable and extensible - Make TestEventTriggerDelegate base class manage multiple sub-handlers. - Add unit tests for TestEventTriggerDelegate - Introduce an interface for generic TestEventTrigger handlers - Add Clear() method to Intrusive list to remove everything. - Document the fact that `emberAfHandleEventTrigger` should NOT be used - Update all examples as needed. - Renumber OTA and SmokeCO testevent trigger prefix to match new Matter 1.3 standard (first 16 bits == cluster ID being tested). - Clean-up dead code - Register OTA test event handler as intended. - Add TODOs for platforms to clean-up TestEventTriggerDelegate registration. - Updated BOOLCFG tests to use `--hex-arg` for PIXIT since `--int-arg` is too confusing with complex keys. Issue #31723 Testing done: - EEVSE Python tests using event triggers pass - SMOKECO YAML tests using event triggers pass - BOOLCFG Python tests using event triggers pass - TC_TestEventTrigger passes - New unit tests pass * Fix merge issue * Restyled by clang-format * Fix uninitialized variable access * Fix Ameba/Silabs build * Fix CopyString bound * More fixes of Ameba/Silabs build --------- Co-authored-by: Restyled.io Cherry-picked from: 4cd18252d7ce47561f17abdf81dabe51c0ba94b8 --- .../all-clusters-common/src/boolcfg-stub.cpp | 49 ++ .../all-clusters-common/src/smco-stub.cpp | 2 +- .../ameba/main/SmokeCOAlarmManager.cpp | 2 +- .../ameba/main/chipinterface.cpp | 6 +- .../ameba/main/include/SmokeCOAlarmManager.h | 2 - .../nrfconnect/main/AppTask.cpp | 10 +- .../src/EVSEManufacturerImpl.cpp | 520 ++++++++++++++++ .../energy-management-app/linux/README.md | 511 ++++++++++++++++ .../infineon/cyw30739/src/main.cpp | 10 +- .../nrfconnect/main/AppTask.cpp | 10 +- examples/light-switch-app/qpg/BUILD.gn | 155 +++++ examples/light-switch-app/qpg/src/AppTask.cpp | 575 ++++++++++++++++++ .../infineon/cyw30739/src/main.cpp | 10 +- .../lighting-app/nrfconnect/main/AppTask.cpp | 10 +- .../nxp/k32w/k32w0/main/AppTask.cpp | 4 +- .../nxp/k32w/k32w1/main/AppTask.cpp | 4 +- examples/lighting-app/qpg/BUILD.gn | 2 +- examples/lighting-app/qpg/src/AppTask.cpp | 12 +- .../lock-app/infineon/cyw30739/src/main.cpp | 10 +- examples/lock-app/nrfconnect/main/AppTask.cpp | 10 +- examples/lock-app/qpg/BUILD.gn | 2 +- examples/lock-app/qpg/src/AppTask.cpp | 12 +- .../AmebaTestEventTriggerDelegate.cpp | 6 - .../AmebaTestEventTriggerDelegate.h | 52 +- .../platform/esp32/common/Esp32AppServer.cpp | 10 +- examples/platform/linux/AppMain.cpp | 86 ++- examples/platform/linux/BUILD.gn | 23 +- examples/platform/nxp/se05x/linux/AppMain.cpp | 12 +- examples/platform/nxp/se05x/linux/BUILD.gn | 2 +- examples/platform/silabs/MatterConfig.cpp | 10 +- .../silabs/SilabsTestEventTriggerDelegate.cpp | 6 - .../silabs/SilabsTestEventTriggerDelegate.h | 53 +- examples/providers/DeviceInfoProviderImpl.cpp | 4 +- examples/pump-app/nrfconnect/main/AppTask.cpp | 10 +- .../nrfconnect/main/AppTask.cpp | 10 +- .../silabs/src/SmokeCoAlarmManager.cpp | 2 +- examples/thermostat/qpg/BUILD.gn | 154 +++++ .../window-app/nrfconnect/main/AppTask.cpp | 10 +- src/app/TestEventTriggerDelegate.h | 81 ++- src/app/chip_data_model.gni | 31 +- ...eConfigurationTestEventTriggerHandler.cpp} | 17 +- ...tateConfigurationTestEventTriggerHandler.h | 49 ++ .../EnergyReportingTestEventTriggerHandler.h | 70 +++ .../EnergyEvseTestEventTriggerHandler.h | 89 +++ ...> GenericFaultTestEventTriggerHandler.cpp} | 11 +- ... => GenericFaultTestEventTriggerHandler.h} | 11 +- .../general-diagnostics-server.cpp | 2 +- ...ate.cpp => OTATestEventTriggerHandler.cpp} | 9 +- ...elegate.h => OTATestEventTriggerHandler.h} | 10 +- .../SmokeCOTestEventTriggerDelegate.h | 83 --- .../SmokeCOTestEventTriggerHandler.h | 78 +++ src/app/tests/BUILD.gn | 1 + .../tests/TestTestEventTriggerDelegate.cpp | 192 ++++++ .../certification/Test_TC_SMOKECO_2_2.yaml | 6 +- .../certification/Test_TC_SMOKECO_2_5.yaml | 36 +- .../certification/Test_TC_SMOKECO_2_6.yaml | 20 +- src/lib/support/IntrusiveList.h | 8 + src/lib/support/tests/TestIntrusiveList.cpp | 30 + .../k32w0/DefaultTestEventTriggerDelegate.cpp | 11 - .../k32w0/DefaultTestEventTriggerDelegate.h | 3 - .../k32w1/DefaultTestEventTriggerDelegate.cpp | 11 - .../k32w1/DefaultTestEventTriggerDelegate.h | 3 - src/python_testing/TC_BOOLCFG_4_2.py | 177 ++++++ src/python_testing/TC_BOOLCFG_4_3.py | 286 +++++++++ src/python_testing/TC_BOOLCFG_4_4.py | 208 +++++++ src/python_testing/TC_BOOLCFG_5_1.py | 178 ++++++ src/python_testing/TC_BOOLCFG_5_2.py | 171 ++++++ 67 files changed, 3903 insertions(+), 357 deletions(-) create mode 100644 examples/all-clusters-app/all-clusters-common/src/boolcfg-stub.cpp create mode 100644 examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp create mode 100644 examples/energy-management-app/linux/README.md create mode 100644 examples/light-switch-app/qpg/BUILD.gn create mode 100644 examples/light-switch-app/qpg/src/AppTask.cpp create mode 100644 examples/thermostat/qpg/BUILD.gn rename src/app/clusters/{smoke-co-alarm-server/SmokeCOTestEventTriggerDelegate.cpp => boolean-state-configuration-server/BooleanStateConfigurationTestEventTriggerHandler.cpp} (60%) create mode 100644 src/app/clusters/boolean-state-configuration-server/BooleanStateConfigurationTestEventTriggerHandler.h create mode 100644 src/app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerHandler.h create mode 100644 src/app/clusters/energy-evse-server/EnergyEvseTestEventTriggerHandler.h rename src/app/clusters/general-diagnostics-server/{GenericFaultTestEventTriggerDelegate.cpp => GenericFaultTestEventTriggerHandler.cpp} (90%) rename src/app/clusters/general-diagnostics-server/{GenericFaultTestEventTriggerDelegate.h => GenericFaultTestEventTriggerHandler.h} (65%) rename src/app/clusters/ota-requestor/{OTATestEventTriggerDelegate.cpp => OTATestEventTriggerHandler.cpp} (80%) rename src/app/clusters/ota-requestor/{OTATestEventTriggerDelegate.h => OTATestEventTriggerHandler.h} (73%) delete mode 100644 src/app/clusters/smoke-co-alarm-server/SmokeCOTestEventTriggerDelegate.h create mode 100644 src/app/clusters/smoke-co-alarm-server/SmokeCOTestEventTriggerHandler.h create mode 100644 src/app/tests/TestTestEventTriggerDelegate.cpp create mode 100644 src/python_testing/TC_BOOLCFG_4_2.py create mode 100644 src/python_testing/TC_BOOLCFG_4_3.py create mode 100644 src/python_testing/TC_BOOLCFG_4_4.py create mode 100644 src/python_testing/TC_BOOLCFG_5_1.py create mode 100644 src/python_testing/TC_BOOLCFG_5_2.py diff --git a/examples/all-clusters-app/all-clusters-common/src/boolcfg-stub.cpp b/examples/all-clusters-app/all-clusters-common/src/boolcfg-stub.cpp new file mode 100644 index 0000000000..fe2ec90506 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/src/boolcfg-stub.cpp @@ -0,0 +1,49 @@ +/* + * + * 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 +#include + +#include + +using namespace chip; +using namespace chip::app::Clusters::BooleanStateConfiguration; +using namespace chip::DeviceLayer; + +bool HandleBooleanStateConfigurationTestEventTrigger(uint64_t eventTrigger) +{ + BooleanStateConfigurationTrigger trigger = static_cast(eventTrigger); + + switch (trigger) + { + case BooleanStateConfigurationTrigger::kSensorTrigger: + ChipLogProgress(Support, "[BooleanStateConfiguration-Test-Event] => Trigger sensor"); + SetAllEnabledAlarmsActive(1); + break; + + case BooleanStateConfigurationTrigger::kSensorUntrigger: + ChipLogProgress(Support, "[BooleanStateConfiguration-Test-Event] => Untrigger sensor"); + ClearAllAlarms(1); + break; + + default: + + return false; + } + + return true; +} diff --git a/examples/all-clusters-app/all-clusters-common/src/smco-stub.cpp b/examples/all-clusters-app/all-clusters-common/src/smco-stub.cpp index 4b1e883a6b..88c85f71fc 100644 --- a/examples/all-clusters-app/all-clusters-common/src/smco-stub.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/smco-stub.cpp @@ -15,7 +15,7 @@ * limitations under the License. */ -#include +#include #include #include diff --git a/examples/all-clusters-app/ameba/main/SmokeCOAlarmManager.cpp b/examples/all-clusters-app/ameba/main/SmokeCOAlarmManager.cpp index bede2bcf53..027bb055cc 100644 --- a/examples/all-clusters-app/ameba/main/SmokeCOAlarmManager.cpp +++ b/examples/all-clusters-app/ameba/main/SmokeCOAlarmManager.cpp @@ -16,7 +16,7 @@ */ #include "SmokeCOAlarmManager.h" -#include +#include #include using namespace chip; diff --git a/examples/all-clusters-app/ameba/main/chipinterface.cpp b/examples/all-clusters-app/ameba/main/chipinterface.cpp index 21280c6761..20de9cd1ff 100644 --- a/examples/all-clusters-app/ameba/main/chipinterface.cpp +++ b/examples/all-clusters-app/ameba/main/chipinterface.cpp @@ -140,8 +140,10 @@ static void InitServer(intptr_t context) static chip::CommonCaseDeviceServerInitParams initParams; #if CONFIG_ENABLE_AMEBA_TEST_EVENT_TRIGGER - static AmebaTestEventTriggerDelegate testEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + // TODO(#31723): Show to customers that they can do `Server::GetInstance().GetTestEventTriggerDelegate().AddHandler(xxx)` + // to add custom handlers during their app init, after InitServer. + static AmebaTestEventTriggerDelegate sTestEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; #endif initParams.InitializeStaticResourcesBeforeServerInit(); diff --git a/examples/all-clusters-app/ameba/main/include/SmokeCOAlarmManager.h b/examples/all-clusters-app/ameba/main/include/SmokeCOAlarmManager.h index 390ee679d0..af6177299a 100644 --- a/examples/all-clusters-app/ameba/main/include/SmokeCOAlarmManager.h +++ b/examples/all-clusters-app/ameba/main/include/SmokeCOAlarmManager.h @@ -22,8 +22,6 @@ #include #include -bool emberAfHandleEventTrigger(uint64_t eventTrigger); - class SmokeCoAlarmManager { public: diff --git a/examples/all-clusters-app/nrfconnect/main/AppTask.cpp b/examples/all-clusters-app/nrfconnect/main/AppTask.cpp index 9f47194340..f8d117790f 100644 --- a/examples/all-clusters-app/nrfconnect/main/AppTask.cpp +++ b/examples/all-clusters-app/nrfconnect/main/AppTask.cpp @@ -27,8 +27,9 @@ #include #include +#include #include -#include +#include #include #include @@ -204,9 +205,12 @@ CHIP_ERROR AppTask::Init() #endif static CommonCaseDeviceServerInitParams initParams; - static OTATestEventTriggerDelegate testEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + static SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; + static OTATestEventTriggerHandler sOtaTestEventTriggerHandler{}; + VerifyOrDie(sTestEventTriggerDelegate.Init(ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR); + VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sOtaTestEventTriggerHandler) == CHIP_NO_ERROR); (void) initParams.InitializeStaticResourcesBeforeServerInit(); - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; ReturnErrorOnFailure(chip::Server::GetInstance().Init(initParams)); AppFabricTableDelegate::Init(); diff --git a/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp b/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp new file mode 100644 index 0000000000..0f204671a9 --- /dev/null +++ b/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp @@ -0,0 +1,520 @@ +/* + * + * 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 +#include +#include +#include +#include + +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() +{ + /* Manufacturers should modify this to do any custom initialisation */ + + /* Register callbacks */ + EnergyEvseDelegate * dg = GetEvseManufacturer()->GetDelegate(); + if (dg == nullptr) + { + ChipLogError(AppServer, "EVSE Delegate is not initialized"); + return CHIP_ERROR_UNINITIALIZED; + } + + dg->HwRegisterEvseCallbackHandler(ApplicationCallbackHandler, reinterpret_cast(this)); + + /* + * This is an example implementation for manufacturers to consider + * + * For Manufacturer to specify the hardware capability in mA: + * dg->HwSetMaxHardwareCurrentLimit(32000); // 32A + * + * For Manufacturer to specify the CircuitCapacity in mA (e.g. from DIP switches) + * dg->HwSetCircuitCapacity(20000); // 20A + * + */ + + /* Once the system is initialised then check to see if the state was restored + * (e.g. after a power outage), and if the Enable timer check needs to be started + */ + dg->ScheduleCheckOnEnabledTimeout(); + + return CHIP_NO_ERROR; +} + +/* + * When the EV is plugged in, and asking for demand change the state + * and set the CableAssembly current limit + * + * EnergyEvseDelegate * dg = GetEvseManufacturer()->GetDelegate(); + * if (dg == nullptr) + * { + * ChipLogError(AppServer, "Delegate is not initialized"); + * return CHIP_ERROR_UNINITIALIZED; + * } + * + * dg->HwSetState(StateEnum::kPluggedInDemand); + * dg->HwSetCableAssemblyLimit(63000); // 63A = 63000mA + * + * + * If the vehicle ID can be retrieved (e.g. over Powerline) + * dg->HwSetVehicleID(CharSpan::fromCharString("TEST_VEHICLE_123456789")); + * + * + * If the EVSE has an RFID sensor, the RFID value read can cause an event to be sent + * (e.g. can be used to indicate if a user as tried to activate the charging) + * + * uint8_t uid[10] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE }; + * dg->HwSetRFID(ByteSpan(uid)); + */ + +CHIP_ERROR EVSEManufacturer::Shutdown() +{ + return CHIP_NO_ERROR; +} + +/** + * @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(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 + * which contain relevant info for the specific callback type + * + * @param arg - optional pointer to some context information (see register function) + */ +void EVSEManufacturer::ApplicationCallbackHandler(const EVSECbInfo * cb, intptr_t arg) +{ + EVSEManufacturer * pClass = reinterpret_cast(arg); + + switch (cb->type) + { + case EVSECallbackType::StateChanged: + ChipLogProgress(AppServer, "EVSE callback - state changed"); + break; + case EVSECallbackType::ChargeCurrentChanged: + ChipLogProgress(AppServer, "EVSE callback - maxChargeCurrent changed to %ld", + static_cast(cb->ChargingCurrent.maximumChargeCurrent)); + break; + case EVSECallbackType::EnergyMeterReadingRequested: + ChipLogProgress(AppServer, "EVSE callback - EnergyMeterReadingRequested"); + if (cb->EnergyMeterReadingRequest.meterType == ChargingDischargingType::kCharging) + { + *(cb->EnergyMeterReadingRequest.energyMeterValuePtr) = pClass->mLastChargingEnergyMeter; + } + else + { + *(cb->EnergyMeterReadingRequest.energyMeterValuePtr) = pClass->mLastDischargingEnergyMeter; + } + break; + + default: + ChipLogError(AppServer, "Unhandled EVSE Callback type %d", static_cast(cb->type)); + } +} + +struct EVSETestEventSaveData +{ + int64_t mOldMaxHardwareCurrentLimit; + int64_t mOldCircuitCapacity; + int64_t mOldUserMaximumChargeCurrent; + int64_t mOldCableAssemblyLimit; + StateEnum mOldHwStateBasic; /* For storing hwState before Basic Func event */ + StateEnum mOldHwStatePluggedIn; /* For storing hwState before PluggedIn event */ + StateEnum mOldHwStatePluggedInDemand; /* For storing hwState before PluggedInDemand event */ +}; + +static EVSETestEventSaveData sEVSETestEventSaveData; + +EnergyEvseDelegate * GetEvseDelegate() +{ + EVSEManufacturer * mn = GetEvseManufacturer(); + VerifyOrDieWithMsg(mn != nullptr, AppServer, "EVSEManufacturer is null"); + EnergyEvseDelegate * dg = mn->GetDelegate(); + VerifyOrDieWithMsg(dg != nullptr, AppServer, "EVSE Delegate is null"); + + return dg; +} + +void SetTestEventTrigger_BasicFunctionality() +{ + EnergyEvseDelegate * dg = GetEvseDelegate(); + + sEVSETestEventSaveData.mOldMaxHardwareCurrentLimit = dg->HwGetMaxHardwareCurrentLimit(); + sEVSETestEventSaveData.mOldCircuitCapacity = dg->GetCircuitCapacity(); + sEVSETestEventSaveData.mOldUserMaximumChargeCurrent = dg->GetUserMaximumChargeCurrent(); + sEVSETestEventSaveData.mOldHwStateBasic = dg->HwGetState(); + + dg->HwSetMaxHardwareCurrentLimit(32000); + dg->HwSetCircuitCapacity(32000); + dg->SetUserMaximumChargeCurrent(32000); + dg->HwSetState(StateEnum::kNotPluggedIn); +} +void SetTestEventTrigger_BasicFunctionalityClear() +{ + EnergyEvseDelegate * dg = GetEvseDelegate(); + + dg->HwSetMaxHardwareCurrentLimit(sEVSETestEventSaveData.mOldMaxHardwareCurrentLimit); + dg->HwSetCircuitCapacity(sEVSETestEventSaveData.mOldCircuitCapacity); + dg->SetUserMaximumChargeCurrent(sEVSETestEventSaveData.mOldUserMaximumChargeCurrent); + dg->HwSetState(sEVSETestEventSaveData.mOldHwStateBasic); +} +void SetTestEventTrigger_EVPluggedIn() +{ + EnergyEvseDelegate * dg = GetEvseDelegate(); + + sEVSETestEventSaveData.mOldCableAssemblyLimit = dg->HwGetCableAssemblyLimit(); + sEVSETestEventSaveData.mOldHwStatePluggedIn = dg->HwGetState(); + + dg->HwSetCableAssemblyLimit(63000); + dg->HwSetState(StateEnum::kPluggedInNoDemand); +} +void SetTestEventTrigger_EVPluggedInClear() +{ + EnergyEvseDelegate * dg = GetEvseDelegate(); + dg->HwSetCableAssemblyLimit(sEVSETestEventSaveData.mOldCableAssemblyLimit); + dg->HwSetState(sEVSETestEventSaveData.mOldHwStatePluggedIn); +} + +void SetTestEventTrigger_EVChargeDemand() +{ + EnergyEvseDelegate * dg = GetEvseDelegate(); + + sEVSETestEventSaveData.mOldHwStatePluggedInDemand = dg->HwGetState(); + dg->HwSetState(StateEnum::kPluggedInDemand); +} +void SetTestEventTrigger_EVChargeDemandClear() +{ + EnergyEvseDelegate * dg = GetEvseDelegate(); + + dg->HwSetState(sEVSETestEventSaveData.mOldHwStatePluggedInDemand); +} +void SetTestEventTrigger_EVSEGroundFault() +{ + EnergyEvseDelegate * dg = GetEvseDelegate(); + + dg->HwSetFault(FaultStateEnum::kGroundFault); +} + +void SetTestEventTrigger_EVSEOverTemperatureFault() +{ + EnergyEvseDelegate * dg = GetEvseDelegate(); + + dg->HwSetFault(FaultStateEnum::kOverTemperature); +} + +void SetTestEventTrigger_EVSEFaultClear() +{ + EnergyEvseDelegate * dg = GetEvseDelegate(); + + dg->HwSetFault(FaultStateEnum::kNoError); +} + +void SetTestEventTrigger_EVSEDiagnosticsComplete() +{ + EnergyEvseDelegate * dg = GetEvseDelegate(); + + 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(eventTrigger); + + switch (trigger) + { + case EnergyEvseTrigger::kBasicFunctionality: + ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => Basic Functionality install"); + SetTestEventTrigger_BasicFunctionality(); + break; + case EnergyEvseTrigger::kBasicFunctionalityClear: + ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => Basic Functionality clear"); + SetTestEventTrigger_BasicFunctionalityClear(); + break; + case EnergyEvseTrigger::kEVPluggedIn: + ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EV plugged in"); + SetTestEventTrigger_EVPluggedIn(); + break; + case EnergyEvseTrigger::kEVPluggedInClear: + ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EV unplugged"); + SetTestEventTrigger_EVPluggedInClear(); + break; + case EnergyEvseTrigger::kEVChargeDemand: + ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EV Charge Demand"); + SetTestEventTrigger_EVChargeDemand(); + break; + case EnergyEvseTrigger::kEVChargeDemandClear: + ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EV Charge NoDemand"); + SetTestEventTrigger_EVChargeDemandClear(); + break; + case EnergyEvseTrigger::kEVSEGroundFault: + ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EVSE has a GroundFault fault"); + SetTestEventTrigger_EVSEGroundFault(); + break; + case EnergyEvseTrigger::kEVSEOverTemperatureFault: + ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EVSE has a OverTemperature fault"); + SetTestEventTrigger_EVSEOverTemperatureFault(); + break; + case EnergyEvseTrigger::kEVSEFaultClear: + ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EVSE faults have cleared"); + SetTestEventTrigger_EVSEFaultClear(); + break; + case EnergyEvseTrigger::kEVSEDiagnosticsComplete: + ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EVSE Diagnostics Completed"); + SetTestEventTrigger_EVSEDiagnosticsComplete(); + break; + + default: + return false; + } + + return true; +} + +bool HandleEnergyReportingTestEventTrigger(uint64_t eventTrigger) +{ + EnergyReportingTrigger trigger = static_cast(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/linux/README.md b/examples/energy-management-app/linux/README.md new file mode 100644 index 0000000000..2a986af2a2 --- /dev/null +++ b/examples/energy-management-app/linux/README.md @@ -0,0 +1,511 @@ +# CHIP Linux Energy Management Example + +An example showing the use of CHIP on the Linux. The document will describe how +to build and run CHIP Linux Energy Management Example on Raspberry Pi. This doc +is tested on **Ubuntu for Raspberry Pi Server 20.04 LTS (aarch64)** and **Ubuntu +for Raspberry Pi Desktop 20.10 (aarch64)** + +To cross-compile this example on x64 host and run on **NXP i.MX 8M Mini** +**EVK**, see the associated +[README document](../../../docs/guides/nxp_imx8m_linux_examples.md) for details. + +
+ +- [CHIP Linux Energy Management Example](#chip-linux-energy-management-example) + - [Building](#building) + - [Commandline arguments](#commandline-arguments) + - [Running the Complete Example on Raspberry Pi 4](#running-the-complete-example-on-raspberry-pi-4) + - [Running RPC Console](#running-rpc-console) + - [Device Tracing](#device-tracing) + - [Python Test Cases](#python-test-cases) + - [Running the test cases:](#running-the-test-cases) + - [CHIP-REPL Interaction](#chip-repl-interaction) + - [Building chip-repl:](#building-chip-repl) + - [Activating python virtual env](#activating-python-virtual-env) + - [Interacting with CHIP-REPL and the example app](#interacting-with-chip-repl-and-the-example-app) + - [Using chip-repl to Fake a charging session](#using-chip-repl-to-fake-a-charging-session) + +
+ +## Building + +- Install tool chain + + $ sudo apt-get install git gcc g++ python pkg-config libssl-dev libdbus-1-dev libglib2.0-dev ninja-build python3-venv python3-dev unzip + +- Build the example application: + + $ cd ~/connectedhomeip/examples/energy-management-app/linux + $ git submodule update --init + $ source third_party/connectedhomeip/scripts/activate.sh + $ gn gen out/debug + $ ninja -C out/debug + +- To delete generated executable, libraries and object files use: + + $ cd ~/connectedhomeip/examples/energy-management-app/linux + $ rm -rf out/ + +- Build the example with pigweed RPC + + $ cd ~/connectedhomeip/examples/energy-management-app/linux + $ git submodule update --init + $ source third_party/connectedhomeip/scripts/activate.sh + $ gn gen out/debug --args='import("//with_pw_rpc.gni")' + $ ninja -C out/debug + +## Commandline arguments + +- `--wifi` + + Enables WiFi management feature. Required for WiFi commissioning. + +- `--thread` + + Enables Thread management feature, requires ot-br-posix dbus daemon running. + Required for Thread commissioning. + +- `--ble-device ` + + Use specific bluetooth interface for BLE advertisement and connections. + + `interface id`: the number after `hci` when listing BLE interfaces by + `hciconfig` command, for example, `--ble-device 1` means using `hci1` + interface. Default: `0`. + +## Running the Complete Example on Raspberry Pi 4 + +> If you want to test Echo protocol, please enable Echo handler +> +> gn gen out/debug --args='chip_app_use_echo=true' +> ninja -C out/debug + +- Prerequisites + + 1. A Raspberry Pi 4 board + 2. A USB Bluetooth Dongle, Ubuntu desktop will send Bluetooth advertisement, + which will block CHIP from connecting via BLE. On Ubuntu server, you need + to install `pi-bluetooth` via APT. + 3. Ubuntu 20.04 or newer image for ARM64 platform. + +- Building + + Follow [Building](#building) section of this document. + +- Running + + - [Optional] Plug USB Bluetooth dongle + + - Plug USB Bluetooth dongle and find its bluetooth device number. The + number after `hci` is the bluetooth device number, `1` in this + example. + + $ hciconfig + hci1: Type: Primary Bus: USB + BD Address: 00:1A:7D:AA:BB:CC ACL MTU: 310:10 SCO MTU: 64:8 + UP RUNNING PSCAN ISCAN + RX bytes:20942 acl:1023 sco:0 events:1140 errors:0 + TX bytes:16559 acl:1011 sco:0 commands:121 errors:0 + + hci0: Type: Primary Bus: UART + BD Address: B8:27:EB:AA:BB:CC ACL MTU: 1021:8 SCO MTU: 64:1 + UP RUNNING PSCAN ISCAN + RX bytes:8609495 acl:14 sco:0 events:217484 errors:0 + TX bytes:92185 acl:20 sco:0 commands:5259 errors:0 + + - Run Linux Energy Management Example App + + $ cd ~/connectedhomeip/examples/energy-management-app/linux + $ sudo out/debug/chip-energy-management-app --ble-device [bluetooth device number] + # In this example, the device we want to use is hci1 + $ sudo out/debug/chip-energy-management-app --ble-device 1 + + - Test the device using ChipDeviceController on your laptop / + workstation etc. + +## Running RPC Console + +- As part of building the example with RPCs enabled the chip_rpc python + interactive console is installed into your venv. The python wheel files are + also created in the output folder: out/debug/chip_rpc_console_wheels. To + install the wheel files without rebuilding: + `pip3 install out/debug/chip_rpc_console_wheels/*.whl` + +- To use the chip-rpc console after it has been installed run: + `chip-console -s localhost:33000 -o //pw_log.out` + +- Then you can Get and Set the Energy Management using the RPCs: + `rpcs.chip.rpc.EnergyManagement.Get()` + +## Device Tracing + +Device tracing is available to analyze the device performance. To turn on +tracing, build with RPC enabled. See [Building with RPC enabled](#building). + +Obtain tracing json file. + +``` + $ ./{PIGWEED_REPO}/pw_trace_tokenized/py/pw_trace_tokenized/get_trace.py -s localhost:33000 \ + -o {OUTPUT_FILE} -t {ELF_FILE} {PIGWEED_REPO}/pw_trace_tokenized/pw_trace_protos/trace_rpc.proto +``` + +## Python Test Cases + +When you want to test this cluster you can use chip-repl or chip-tool by hand. +CHIP-REPL is slightly easier to interact with when dealing with some of the +complex structures. + +There are several test scripts provided for `EVSE` (in +[src/python_testing](src/python_testing)): + +- `TC_EEVSE_2_2`: This validates the primary functionality +- `TC_EEVSE_2_3`: This validates Get/Set/Clear target commands +- `TC_EEVSE_2_4`: This validates Faults +- `TC_EEVSE_2_5`: This validates `EVSE` diagnostic command (optional) + +These scripts require the use of Test Event Triggers via the GeneralDiagnostics +cluster on Endpoint 0. This requires an `enableKey` (16 bytes) and a set of +reserved int64_t test event trigger codes. + +By default the test event support is not enabled, and when compiling the example +app you need to add `chip_enable_energy_evse_trigger=true` to the gn args. + +```bash + $ gn gen out/debug --args='chip_enable_energy_evse_trigger=true' + $ ninja -C out/debug +``` + +Once the application is built you also need to tell it at runtime what the +chosen enable key is using the `--enable-key` command line option. +```bash + $ ./chip-energy-management-app --enable-key 000102030405060708090a0b0c0d0e0f +``` + +### Running the test cases: + +From the top-level of the connectedhomeip repo type: + +```bash + $ python src/python_testing/TC_EEVSE_2_2.py --endpoint 1 -m on-network -n 1234 -p 20202021 -d 3840 --hex-arg enableKey:000102030405060708090a0b0c0d0e0f +``` + +- Note that the `--endpoint 1` must be used with the example, since the `EVSE` + cluster is on endpoint 1. The `--hex-arg enableKey:` value must match + the `--enable-key ` used on chip-energy-management-app args. + +## CHIP-REPL Interaction + +- See chip-repl documentation in + [Matter_REPL_Intro](../../../docs/guides/repl/Matter_REPL_Intro.ipynb) + +### Building chip-repl: + +```bash + $ ./build_python.sh -i +``` + +### Activating python virtual env + +- You need to repeat this step each time you start a new shell. + +```bash + $ source /bin/activate +``` + +### Interacting with CHIP-REPL and the example app + +- Step 1: Launch the example app + +```bash + $ ./chip-energy-management-app --enable-key 000102030405060708090a0b0c0d0e0f +``` + +- Step 2: Launch CHIP-REPL + +```bash + $ chip-repl +``` + +- Step 3: (In chip-repl) Commissioning OnNetwork + +```python + devCtrl.CommissionOnNetwork(1234,20202021) # Commission with NodeID 1234 + Established secure session with Device + Commissioning complete + Out[2]: +``` + +- Step 4: (In chip-repl) Read `EVSE` attributes + +```python + # Read from NodeID 1234, Endpoint 1, all attributes on EnergyEvse cluster + await devCtrl.ReadAttribute(1234,[(1, chip.clusters.EnergyEvse)]) +``` + +``` +{ +│ 1: { +│ │ : { +│ │ │ : 3790455237, +│ │ │ : Null, +│ │ │ : , +│ │ │ : Null, +│ │ │ : Null, +│ │ │ : 0, +│ │ │ : Null, +│ │ │ : Null, +│ │ │ : [ +... │ │ ], +│ │ │ : 6000, +│ │ │ : Null, +│ │ │ : 758415333, +│ │ │ : 0, +│ │ │ : 1, +│ │ │ : [ +... +│ │ │ ], +│ │ │ : , +│ │ │ : Null, +│ │ │ : Null, +│ │ │ : Null, +│ │ │ : Null, +│ │ │ : Null, +│ │ │ : [ +... │ │ ], +│ │ │ : Null, +│ │ │ : 0, +│ │ │ : Null, +│ │ │ : , +│ │ │ : 600, +│ │ │ : 0, +│ │ │ : 1, +│ │ │ : 80000, +│ │ │ : 2 +│ │ } +│ } +} + +``` + +- Step 5: Setting up a subscription so that attributes updates are sent + automatically + +```python + reportingTimingParams = (3, 60) # MinInterval = 3s, MaxInterval = 60s + subscription = await devCtrl.ReadAttribute(1234,[(1, chip.clusters.EnergyEvse)], reportInterval=reportingTimingParams) +``` + +- Step 6: Send an `EnableCharging` command which lasts for 60 seconds The + `EnableCharging` takes an optional `chargingEnabledUntil` parameter which + allows the charger to automatically disable itself at some preset time in + the future. Note that it uses Epoch_s (which is from Jan 1 2000) which is a + uint32_t in seconds. + +```python + from datetime import datetime, timezone, timedelta + epoch_end = int((datetime.now(tz=timezone.utc) + timedelta(seconds=60) - datetime(2000, 1, 1, 0, 0, 0, 0, timezone.utc)).total_seconds()) + + await devCtrl.SendCommand(1234, 1, chip.clusters.EnergyEvse.Commands.EnableCharging(chargingEnabledUntil=epoch_end,minimumChargeCurrent=2000,maximumChargeCurrent=25000),timedRequestTimeoutMs=3000) +``` + +The output should look like: + +``` +Attribute Changed: +{ +│ 'Endpoint': 1, +│ 'Attribute': , +│ 'Value': +} +Attribute Changed: +{ +│ 'Endpoint': 1, +│ 'Attribute': , +│ 'Value': 2000 +} +Attribute Changed: +{ +│ 'Endpoint': 1, +│ 'Attribute': , +│ 'Value': 758416066 +} +``` + +After 60 seconds the charging should automatically become disabled: + +``` +Attribute Changed: +{ +│ 'Endpoint': 1, +│ 'Attribute': , +│ 'Value': +} +Attribute Changed: +{ +│ 'Endpoint': 1, +│ 'Attribute': , +│ 'Value': 0 +} +Attribute Changed: +{ +│ 'Endpoint': 1, +│ 'Attribute': , +│ 'Value': 0 +} +Attribute Changed: +{ +│ 'Endpoint': 1, +│ 'Attribute': , +│ 'Value': 0 +} +``` + +Note that you can omit the `chargingEnabledUntil` argument and it will charge +indefinitely. + +### Using chip-repl to Fake a charging session + +If you haven't implemented a real `EVSE` but want to simulate plugging in an `EV` +then you can use a few of the test event triggers to simulate these scenarios. + +The test event triggers values can be found in: +[EnergyEvseTestEventTriggerHandler.h](../../../src/app/clusters/energy-EVSE-server/EnergyEvseTestEventTriggerHandler.h) + +- 0x0099000000000000 - Simulates the `EVSE` being installed on a 32A supply +- 0x0099000000000002 - Simulates the `EVSE` being plugged in (this should + generate an `EVConnected` event) +- 0x0099000000000004 - Simulates the `EVSE` requesting power + +To send a test event trigger to the app, use the following commands (in +chip-repl): + +```python + # send 1st event trigger to 'install' the `EVSE` on a 32A supply + await devCtrl.SendCommand(1234, 0, chip.clusters.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=bytes([b for b in range(16)]), eventTrigger=0x0099000000000000)) + + # send 2nd event trigger to plug the `EV` in + await devCtrl.SendCommand(1234, 0, chip.clusters.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=bytes([b for b in range(16)]), eventTrigger=0x0099000000000002)) + +``` + +Now send the enable charging command (omit the `chargingEnabledUntil` arg this +time): + +```python + await devCtrl.SendCommand(1234, 1, chip.clusters.EnergyEvse.Commands.EnableCharging(minimumChargeCurrent=2000,maximumChargeCurrent=25000),timedRequestTimeoutMs=3000) +``` + +Now send the test event trigger to simulate the `EV` asking for demand: + +```python + # send 2nd event trigger to plug the `EV` in + await devCtrl.SendCommand(1234, 0, chip.clusters.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=bytes([b for b in range(16)]), eventTrigger=0x0099000000000004)) + + # Read the events + await devCtrl.ReadEvent(1234,[(1, chip.clusters.EnergyEvse,1)]) +``` + +``` +[ +│ EventReadResult( +│ │ Header=EventHeader( +│ │ │ EndpointId=1, +│ │ │ ClusterId=153, +│ │ │ EventId=0, +│ │ │ EventNumber=65538, +│ │ │ Priority=, +│ │ │ Timestamp=1705102500069, +│ │ │ TimestampType= +│ │ ), +│ │ Status=, +│ │ Data=EVConnected( +│ │ │ sessionID=0 +│ │ ) +│ ), +│ EventReadResult( +│ │ Header=EventHeader( +│ │ │ EndpointId=1, +│ │ │ ClusterId=153, +│ │ │ EventId=2, +│ │ │ EventNumber=65539, +│ │ │ Priority=, +│ │ │ Timestamp=1705102801764, +│ │ │ TimestampType= +│ │ ), +│ │ Status=, +│ │ Data=EnergyTransferStarted( +│ │ │ sessionID=0, +│ │ │ state=, +│ │ │ maximumCurrent=25000 +│ │ ) +│ ) +] +``` + +- We can see that the `EventNumber 65538` was sent when the vehicle was + plugged in, and a new `sessionID=0` was created. +- We can also see that the `EnergyTransferStarted` was sent in + `EventNumber 65539` + +What happens when we unplug the vehicle? + +```python + await devCtrl.SendCommand(1234, 0, chip.clusters.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=bytes([b for b in range(16)]), eventTrigger=0x0099000000000001)) +``` + +When we re-read the events: + +``` +[ +│ EventReadResult( +│ │ Header=EventHeader( +│ │ │ EndpointId=1, +│ │ │ ClusterId=153, +│ │ │ EventId=3, +│ │ │ EventNumber=65540, +│ │ │ Priority=, +│ │ │ Timestamp=1705102996749, +│ │ │ TimestampType= +│ │ ), +│ │ Status=, +│ │ Data=EnergyTransferStopped( +│ │ │ sessionID=0, +│ │ │ state=, +│ │ │ reason=, +│ │ │ energyTransferred=0 +│ │ ) +│ ), +│ EventReadResult( +│ │ Header=EventHeader( +│ │ │ EndpointId=1, +│ │ │ ClusterId=153, +│ │ │ EventId=1, +│ │ │ EventNumber=65541, +│ │ │ Priority=, +│ │ │ Timestamp=1705102996749, +│ │ │ TimestampType= +│ │ ), +│ │ Status=, +│ │ Data=EVNotDetected( +│ │ │ sessionID=0, +│ │ │ state=, +│ │ │ sessionDuration=0, +│ │ │ sessionEnergyCharged=0, +│ │ │ sessionEnergyDischarged=0 +│ │ ) +│ ) +] + +``` + +- In `EventNumber 65540` we had an `EnergyTransferStopped` event with reason + `kOther`. + + This was a rather abrupt end to a charging session (normally we would see + the `EVSE` or `EV` decide to stop charging), but this demonstrates the cable + being pulled out without a graceful charging shutdown. + +- In `EventNumber 65541` we had an `EvNotDetected` event showing that the + state was `kPluggedInCharging` prior to the `EV` being not detected (normally + in a graceful shutdown this would be `kPluggedInNoDemand` or + `kPluggedInDemand`). diff --git a/examples/light-switch-app/infineon/cyw30739/src/main.cpp b/examples/light-switch-app/infineon/cyw30739/src/main.cpp index dbfdb81b87..483a475b51 100644 --- a/examples/light-switch-app/infineon/cyw30739/src/main.cpp +++ b/examples/light-switch-app/infineon/cyw30739/src/main.cpp @@ -26,8 +26,9 @@ #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR #include #endif +#include #include -#include +#include #include #include #include @@ -192,10 +193,13 @@ void InitApp(intptr_t args) // Print QR Code URL PrintOnboardingCodes(chip::RendezvousInformationFlag(chip::RendezvousInformationFlag::kBLE)); /* Start CHIP datamodel server */ - static chip::OTATestEventTriggerDelegate testEventTriggerDelegate{ chip::ByteSpan(sTestEventTriggerEnableKey) }; + static chip::SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; + static chip::OTATestEventTriggerHandler sOtaTestEventTriggerHandler{}; + VerifyOrDie(sTestEventTriggerDelegate.Init(chip::ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR); + VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sOtaTestEventTriggerHandler) == CHIP_NO_ERROR); static chip::CommonCaseDeviceServerInitParams initParams; (void) initParams.InitializeStaticResourcesBeforeServerInit(); - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; gExampleDeviceInfoProvider.SetStorageDelegate(initParams.persistentStorageDelegate); chip::DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider); chip::Inet::EndPointStateOpenThread::OpenThreadEndpointInitParam nativeParams; diff --git a/examples/light-switch-app/nrfconnect/main/AppTask.cpp b/examples/light-switch-app/nrfconnect/main/AppTask.cpp index a4b5ac5b47..06ac797f6a 100644 --- a/examples/light-switch-app/nrfconnect/main/AppTask.cpp +++ b/examples/light-switch-app/nrfconnect/main/AppTask.cpp @@ -24,8 +24,9 @@ #include "LightSwitch.h" #include +#include #include -#include +#include #include #include #include @@ -215,9 +216,12 @@ CHIP_ERROR AppTask::Init() #endif static CommonCaseDeviceServerInitParams initParams; - static OTATestEventTriggerDelegate testEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + static SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; + static OTATestEventTriggerHandler sOtaTestEventTriggerHandler{}; + VerifyOrDie(sTestEventTriggerDelegate.Init(ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR); + VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sOtaTestEventTriggerHandler) == CHIP_NO_ERROR); (void) initParams.InitializeStaticResourcesBeforeServerInit(); - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; ReturnErrorOnFailure(chip::Server::GetInstance().Init(initParams)); AppFabricTableDelegate::Init(); diff --git a/examples/light-switch-app/qpg/BUILD.gn b/examples/light-switch-app/qpg/BUILD.gn new file mode 100644 index 0000000000..055cfd46f9 --- /dev/null +++ b/examples/light-switch-app/qpg/BUILD.gn @@ -0,0 +1,155 @@ +# Copyright(c) 2021 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. + +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") +import("//build_overrides/openthread.gni") +import("//build_overrides/qpg_sdk.gni") + +import("${build_root}/config/defaults.gni") +import("${chip_root}/src/platform/device.gni") +import("${qpg_sdk_build_root}/qpg_executable.gni") +import("${qpg_sdk_build_root}/qpg_sdk.gni") + +# declares chip_build_libshell = false +import("${chip_root}/src/lib/lib.gni") + +# declares chip_enable_pw_rpc = false +import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni") +if (chip_enable_pw_rpc) { + import("//build_overrides/pigweed.gni") + import("$dir_pw_build/target_types.gni") +} + +assert(current_os == "freertos") + +qpg_project_dir = "${chip_root}/examples/light-switch-app/qpg" +examples_plat_dir = "${chip_root}/examples/platform/qpg" + +qpg_sdk("sdk") { + include_dirs = [ + "${chip_root}/src/platform/qpg", + "${examples_plat_dir}", + "${qpg_project_dir}/include", + ] + + defines = [ "PW_RPC_ENABLED=${chip_enable_pw_rpc}" ] +} + +qpg_executable("light_switch_app") { + output_name = "chip-${qpg_target_ic}-light-switch-example.out" + + sources = [ + "${chip_root}/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.cpp", + "${examples_plat_dir}/app/main.cpp", + "${examples_plat_dir}/ota/ota.cpp", + "src/AppTask.cpp", + "src/SwitchManager.cpp", + "src/ZclCallbacks.cpp", + "src/binding-handler.cpp", + ] + + deps = [ + ":sdk", + "${chip_root}/examples/light-switch-app/qpg/zap/", + "${chip_root}/examples/providers:device_info_provider", + "${chip_root}/src/lib", + "${chip_root}/src/setup_payload", + "${chip_root}/third_party/openthread/platforms:libopenthread-platform", + "${chip_root}/third_party/openthread/platforms:libopenthread-platform-utils", + "${chip_root}/third_party/qpg_sdk:qpg_switch_factorydata_lib", + "${chip_root}/third_party/qpg_sdk:qpg_switch_firmwaredata_lib", + ] + + if (chip_openthread_ftd) { + deps += [ "${chip_root}/third_party/openthread/repo:libopenthread-ftd" ] + } else { + deps += [ "${chip_root}/third_party/openthread/repo:libopenthread-mtd" ] + } + + include_dirs = [ + "include", + "${examples_plat_dir}/ota", + ] + + defines = [] + + if (chip_enable_pw_rpc) { + defines += [ + "PW_RPC_ATTRIBUTE_SERVICE=1", + "PW_RPC_BUTTON_SERVICE=1", + "PW_RPC_DEVICE_SERVICE=1", + "PW_RPC_LOCKING_SERVICE=1", + ] + + sources += [ + "${chip_root}/examples/common/pigweed/RpcService.cpp", + "${chip_root}/examples/common/pigweed/qpg/PigweedLoggerMutex.cpp", + "${examples_plat_dir}/PigweedLogger.cpp", + "${examples_plat_dir}/Rpc.cpp", + "${examples_plat_dir}/uart.c", + ] + + deps += [ + "$dir_pw_hdlc:default_addresses", + "$dir_pw_hdlc:rpc_channel_output", + "$dir_pw_stream:sys_io_stream", + "${chip_root}/config/qpg/lib/pw_rpc:pw_rpc", + "${chip_root}/examples/common/pigweed:attributes_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:button_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:device_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:locking_service.nanopb_rpc", + "${examples_plat_dir}/pw_sys_io:pw_sys_io_qpg", + ] + + deps += pw_build_LINK_DEPS + + include_dirs += [ + "${chip_root}/examples/common", + "${chip_root}/examples/common/pigweed/qpg", + ] + } else { + # The below gives compiler erros in pigweed, therefore it is only enabled + # when rpc is not + cflags = [ "-Wconversion" ] + } + + if (chip_build_libshell) { + deps += [ "${examples_plat_dir}:qpg-matter-shell" ] + } else { + if (chip_openthread_ftd) { + deps += + [ "${chip_root}/third_party/openthread/repo:libopenthread-cli-ftd" ] + } else { + deps += + [ "${chip_root}/third_party/openthread/repo:libopenthread-cli-mtd" ] + } + } + + ldscript = "${qpg_sdk_root}/Libraries/Qorvo/QorvoStack/gen/QorvoStack_${qpg_target_ic}/QorvoStack_${qpg_target_ic}.ld" + + inputs = [ ldscript ] + + ldflags = [ "-T" + rebase_path(ldscript, root_build_dir) ] + + output_dir = root_out_dir +} + +group("qpg") { + deps = [ ":light_switch_app" ] +} + +group("default") { + deps = [ ":qpg" ] +} diff --git a/examples/light-switch-app/qpg/src/AppTask.cpp b/examples/light-switch-app/qpg/src/AppTask.cpp new file mode 100644 index 0000000000..f02823b178 --- /dev/null +++ b/examples/light-switch-app/qpg/src/AppTask.cpp @@ -0,0 +1,575 @@ +/* + * + * Copyright (c) 2021 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. + */ + +// TODO - Intentionally grouped together +#include "binding-handler.h" +#include +using namespace ::chip; + +#include "gpSched.h" +#include "qvIO.h" + +#include "AppConfig.h" +#include "AppEvent.h" +#include "AppTask.h" +#include "ota.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +using namespace ::chip; +using namespace ::chip::app; +using namespace ::chip::TLV; +using namespace ::chip::Credentials; +using namespace ::chip::DeviceLayer; + +#include + +#define FACTORY_RESET_TRIGGER_TIMEOUT 3000 +#define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000 +#define APP_TASK_STACK_SIZE (2 * 1024) +#define APP_TASK_PRIORITY 2 +#define APP_EVENT_QUEUE_SIZE 10 + +namespace { +TaskHandle_t sAppTaskHandle; +QueueHandle_t sAppEventQueue; + +bool sIsThreadProvisioned = false; +bool sIsThreadEnabled = false; +bool sHaveBLEConnections = false; +bool sIsBLEAdvertisingEnabled = false; + +// NOTE! This key is for test/certification only and should not be available in production devices! +uint8_t sTestEventTriggerEnableKey[TestEventTriggerDelegate::kEnableKeyLength] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; + +uint8_t sAppEventQueueBuffer[APP_EVENT_QUEUE_SIZE * sizeof(AppEvent)]; + +StaticQueue_t sAppEventQueueStruct; + +StackType_t appStack[APP_TASK_STACK_SIZE / sizeof(StackType_t)]; +StaticTask_t appTaskStruct; + +chip::DeviceLayer::DeviceInfoProviderImpl gExampleDeviceInfoProvider; +} // namespace + +AppTask AppTask::sAppTask; + +namespace { +constexpr int extDiscTimeoutSecs = 20; +} + +Clusters::Identify::EffectIdentifierEnum sIdentifyEffect = Clusters::Identify::EffectIdentifierEnum::kStopEffect; + +/********************************************************** + * Identify Callbacks + *********************************************************/ + +namespace { +void OnTriggerIdentifyEffectCompleted(chip::System::Layer * systemLayer, void * appState) +{ + sIdentifyEffect = Clusters::Identify::EffectIdentifierEnum::kStopEffect; +} +} // namespace + +void OnTriggerIdentifyEffect(Identify * identify) +{ + sIdentifyEffect = identify->mCurrentEffectIdentifier; + + if (identify->mEffectVariant != Clusters::Identify::EffectVariantEnum::kDefault) + { + ChipLogDetail(AppServer, "Identify Effect Variant unsupported. Using default"); + } + + switch (sIdentifyEffect) + { + case Clusters::Identify::EffectIdentifierEnum::kBlink: + case Clusters::Identify::EffectIdentifierEnum::kBreathe: + case Clusters::Identify::EffectIdentifierEnum::kOkay: + case Clusters::Identify::EffectIdentifierEnum::kChannelChange: + SystemLayer().ScheduleLambda([identify] { + (void) chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(5), OnTriggerIdentifyEffectCompleted, + identify); + }); + break; + case Clusters::Identify::EffectIdentifierEnum::kFinishEffect: + SystemLayer().ScheduleLambda([identify] { + (void) chip::DeviceLayer::SystemLayer().CancelTimer(OnTriggerIdentifyEffectCompleted, identify); + (void) chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(1), OnTriggerIdentifyEffectCompleted, + identify); + }); + break; + case Clusters::Identify::EffectIdentifierEnum::kStopEffect: + SystemLayer().ScheduleLambda( + [identify] { (void) chip::DeviceLayer::SystemLayer().CancelTimer(OnTriggerIdentifyEffectCompleted, identify); }); + sIdentifyEffect = Clusters::Identify::EffectIdentifierEnum::kStopEffect; + break; + default: + ChipLogProgress(Zcl, "No identifier effect"); + } +} + +Identify gIdentify = { + chip::EndpointId{ 1 }, + [](Identify *) { ChipLogProgress(Zcl, "onIdentifyStart"); }, + [](Identify *) { ChipLogProgress(Zcl, "onIdentifyStop"); }, + Clusters::Identify::IdentifyTypeEnum::kVisibleIndicator, + OnTriggerIdentifyEffect, +}; + +void LockOpenThreadTask(void) +{ + chip::DeviceLayer::ThreadStackMgr().LockThreadStack(); +} + +void UnlockOpenThreadTask(void) +{ + chip::DeviceLayer::ThreadStackMgr().UnlockThreadStack(); +} + +CHIP_ERROR AppTask::StartAppTask() +{ + sAppEventQueue = xQueueCreateStatic(APP_EVENT_QUEUE_SIZE, sizeof(AppEvent), sAppEventQueueBuffer, &sAppEventQueueStruct); + if (sAppEventQueue == nullptr) + { + ChipLogError(NotSpecified, "Failed to allocate app event queue"); + return CHIP_ERROR_NO_MEMORY; + } + + // Start App task. + sAppTaskHandle = xTaskCreateStatic(AppTaskMain, APP_TASK_NAME, ArraySize(appStack), nullptr, 1, appStack, &appTaskStruct); + if (sAppTaskHandle == nullptr) + { + return CHIP_ERROR_NO_MEMORY; + } + + return CHIP_NO_ERROR; +} + +void AppTask::InitServer(intptr_t arg) +{ + static chip::CommonCaseDeviceServerInitParams initParams; + (void) initParams.InitializeStaticResourcesBeforeServerInit(); + + gExampleDeviceInfoProvider.SetStorageDelegate(initParams.persistentStorageDelegate); + chip::DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider); + + chip::Inet::EndPointStateOpenThread::OpenThreadEndpointInitParam nativeParams; + nativeParams.lockCb = LockOpenThreadTask; + nativeParams.unlockCb = UnlockOpenThreadTask; + nativeParams.openThreadInstancePtr = chip::DeviceLayer::ThreadStackMgrImpl().OTInstance(); + initParams.endpointNativeParams = static_cast(&nativeParams); + + // Use GenericFaultTestEventTriggerHandler to inject faults + static SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; + static GenericFaultTestEventTriggerHandler sFaultTestEventTriggerHandler{}; + VerifyOrDie(sTestEventTriggerDelegate.Init(ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR); + VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sFaultTestEventTriggerHandler) == CHIP_NO_ERROR); + (void) initParams.InitializeStaticResourcesBeforeServerInit(); + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; + + chip::Server::GetInstance().Init(initParams); + +#if CHIP_DEVICE_CONFIG_ENABLE_EXTENDED_DISCOVERY + chip::app::DnssdServer::Instance().SetExtendedDiscoveryTimeoutSecs(extDiscTimeoutSecs); +#endif + + // Open commissioning after boot if no fabric was available + if (chip::Server::GetInstance().GetFabricTable().FabricCount() == 0) + { + PlatformMgr().ScheduleWork(OpenCommissioning, 0); + } +} + +void AppTask::OpenCommissioning(intptr_t arg) +{ + // Enable BLE advertisements + chip::Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow(); + ChipLogProgress(NotSpecified, "BLE advertising started. Waiting for Pairing."); +} + +CHIP_ERROR AppTask::Init() +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + PlatformMgr().AddEventHandler(MatterEventHandler, 0); + + ChipLogProgress(NotSpecified, "Current Software Version: %s", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING); + + // Init ZCL Data Model and start server + PlatformMgr().ScheduleWork(InitServer, 0); + + ReturnErrorOnFailure(mFactoryDataProvider.Init()); + SetDeviceInstanceInfoProvider(&mFactoryDataProvider); + SetCommissionableDataProvider(&mFactoryDataProvider); + + SetDeviceAttestationCredentialsProvider(&mFactoryDataProvider); + + SwitchMgr().Init(); + UpdateClusterState(); + + // Setup button handler + qvIO_SetBtnCallback(ButtonEventHandler); + + DeviceLayer::PlatformMgr().ScheduleWork(InitBindingManager); + + // Log device configuration + ConfigurationMgr().LogDeviceConfig(); + PrintOnboardingCodes(chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE)); + + sIsThreadProvisioned = ConnectivityMgr().IsThreadProvisioned(); + sIsThreadEnabled = ConnectivityMgr().IsThreadEnabled(); + sHaveBLEConnections = (ConnectivityMgr().NumBLEConnections() != 0); + sIsBLEAdvertisingEnabled = ConnectivityMgr().IsBLEAdvertisingEnabled(); + UpdateLEDs(); + + return err; +} + +void AppTask::AppTaskMain(void * pvParameter) +{ + AppEvent event; + + while (true) + { + BaseType_t eventReceived = xQueueReceive(sAppEventQueue, &event, portMAX_DELAY); + while (eventReceived == pdTRUE) + { + sAppTask.DispatchEvent(&event); + eventReceived = xQueueReceive(sAppEventQueue, &event, 0); + } + } +} + +void AppTask::ButtonEventHandler(uint8_t btnIdx, bool btnPressed) +{ + ChipLogProgress(NotSpecified, "ButtonEventHandler %d, %d", btnIdx, btnPressed); + + AppEvent button_event = {}; + button_event.Type = AppEvent::kEventType_Button; + button_event.ButtonEvent.ButtonIdx = btnIdx; + button_event.ButtonEvent.Action = btnPressed; + + switch (btnIdx) + { + case APP_FUNCTION1_BUTTON: { + if (!btnPressed) + { + return; + } + + ChipLogProgress(NotSpecified, "Level Button pressed"); + button_event.Handler = SwitchMgr().LevelHandler; + break; + } + case APP_FUNCTION2_SWITCH: { + if (!btnPressed) + { + ChipLogProgress(NotSpecified, "Switch initial press"); + SwitchMgr().GenericSwitchInitialPress(); + } + else + { + ChipLogProgress(NotSpecified, "Switch release press"); + SwitchMgr().GenericSwitchReleasePress(); + } + break; + } + case APP_FUNCTION3_BUTTON: { + if (!btnPressed) + { + return; + } + ChipLogProgress(NotSpecified, "Color button pressed"); + button_event.Handler = SwitchMgr().ColorHandler; + break; + } + case APP_FUNCTION4_BUTTON: { + if (!btnPressed) + { + return; + } + + ChipLogProgress(NotSpecified, "Toggle Button pressed"); + button_event.Handler = SwitchMgr().ToggleHandler; + break; + } + case APP_FUNCTION5_BUTTON: { + button_event.Handler = FunctionHandler; + break; + } + default: { + // invalid button + return; + } + } + + sAppTask.PostEvent(&button_event); +} + +void AppTask::TimerEventHandler(chip::System::Layer * aLayer, void * aAppState) +{ + AppEvent event; + event.Type = AppEvent::kEventType_Timer; + event.TimerEvent.Context = aAppState; + event.Handler = FunctionTimerEventHandler; + sAppTask.PostEvent(&event); +} + +void AppTask::FunctionTimerEventHandler(AppEvent * aEvent) +{ + if (aEvent->Type != AppEvent::kEventType_Timer) + { + return; + } + + // If we reached here, the button was held past FACTORY_RESET_TRIGGER_TIMEOUT, + // initiate factory reset + if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_SoftwareUpdate) + { + ChipLogProgress(NotSpecified, "[BTN] Factory Reset selected. Release within %us to cancel.", + FACTORY_RESET_CANCEL_WINDOW_TIMEOUT / 1000); + + // Start timer for FACTORY_RESET_CANCEL_WINDOW_TIMEOUT to allow user to + // cancel, if required. + sAppTask.StartTimer(FACTORY_RESET_CANCEL_WINDOW_TIMEOUT); + + sAppTask.mFunction = kFunction_FactoryReset; + + // Turn off all LEDs before starting blink to make sure blink is + // co-ordinated. + qvIO_LedSet(SYSTEM_STATE_LED, false); + + qvIO_LedBlink(SYSTEM_STATE_LED, 500, 500); + } + else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset) + { + // Actually trigger Factory Reset + sAppTask.mFunction = kFunction_NoneSelected; + chip::Server::GetInstance().ScheduleFactoryReset(); + } +} + +void AppTask::FunctionHandler(AppEvent * aEvent) +{ + if (aEvent->ButtonEvent.ButtonIdx != APP_FUNCTION5_BUTTON) + { + return; + } + + // To trigger software update: press the APP_FUNCTION_BUTTON button briefly (< + // FACTORY_RESET_TRIGGER_TIMEOUT) To initiate factory reset: press the + // APP_FUNCTION_BUTTON for FACTORY_RESET_TRIGGER_TIMEOUT + + // FACTORY_RESET_CANCEL_WINDOW_TIMEOUT All LEDs start blinking after + // FACTORY_RESET_TRIGGER_TIMEOUT to signal factory reset has been initiated. + // To cancel factory reset: release the APP_FUNCTION_BUTTON once all LEDs + // start blinking within the FACTORY_RESET_CANCEL_WINDOW_TIMEOUT + if (aEvent->ButtonEvent.Action == true) + { + if (!sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_NoneSelected) + { + ChipLogProgress(NotSpecified, "[BTN] Hold to select function:"); + ChipLogProgress(NotSpecified, "[BTN] - Trigger OTA (0-3s)"); + ChipLogProgress(NotSpecified, "[BTN] - Factory Reset (>6s)"); + + sAppTask.StartTimer(FACTORY_RESET_TRIGGER_TIMEOUT); + + sAppTask.mFunction = kFunction_SoftwareUpdate; + } + } + else + { + // If the button was released before factory reset got initiated, trigger a + // software update. + if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_SoftwareUpdate) + { + sAppTask.CancelTimer(); + + sAppTask.mFunction = kFunction_NoneSelected; + + ChipLogProgress(NotSpecified, "[BTN] Triggering OTA Query"); + + TriggerOTAQuery(); + } + else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset) + { + sAppTask.CancelTimer(); + + // Change the function to none selected since factory reset has been + // canceled. + sAppTask.mFunction = kFunction_NoneSelected; + + ChipLogProgress(NotSpecified, "[BTN] Factory Reset has been Canceled"); + } + } +} + +void AppTask::CancelTimer() +{ + chip::DeviceLayer::SystemLayer().CancelTimer(TimerEventHandler, this); + mFunctionTimerActive = false; +} + +void AppTask::StartTimer(uint32_t aTimeoutInMs) +{ + CHIP_ERROR err; + + chip::DeviceLayer::SystemLayer().CancelTimer(TimerEventHandler, this); + err = chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(aTimeoutInMs), TimerEventHandler, this); + SuccessOrExit(err); + + mFunctionTimerActive = true; +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "StartTimer failed %s: ", chip::ErrorStr(err)); + } +} + +void AppTask::PostEvent(const AppEvent * aEvent) +{ + if (sAppEventQueue != nullptr) + { + if (!xQueueSend(sAppEventQueue, aEvent, 1)) + { + ChipLogError(NotSpecified, "Failed to post event to app task event queue"); + } + } + else + { + ChipLogError(NotSpecified, "Event Queue is nullptr should never happen"); + } +} + +void AppTask::DispatchEvent(AppEvent * aEvent) +{ + if (aEvent->Handler) + { + aEvent->Handler(aEvent); + } + else + { + ChipLogError(NotSpecified, "Event received with no handler. Dropping event."); + } +} + +/** + * Update cluster status after application level changes + */ +void AppTask::UpdateClusterState(void) +{ + ChipLogProgress(NotSpecified, "UpdateClusterState"); + + /* write the new attribute value based on attribute setter API. + example API usage of on-off attribute: + + EmberAfStatus status = Clusters::OnOff::Attributes::OnOff::Set(QPG_LIGHT_ENDPOINT_ID, LightingMgr().IsTurnedOn()); + + if (status != EMBER_ZCL_STATUS_SUCCESS) + { + ChipLogError(NotSpecified, "ERR: updating on/off %x", status); + } + */ +} + +void AppTask::UpdateLEDs(void) +{ + // If system has "full connectivity", keep the LED On constantly. + // + // If thread and service provisioned, but not attached to the thread network + // yet OR no connectivity to the service OR subscriptions are not fully + // established THEN blink the LED Off for a short period of time. + // + // If the system has ble connection(s) uptill the stage above, THEN blink + // the LEDs at an even rate of 100ms. + // + // Otherwise, blink the LED ON for a very short time. + if (sIsThreadProvisioned && sIsThreadEnabled) + { + qvIO_LedSet(SYSTEM_STATE_LED, true); + } + else if (sHaveBLEConnections) + { + qvIO_LedBlink(SYSTEM_STATE_LED, 100, 100); + } + else if (sIsBLEAdvertisingEnabled) + { + qvIO_LedBlink(SYSTEM_STATE_LED, 50, 50); + } + else + { + // not commissioned yet + qvIO_LedBlink(SYSTEM_STATE_LED, 50, 950); + } +} + +void AppTask::MatterEventHandler(const ChipDeviceEvent * event, intptr_t) +{ + switch (event->Type) + { + case DeviceEventType::kServiceProvisioningChange: { + sIsThreadProvisioned = event->ServiceProvisioningChange.IsServiceProvisioned; + UpdateLEDs(); + break; + } + + case DeviceEventType::kThreadConnectivityChange: { + sIsThreadEnabled = (event->ThreadConnectivityChange.Result == kConnectivity_Established); + UpdateLEDs(); + break; + } + + case DeviceEventType::kCHIPoBLEConnectionEstablished: { + sHaveBLEConnections = true; + UpdateLEDs(); + break; + } + + case DeviceEventType::kCHIPoBLEConnectionClosed: { + sHaveBLEConnections = false; + UpdateLEDs(); + break; + } + + case DeviceEventType::kCHIPoBLEAdvertisingChange: { + sIsBLEAdvertisingEnabled = (event->CHIPoBLEAdvertisingChange.Result == kActivity_Started); + UpdateLEDs(); + break; + } + + default: + break; + } +} diff --git a/examples/lighting-app/infineon/cyw30739/src/main.cpp b/examples/lighting-app/infineon/cyw30739/src/main.cpp index 14730252de..f851e2e881 100644 --- a/examples/lighting-app/infineon/cyw30739/src/main.cpp +++ b/examples/lighting-app/infineon/cyw30739/src/main.cpp @@ -26,8 +26,9 @@ #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR #include #endif +#include #include -#include +#include #include #include #include @@ -191,10 +192,13 @@ void InitApp(intptr_t args) // Print QR Code URL PrintOnboardingCodes(chip::RendezvousInformationFlag(chip::RendezvousInformationFlag::kBLE)); /* Start CHIP datamodel server */ - static chip::OTATestEventTriggerDelegate testEventTriggerDelegate{ chip::ByteSpan(sTestEventTriggerEnableKey) }; + static chip::SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; + static chip::OTATestEventTriggerHandler sOtaTestEventTriggerHandler{}; + VerifyOrDie(sTestEventTriggerDelegate.Init(chip::ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR); + VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sOtaTestEventTriggerHandler) == CHIP_NO_ERROR); static chip::CommonCaseDeviceServerInitParams initParams; (void) initParams.InitializeStaticResourcesBeforeServerInit(); - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; gExampleDeviceInfoProvider.SetStorageDelegate(initParams.persistentStorageDelegate); chip::DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider); chip::Inet::EndPointStateOpenThread::OpenThreadEndpointInitParam nativeParams; diff --git a/examples/lighting-app/nrfconnect/main/AppTask.cpp b/examples/lighting-app/nrfconnect/main/AppTask.cpp index 03632106fc..494095e04d 100644 --- a/examples/lighting-app/nrfconnect/main/AppTask.cpp +++ b/examples/lighting-app/nrfconnect/main/AppTask.cpp @@ -27,8 +27,9 @@ #include #include #include +#include #include -#include +#include #include #include #include @@ -243,9 +244,12 @@ CHIP_ERROR AppTask::Init() #endif static CommonCaseDeviceServerInitParams initParams; - static OTATestEventTriggerDelegate testEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + static SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; + static OTATestEventTriggerHandler sOtaTestEventTriggerHandler{}; + VerifyOrDie(sTestEventTriggerDelegate.Init(ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR); + VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sOtaTestEventTriggerHandler) == CHIP_NO_ERROR); (void) initParams.InitializeStaticResourcesBeforeServerInit(); - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; ReturnErrorOnFailure(chip::Server::GetInstance().Init(initParams)); AppFabricTableDelegate::Init(); diff --git a/examples/lighting-app/nxp/k32w/k32w0/main/AppTask.cpp b/examples/lighting-app/nxp/k32w/k32w0/main/AppTask.cpp index 02656ae622..58efc99fb5 100644 --- a/examples/lighting-app/nxp/k32w/k32w0/main/AppTask.cpp +++ b/examples/lighting-app/nxp/k32w/k32w0/main/AppTask.cpp @@ -272,8 +272,8 @@ void AppTask::InitServer(intptr_t arg) chip::DeviceLayer::SetDeviceInfoProvider(&infoProvider); // Init ZCL Data Model and start server - static DefaultTestEventTriggerDelegate testEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + static DefaultTestEventTriggerDelegate sTestEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; chip::Inet::EndPointStateOpenThread::OpenThreadEndpointInitParam nativeParams; nativeParams.lockCb = LockOpenThreadTask; nativeParams.unlockCb = UnlockOpenThreadTask; diff --git a/examples/lighting-app/nxp/k32w/k32w1/main/AppTask.cpp b/examples/lighting-app/nxp/k32w/k32w1/main/AppTask.cpp index 66eee725c5..7150003f59 100644 --- a/examples/lighting-app/nxp/k32w/k32w1/main/AppTask.cpp +++ b/examples/lighting-app/nxp/k32w/k32w1/main/AppTask.cpp @@ -214,8 +214,8 @@ void AppTask::InitServer(intptr_t arg) #endif // Init ZCL Data Model and start server - static DefaultTestEventTriggerDelegate testEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + static DefaultTestEventTriggerDelegate sTestEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; chip::Inet::EndPointStateOpenThread::OpenThreadEndpointInitParam nativeParams; nativeParams.lockCb = LockOpenThreadTask; nativeParams.unlockCb = UnlockOpenThreadTask; diff --git a/examples/lighting-app/qpg/BUILD.gn b/examples/lighting-app/qpg/BUILD.gn index 981e740515..3d2db96e4b 100644 --- a/examples/lighting-app/qpg/BUILD.gn +++ b/examples/lighting-app/qpg/BUILD.gn @@ -51,7 +51,7 @@ qpg_executable("lighting_app") { output_name = "chip-${qpg_target_ic}-lighting-example.out" sources = [ - "${chip_root}/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerDelegate.cpp", + "${chip_root}/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.cpp", "${examples_plat_dir}/app/main.cpp", "${examples_plat_dir}/ota/ota.cpp", "${examples_plat_dir}/powercycle_counting.c", diff --git a/examples/lighting-app/qpg/src/AppTask.cpp b/examples/lighting-app/qpg/src/AppTask.cpp index c4fe7ff0c0..96095e879e 100644 --- a/examples/lighting-app/qpg/src/AppTask.cpp +++ b/examples/lighting-app/qpg/src/AppTask.cpp @@ -33,7 +33,8 @@ #include #include -#include +#include +#include #include #include #include @@ -243,10 +244,13 @@ void AppTask::InitServer(intptr_t arg) nativeParams.openThreadInstancePtr = chip::DeviceLayer::ThreadStackMgrImpl().OTInstance(); initParams.endpointNativeParams = static_cast(&nativeParams); - // Use GenericFaultTestEventTriggerDelegate to inject faults - static GenericFaultTestEventTriggerDelegate testEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + // Use GenericFaultTestEventTriggerHandler to inject faults + static SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; + static GenericFaultTestEventTriggerHandler sFaultTestEventTriggerHandler{}; + VerifyOrDie(sTestEventTriggerDelegate.Init(ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR); + VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sFaultTestEventTriggerHandler) == CHIP_NO_ERROR); (void) initParams.InitializeStaticResourcesBeforeServerInit(); - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; chip::Server::GetInstance().Init(initParams); diff --git a/examples/lock-app/infineon/cyw30739/src/main.cpp b/examples/lock-app/infineon/cyw30739/src/main.cpp index 76fa8484ce..a0a92487a8 100644 --- a/examples/lock-app/infineon/cyw30739/src/main.cpp +++ b/examples/lock-app/infineon/cyw30739/src/main.cpp @@ -28,9 +28,10 @@ #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR #include #endif +#include #include #include -#include +#include #include #include #include @@ -191,10 +192,13 @@ void InitApp(intptr_t args) // Print QR Code URL PrintOnboardingCodes(chip::RendezvousInformationFlag(chip::RendezvousInformationFlag::kBLE)); /* Start CHIP datamodel server */ - static chip::OTATestEventTriggerDelegate testEventTriggerDelegate{ chip::ByteSpan(sTestEventTriggerEnableKey) }; + static chip::SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; + static chip::OTATestEventTriggerHandler sOtaTestEventTriggerHandler{}; + VerifyOrDie(sTestEventTriggerDelegate.Init(chip::ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR); + VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sOtaTestEventTriggerHandler) == CHIP_NO_ERROR); static chip::CommonCaseDeviceServerInitParams initParams; (void) initParams.InitializeStaticResourcesBeforeServerInit(); - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; gExampleDeviceInfoProvider.SetStorageDelegate(initParams.persistentStorageDelegate); chip::DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider); chip::Inet::EndPointStateOpenThread::OpenThreadEndpointInitParam nativeParams; diff --git a/examples/lock-app/nrfconnect/main/AppTask.cpp b/examples/lock-app/nrfconnect/main/AppTask.cpp index d64a4950ac..07a96b6d6c 100644 --- a/examples/lock-app/nrfconnect/main/AppTask.cpp +++ b/examples/lock-app/nrfconnect/main/AppTask.cpp @@ -25,9 +25,10 @@ #include #include +#include #include #include -#include +#include #include #include #include @@ -209,9 +210,12 @@ CHIP_ERROR AppTask::Init() #endif static CommonCaseDeviceServerInitParams initParams; - static OTATestEventTriggerDelegate testEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + static SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; + static OTATestEventTriggerHandler sOtaTestEventTriggerHandler{}; + VerifyOrDie(sTestEventTriggerDelegate.Init(ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR); + VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sOtaTestEventTriggerHandler) == CHIP_NO_ERROR); (void) initParams.InitializeStaticResourcesBeforeServerInit(); - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; ReturnErrorOnFailure(chip::Server::GetInstance().Init(initParams)); AppFabricTableDelegate::Init(); diff --git a/examples/lock-app/qpg/BUILD.gn b/examples/lock-app/qpg/BUILD.gn index 1023e9f275..2ade124e8d 100644 --- a/examples/lock-app/qpg/BUILD.gn +++ b/examples/lock-app/qpg/BUILD.gn @@ -51,7 +51,7 @@ qpg_executable("lock_app") { output_name = "chip-${qpg_target_ic}-lock-example.out" sources = [ - "${chip_root}/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerDelegate.cpp", + "${chip_root}/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.cpp", "${examples_plat_dir}/app/main.cpp", "${examples_plat_dir}/ota/ota.cpp", "src/AppTask.cpp", diff --git a/examples/lock-app/qpg/src/AppTask.cpp b/examples/lock-app/qpg/src/AppTask.cpp index 3466d8c4a4..b477054f2f 100644 --- a/examples/lock-app/qpg/src/AppTask.cpp +++ b/examples/lock-app/qpg/src/AppTask.cpp @@ -26,7 +26,8 @@ #include #include -#include +#include +#include #include #include #include @@ -189,10 +190,13 @@ void AppTask::InitServer(intptr_t arg) nativeParams.openThreadInstancePtr = chip::DeviceLayer::ThreadStackMgrImpl().OTInstance(); initParams.endpointNativeParams = static_cast(&nativeParams); - // Use GenericFaultTestEventTriggerDelegate to inject faults - static GenericFaultTestEventTriggerDelegate testEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + // Use GenericFaultTestEventTriggerHandler to inject faults + static SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; + static GenericFaultTestEventTriggerHandler sFaultTestEventTriggerHandler{}; + VerifyOrDie(sTestEventTriggerDelegate.Init(ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR); + VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sFaultTestEventTriggerHandler) == CHIP_NO_ERROR); (void) initParams.InitializeStaticResourcesBeforeServerInit(); - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; chip::Server::GetInstance().Init(initParams); diff --git a/examples/platform/ameba/test_event_trigger/AmebaTestEventTriggerDelegate.cpp b/examples/platform/ameba/test_event_trigger/AmebaTestEventTriggerDelegate.cpp index 9cfddd6f3d..8bbae5e197 100644 --- a/examples/platform/ameba/test_event_trigger/AmebaTestEventTriggerDelegate.cpp +++ b/examples/platform/ameba/test_event_trigger/AmebaTestEventTriggerDelegate.cpp @@ -28,10 +28,4 @@ bool AmebaTestEventTriggerDelegate::DoesEnableKeyMatch(const ByteSpan & enableKe return !mEnableKey.empty() && mEnableKey.data_equal(enableKey); } -CHIP_ERROR AmebaTestEventTriggerDelegate::HandleEventTrigger(uint64_t eventTrigger) -{ - bool success = emberAfHandleEventTrigger(eventTrigger); - return success ? CHIP_NO_ERROR : CHIP_ERROR_INVALID_ARGUMENT; -} - } // namespace chip diff --git a/examples/platform/ameba/test_event_trigger/AmebaTestEventTriggerDelegate.h b/examples/platform/ameba/test_event_trigger/AmebaTestEventTriggerDelegate.h index d461194de9..9e77040630 100644 --- a/examples/platform/ameba/test_event_trigger/AmebaTestEventTriggerDelegate.h +++ b/examples/platform/ameba/test_event_trigger/AmebaTestEventTriggerDelegate.h @@ -18,15 +18,38 @@ #pragma once -#include "SmokeCOAlarmManager.h" +#include + #include +#include +#include +#include + +/** + * @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 + * + * @warning *** DO NOT USE FOR STANDARD CLUSTER EVENT TRIGGERS *** + * + * TODO(#31723): Rename `emberAfHandleEventTrigger` to `AmebaHandleGlobalTestEventTrigger` + * + * @retval true on success + * @retval false if error happened + */ +bool emberAfHandleEventTrigger(uint64_t eventTrigger); namespace chip { -class AmebaTestEventTriggerDelegate : public TestEventTriggerDelegate +class AmebaTestEventTriggerDelegate : public TestEventTriggerDelegate, TestEventTriggerHandler { public: - explicit AmebaTestEventTriggerDelegate(const ByteSpan & enableKey) : mEnableKey(enableKey) {} + explicit AmebaTestEventTriggerDelegate(const ByteSpan & enableKey) : mEnableKey(enableKey) + { + VerifyOrDie(AddHandler(this) == CHIP_NO_ERROR); + } /** * @brief Checks to see if `enableKey` provided matches value chosen by the manufacturer. @@ -35,27 +58,14 @@ class AmebaTestEventTriggerDelegate : public TestEventTriggerDelegate */ bool DoesEnableKeyMatch(const ByteSpan & enableKey) const override; - /** - * @brief User handler for handling the test event trigger based on `eventTrigger` provided. - * @param eventTrigger Event trigger to handle. - * @return CHIP_NO_ERROR on success or CHIP_ERROR_INVALID_ARGUMENT on failure. - */ - CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override; + CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override + { + // WARNING: LEGACY SUPPORT ONLY, DO NOT EXTEND FOR STANDARD CLUSTERS + return (emberAfHandleEventTrigger(eventTrigger)) ? CHIP_NO_ERROR : CHIP_ERROR_INVALID_ARGUMENT; + } private: ByteSpan mEnableKey; }; } // 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 emberAfHandleEventTrigger(uint64_t eventTrigger); diff --git a/examples/platform/esp32/common/Esp32AppServer.cpp b/examples/platform/esp32/common/Esp32AppServer.cpp index 8baafcb458..cfce1aef43 100644 --- a/examples/platform/esp32/common/Esp32AppServer.cpp +++ b/examples/platform/esp32/common/Esp32AppServer.cpp @@ -19,8 +19,9 @@ #include "Esp32AppServer.h" #include "CHIPDeviceManager.h" #include +#include #include -#include +#include #include #include #include @@ -168,8 +169,11 @@ void Esp32AppServer::Init(AppDelegate * sAppDelegate) ESP_LOGE(TAG, "Failed to convert the EnableKey string to octstr type value"); memset(sTestEventTriggerEnableKey, 0, sizeof(sTestEventTriggerEnableKey)); } - static OTATestEventTriggerDelegate testEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + static SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; + static OTATestEventTriggerHandler sOtaTestEventTriggerHandler{}; + VerifyOrDie(sTestEventTriggerDelegate.Init(ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR); + VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sOtaTestEventTriggerHandler) == CHIP_NO_ERROR); + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; #endif // CONFIG_TEST_EVENT_TRIGGER_ENABLED (void) initParams.InitializeStaticResourcesBeforeServerInit(); if (sAppDelegate != nullptr) diff --git a/examples/platform/linux/AppMain.cpp b/examples/platform/linux/AppMain.cpp index 56e6edd446..f5ce3abd85 100644 --- a/examples/platform/linux/AppMain.cpp +++ b/examples/platform/linux/AppMain.cpp @@ -74,11 +74,21 @@ #endif #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR -#include +#include #endif #if CHIP_DEVICE_CONFIG_ENABLE_SMOKE_CO_TRIGGER -#include +#include #endif +#if CHIP_DEVICE_CONFIG_ENABLE_BOOLEAN_STATE_CONFIGURATION_TRIGGER +#include // nogncheck +#endif +#if CHIP_DEVICE_CONFIG_ENABLE_ENERGY_EVSE_TRIGGER +#include // nogncheck +#endif +#if CHIP_DEVICE_CONFIG_ENABLE_ENERGY_REPORTING_TRIGGER +#include // nogncheck +#endif + #include #include @@ -315,34 +325,13 @@ static bool EnsureWiFiIsStarted() } #endif -class SampleTestEventTriggerDelegate : public TestEventTriggerDelegate +class SampleTestEventTriggerHandler : public TestEventTriggerHandler { -public: - /// NOTE: If you copy this, please use the reserved range FFFF_FFFF__xxxx for your trigger codes. + /// NOTE: If you copy this for NON-STANDARD CLUSTERS OR USAGES, please use the reserved range FFFF_FFFF__xxxx for your + /// trigger codes. NOTE: Standard codes are _xxxx_xxxx_xxxx. static constexpr uint64_t kSampleTestEventTriggerAlwaysSuccess = static_cast(0xFFFF'FFFF'FFF1'0000ull); - SampleTestEventTriggerDelegate() { memset(&mEnableKey[0], 0, sizeof(mEnableKey)); } - - /** - * @brief Initialize the delegate with a key and an optional other handler - * - * The `otherDelegate` will be called if there is no match of the eventTrigger - * when HandleEventTrigger is called, if it is non-null. - * - * @param enableKey - EnableKey to use for this instance. - * @param otherDelegate - Other delegate (e.g. OTA delegate) where defer trigger. Can be nullptr - * @return CHIP_NO_ERROR on success, CHIP_ERROR_INVALID_ARGUMENT if enableKey is wrong size. - */ - CHIP_ERROR Init(ByteSpan enableKey, TestEventTriggerDelegate * otherDelegate) - { - VerifyOrReturnError(enableKey.size() == sizeof(mEnableKey), CHIP_ERROR_INVALID_ARGUMENT); - mOtherDelegate = otherDelegate; - MutableByteSpan ourEnableKeySpan(mEnableKey); - return CopySpanToMutableSpan(enableKey, ourEnableKeySpan); - } - - bool DoesEnableKeyMatch(const ByteSpan & enableKey) const override { return enableKey.data_equal(ByteSpan(mEnableKey)); } - +public: CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override { ChipLogProgress(Support, "Saw TestEventTrigger: " ChipLogFormatX64, ChipLogValueX64(eventTrigger)); @@ -354,12 +343,8 @@ class SampleTestEventTriggerDelegate : public TestEventTriggerDelegate return CHIP_NO_ERROR; } - return (mOtherDelegate != nullptr) ? mOtherDelegate->HandleEventTrigger(eventTrigger) : CHIP_ERROR_INVALID_ARGUMENT; + return CHIP_ERROR_INVALID_ARGUMENT; } - -private: - uint8_t mEnableKey[TestEventTriggerDelegate::kEnableKeyLength]; - TestEventTriggerDelegate * mOtherDelegate = nullptr; }; int ChipLinuxAppInit(int argc, char * const argv[], OptionSet * customOptions, @@ -532,25 +517,36 @@ void ChipLinuxAppMainLoop(AppMainLoopImplementation * impl) initParams.operationalKeystore = &LinuxDeviceOptions::GetInstance().mCSRResponseOptions.badCsrOperationalKeyStoreForTest; } - TestEventTriggerDelegate * otherDelegate = nullptr; + // For general testing of TestEventTrigger, we have a common "core" event trigger delegate. + static SimpleTestEventTriggerDelegate sTestEventTriggerDelegate; + static SampleTestEventTriggerHandler sTestEventTriggerHandler; + VerifyOrDie(sTestEventTriggerDelegate.Init(ByteSpan(LinuxDeviceOptions::GetInstance().testEventTriggerEnableKey)) == + CHIP_NO_ERROR); + VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sTestEventTriggerHandler) == CHIP_NO_ERROR); + #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR // We want to allow triggering OTA queries if OTA requestor is enabled - static OTATestEventTriggerDelegate otaTestEventTriggerDelegate{ ByteSpan( - LinuxDeviceOptions::GetInstance().testEventTriggerEnableKey) }; - otherDelegate = &otaTestEventTriggerDelegate; + static OTATestEventTriggerHandler sOtaTestEventTriggerHandler; + sTestEventTriggerDelegate.AddHandler(&sOtaTestEventTriggerHandler); #endif #if CHIP_DEVICE_CONFIG_ENABLE_SMOKE_CO_TRIGGER - static SmokeCOTestEventTriggerDelegate smokeCOTestEventTriggerDelegate{ - ByteSpan(LinuxDeviceOptions::GetInstance().testEventTriggerEnableKey), otherDelegate - }; - otherDelegate = &smokeCOTestEventTriggerDelegate; + static SmokeCOTestEventTriggerHandler sSmokeCOTestEventTriggerHandler; + sTestEventTriggerDelegate.AddHandler(&sSmokeCOTestEventTriggerHandler); +#endif +#if CHIP_DEVICE_CONFIG_ENABLE_BOOLEAN_STATE_CONFIGURATION_TRIGGER + static BooleanStateConfigurationTestEventTriggerHandler sBooleanStateConfigurationTestEventTriggerHandler; + sTestEventTriggerDelegate.AddHandler(&sBooleanStateConfigurationTestEventTriggerHandler); +#endif +#if CHIP_DEVICE_CONFIG_ENABLE_ENERGY_EVSE_TRIGGER + static EnergyEvseTestEventTriggerHandler sEnergyEvseTestEventTriggerHandler; + sTestEventTriggerDelegate.AddHandler(&sEnergyEvseTestEventTriggerHandler); +#endif +#if CHIP_DEVICE_CONFIG_ENABLE_ENERGY_REPORTING_TRIGGER + static EnergyReportingTestEventTriggerHandler sEnergyReportingTestEventTriggerHandler; + sTestEventTriggerDelegate.AddHandler(&sEnergyReportingTestEventTriggerHandler); #endif - // For general testing of TestEventTrigger, we have a common "core" event trigger delegate. - static SampleTestEventTriggerDelegate testEventTriggerDelegate; - VerifyOrDie(testEventTriggerDelegate.Init(ByteSpan(LinuxDeviceOptions::GetInstance().testEventTriggerEnableKey), - otherDelegate) == CHIP_NO_ERROR); - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; // We need to set DeviceInfoProvider before Server::Init to setup the storage of DeviceInfoProvider properly. DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider); diff --git a/examples/platform/linux/BUILD.gn b/examples/platform/linux/BUILD.gn index 46229c5f7e..076926de54 100644 --- a/examples/platform/linux/BUILD.gn +++ b/examples/platform/linux/BUILD.gn @@ -29,12 +29,24 @@ config("app-main-config") { source_set("ota-test-event-trigger") { sources = [ - "${chip_root}/src/app/clusters/ota-requestor/OTATestEventTriggerDelegate.h", + "${chip_root}/src/app/clusters/ota-requestor/OTATestEventTriggerHandler.h", ] } source_set("smco-test-event-trigger") { - sources = [ "${chip_root}/src/app/clusters/smoke-co-alarm-server/SmokeCOTestEventTriggerDelegate.h" ] + sources = [ "${chip_root}/src/app/clusters/smoke-co-alarm-server/SmokeCOTestEventTriggerHandler.h" ] +} + +source_set("boolean-state-configuration-test-event-trigger") { + sources = [ "${chip_root}/src/app/clusters/boolean-state-configuration-server/BooleanStateConfigurationTestEventTriggerHandler.h" ] +} + +source_set("energy-evse-test-event-trigger") { + sources = [ "${chip_root}/src/app/clusters/energy-evse-server/EnergyEvseTestEventTriggerHandler.h" ] +} + +source_set("energy-reporting-test-event-trigger") { + sources = [ "${chip_root}/src/app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerHandler.h" ] } source_set("app-main") { @@ -94,7 +106,12 @@ source_set("app-main") { ] } - defines += [ "CHIP_DEVICE_CONFIG_ENABLE_SMOKE_CO_TRIGGER=${chip_enable_smoke_co_trigger}" ] + defines += [ + "CHIP_DEVICE_CONFIG_ENABLE_SMOKE_CO_TRIGGER=${chip_enable_smoke_co_trigger}", + "CHIP_DEVICE_CONFIG_ENABLE_BOOLEAN_STATE_CONFIGURATION_TRIGGER=false", + "CHIP_DEVICE_CONFIG_ENABLE_ENERGY_EVSE_TRIGGER=false", + "CHIP_DEVICE_CONFIG_ENABLE_ENERGY_REPORTING_TRIGGER=false", + ] public_configs = [ ":app-main-config" ] } diff --git a/examples/platform/nxp/se05x/linux/AppMain.cpp b/examples/platform/nxp/se05x/linux/AppMain.cpp index 2c04b81b0c..bd13e24968 100644 --- a/examples/platform/nxp/se05x/linux/AppMain.cpp +++ b/examples/platform/nxp/se05x/linux/AppMain.cpp @@ -69,7 +69,8 @@ #endif // CHIP_CONFIG_TRANSPORT_TRACE_ENABLED #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR -#include +#include +#include #endif #include @@ -315,9 +316,12 @@ void ChipLinuxAppMainLoop(AppMainLoopImplementation * impl) } #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR - static OTATestEventTriggerDelegate testEventTriggerDelegate{ ByteSpan( - LinuxDeviceOptions::GetInstance().testEventTriggerEnableKey) }; - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + static SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; + static OTATestEventTriggerHandler sOtaTestEventTriggerHandler{}; + VerifyOrDie(sTestEventTriggerDelegate.Init(ByteSpan(LinuxDeviceOptions::GetInstance().testEventTriggerEnableKey)) == + CHIP_NO_ERROR); + VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sOtaTestEventTriggerHandler) == CHIP_NO_ERROR); + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; #endif // We need to set DeviceInfoProvider before Server::Init to setup the storage of DeviceInfoProvider properly. diff --git a/examples/platform/nxp/se05x/linux/BUILD.gn b/examples/platform/nxp/se05x/linux/BUILD.gn index 2a93a4bf08..a5efe46074 100644 --- a/examples/platform/nxp/se05x/linux/BUILD.gn +++ b/examples/platform/nxp/se05x/linux/BUILD.gn @@ -25,7 +25,7 @@ config("app-main-config") { source_set("ota-test-event-trigger") { sources = [ - "${chip_root}/src/app/clusters/ota-requestor/OTATestEventTriggerDelegate.h", + "${chip_root}/src/app/clusters/ota-requestor/OTATestEventTriggerHandler.h", ] } diff --git a/examples/platform/silabs/MatterConfig.cpp b/examples/platform/silabs/MatterConfig.cpp index f9a1b7188b..875c82d980 100644 --- a/examples/platform/silabs/MatterConfig.cpp +++ b/examples/platform/silabs/MatterConfig.cpp @@ -219,8 +219,9 @@ CHIP_ERROR SilabsMatterConfig::InitMatter(const char * appName) SILABS_LOG("Failed to convert the EnableKey string to octstr type value"); memset(sTestEventTriggerEnableKey, 0, sizeof(sTestEventTriggerEnableKey)); } - static SilabsTestEventTriggerDelegate testEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + // TODO(#31723): Show to customers that they can do `Server::GetInstance().GetTestEventTriggerDelegate().AddHandler()` + static SilabsTestEventTriggerDelegate sTestEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; #endif // SILABS_TEST_EVENT_TRIGGER_ENABLED #if CHIP_CRYPTO_PLATFORM && !(defined(SIWX_917)) @@ -232,8 +233,9 @@ CHIP_ERROR SilabsMatterConfig::InitMatter(const char * appName) #ifdef PERFORMANCE_TEST_ENABLED // Set up Test Event Trigger command of the General Diagnostics cluster. Used only in performance testing - static SilabsTestEventTriggerDelegate testEventTriggerDelegate{ ByteSpan(kTestEventTriggerEnableKey) }; - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + // TODO(#31723): Show to customers that they can do `Server::GetInstance().GetTestEventTriggerDelegate().AddHandler()` + static SilabsTestEventTriggerDelegate sTestEventTriggerDelegate{ ByteSpan(kTestEventTriggerEnableKey) }; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; #endif // Initialize the remaining (not overridden) providers to the SDK example defaults diff --git a/examples/platform/silabs/SilabsTestEventTriggerDelegate.cpp b/examples/platform/silabs/SilabsTestEventTriggerDelegate.cpp index f231067cec..f9a11df74d 100644 --- a/examples/platform/silabs/SilabsTestEventTriggerDelegate.cpp +++ b/examples/platform/silabs/SilabsTestEventTriggerDelegate.cpp @@ -27,10 +27,4 @@ bool SilabsTestEventTriggerDelegate::DoesEnableKeyMatch(const ByteSpan & enableK return !mEnableKey.empty() && mEnableKey.data_equal(enableKey); } -CHIP_ERROR SilabsTestEventTriggerDelegate::HandleEventTrigger(uint64_t eventTrigger) -{ - bool success = emberAfHandleEventTrigger(eventTrigger); - return success ? CHIP_NO_ERROR : CHIP_ERROR_INVALID_ARGUMENT; -} - } // namespace chip diff --git a/examples/platform/silabs/SilabsTestEventTriggerDelegate.h b/examples/platform/silabs/SilabsTestEventTriggerDelegate.h index e9cd62fb81..e08e972f69 100644 --- a/examples/platform/silabs/SilabsTestEventTriggerDelegate.h +++ b/examples/platform/silabs/SilabsTestEventTriggerDelegate.h @@ -18,14 +18,38 @@ #pragma once +#include + #include +#include +#include +#include + +/** + * @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 + * + * @warning *** DO NOT USE FOR STANDARD CLUSTER EVENT TRIGGERS *** + * + * TODO(#31723): Rename `emberAfHandleEventTrigger` to `AmebaHandleGlobalTestEventTrigger` + * + * @retval true on success + * @retval false if error happened + */ +bool emberAfHandleEventTrigger(uint64_t eventTrigger); namespace chip { -class SilabsTestEventTriggerDelegate : public TestEventTriggerDelegate +class SilabsTestEventTriggerDelegate : public TestEventTriggerDelegate, TestEventTriggerHandler { public: - explicit SilabsTestEventTriggerDelegate(const ByteSpan & enableKey) : mEnableKey(enableKey) {} + explicit SilabsTestEventTriggerDelegate(const ByteSpan & enableKey) : mEnableKey(enableKey) + { + VerifyOrDie(AddHandler(this) == CHIP_NO_ERROR); + } /** * @brief Checks to see if `enableKey` provided matches value chosen by the manufacturer. @@ -35,26 +59,19 @@ class SilabsTestEventTriggerDelegate : public TestEventTriggerDelegate bool DoesEnableKeyMatch(const ByteSpan & enableKey) const override; /** - * @brief User handler for handling the test event trigger based on `eventTrigger` provided. - * @param eventTrigger Event trigger to handle. - * @return CHIP_NO_ERROR on success or CHIP_ERROR_INVALID_ARGUMENT on failure. + * @brief Delegates handling to global `emberAfHandleEventTrigger` function. DO NOT EXTEND. + * + * @param eventTrigger - trigger to process. + * @return CHIP_NO_ERROR if properly handled, else another CHIP_ERROR. */ - CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override; + CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override + { + // WARNING: LEGACY SUPPORT ONLY, DO NOT EXTEND FOR STANDARD CLUSTERS + return (emberAfHandleEventTrigger(eventTrigger)) ? CHIP_NO_ERROR : CHIP_ERROR_INVALID_ARGUMENT; + } private: ByteSpan mEnableKey; }; } // 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 emberAfHandleEventTrigger(uint64_t eventTrigger); diff --git a/examples/providers/DeviceInfoProviderImpl.cpp b/examples/providers/DeviceInfoProviderImpl.cpp index 5524b770c0..7b07561c2c 100644 --- a/examples/providers/DeviceInfoProviderImpl.cpp +++ b/examples/providers/DeviceInfoProviderImpl.cpp @@ -100,8 +100,8 @@ bool DeviceInfoProviderImpl::FixedLabelIteratorImpl::Next(FixedLabelType & outpu VerifyOrReturnError(std::strlen(labelPtr) <= kMaxLabelNameLength, false); VerifyOrReturnError(std::strlen(valuePtr) <= kMaxLabelValueLength, false); - Platform::CopyString(mFixedLabelNameBuf, kMaxLabelNameLength + 1, labelPtr); - Platform::CopyString(mFixedLabelValueBuf, kMaxLabelValueLength + 1, valuePtr); + Platform::CopyString(mFixedLabelNameBuf, labelPtr); + Platform::CopyString(mFixedLabelValueBuf, valuePtr); output.label = CharSpan::fromCharString(mFixedLabelNameBuf); output.value = CharSpan::fromCharString(mFixedLabelValueBuf); diff --git a/examples/pump-app/nrfconnect/main/AppTask.cpp b/examples/pump-app/nrfconnect/main/AppTask.cpp index fa38eba254..329c8d95f6 100644 --- a/examples/pump-app/nrfconnect/main/AppTask.cpp +++ b/examples/pump-app/nrfconnect/main/AppTask.cpp @@ -25,7 +25,8 @@ #include #include -#include +#include +#include #include #include #include @@ -186,9 +187,12 @@ CHIP_ERROR AppTask::Init() #endif static CommonCaseDeviceServerInitParams initParams; - static OTATestEventTriggerDelegate testEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + static SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; + static OTATestEventTriggerHandler sOtaTestEventTriggerHandler{}; + VerifyOrDie(sTestEventTriggerDelegate.Init(ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR); + VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sOtaTestEventTriggerHandler) == CHIP_NO_ERROR); (void) initParams.InitializeStaticResourcesBeforeServerInit(); - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; ReturnErrorOnFailure(chip::Server::GetInstance().Init(initParams)); AppFabricTableDelegate::Init(); diff --git a/examples/pump-controller-app/nrfconnect/main/AppTask.cpp b/examples/pump-controller-app/nrfconnect/main/AppTask.cpp index db96480e5a..ca79a41160 100644 --- a/examples/pump-controller-app/nrfconnect/main/AppTask.cpp +++ b/examples/pump-controller-app/nrfconnect/main/AppTask.cpp @@ -25,7 +25,8 @@ #include #include -#include +#include +#include #include #include #include @@ -184,9 +185,12 @@ CHIP_ERROR AppTask::Init() #endif static CommonCaseDeviceServerInitParams initParams; - static OTATestEventTriggerDelegate testEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + static SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; + static OTATestEventTriggerHandler sOtaTestEventTriggerHandler{}; + VerifyOrDie(sTestEventTriggerDelegate.Init(ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR); + VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sOtaTestEventTriggerHandler) == CHIP_NO_ERROR); (void) initParams.InitializeStaticResourcesBeforeServerInit(); - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; ReturnErrorOnFailure(chip::Server::GetInstance().Init(initParams)); AppFabricTableDelegate::Init(); diff --git a/examples/smoke-co-alarm-app/silabs/src/SmokeCoAlarmManager.cpp b/examples/smoke-co-alarm-app/silabs/src/SmokeCoAlarmManager.cpp index 85c8bca219..7d64a03637 100644 --- a/examples/smoke-co-alarm-app/silabs/src/SmokeCoAlarmManager.cpp +++ b/examples/smoke-co-alarm-app/silabs/src/SmokeCoAlarmManager.cpp @@ -21,7 +21,7 @@ #include "AppTask.h" #include -#include +#include #include using namespace chip; diff --git a/examples/thermostat/qpg/BUILD.gn b/examples/thermostat/qpg/BUILD.gn new file mode 100644 index 0000000000..9989e349fc --- /dev/null +++ b/examples/thermostat/qpg/BUILD.gn @@ -0,0 +1,154 @@ +# Copyright(c) 2021 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. + +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") +import("//build_overrides/openthread.gni") +import("//build_overrides/qpg_sdk.gni") + +import("${build_root}/config/defaults.gni") +import("${chip_root}/src/platform/device.gni") +import("${qpg_sdk_build_root}/qpg_executable.gni") +import("${qpg_sdk_build_root}/qpg_sdk.gni") + +# declares chip_build_libshell = false +import("${chip_root}/src/lib/lib.gni") + +# declares chip_enable_pw_rpc = false +import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni") +if (chip_enable_pw_rpc) { + import("//build_overrides/pigweed.gni") + import("$dir_pw_build/target_types.gni") +} + +assert(current_os == "freertos") + +qpg_project_dir = "${chip_root}/examples/thermostat/qpg" +examples_plat_dir = "${chip_root}/examples/platform/qpg" + +qpg_sdk("sdk") { + include_dirs = [ + "${chip_root}/src/platform/qpg", + "${examples_plat_dir}", + "${qpg_project_dir}/include", + ] + + defines = [ "PW_RPC_ENABLED=${chip_enable_pw_rpc}" ] +} + +qpg_executable("thermostat") { + output_name = "chip-${qpg_target_ic}-thermostat-example.out" + + sources = [ + "${chip_root}/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.cpp", + "${examples_plat_dir}/app/main.cpp", + "${examples_plat_dir}/ota/ota.cpp", + "src/AppTask.cpp", + "src/ThermostaticRadiatorValveManager.cpp", + "src/ZclCallbacks.cpp", + ] + + deps = [ + ":sdk", + "${chip_root}/examples/providers:device_info_provider", + "${chip_root}/examples/thermostat/qpg/zap/", + "${chip_root}/src/lib", + "${chip_root}/src/setup_payload", + "${chip_root}/third_party/openthread/platforms:libopenthread-platform", + "${chip_root}/third_party/openthread/platforms:libopenthread-platform-utils", + "${chip_root}/third_party/qpg_sdk:qpg_thermostat_factorydata_lib", + "${chip_root}/third_party/qpg_sdk:qpg_thermostat_firmwaredata_lib", + ] + + if (chip_openthread_ftd) { + deps += [ "${chip_root}/third_party/openthread/repo:libopenthread-ftd" ] + } else { + deps += [ "${chip_root}/third_party/openthread/repo:libopenthread-mtd" ] + } + + include_dirs = [ + "include", + "${examples_plat_dir}/ota", + ] + + defines = [] + + if (chip_enable_pw_rpc) { + defines += [ + "PW_RPC_ATTRIBUTE_SERVICE=1", + "PW_RPC_BUTTON_SERVICE=1", + "PW_RPC_DEVICE_SERVICE=1", + "PW_RPC_LOCKING_SERVICE=1", + ] + + sources += [ + "${chip_root}/examples/common/pigweed/RpcService.cpp", + "${chip_root}/examples/common/pigweed/qpg/PigweedLoggerMutex.cpp", + "${examples_plat_dir}/PigweedLogger.cpp", + "${examples_plat_dir}/Rpc.cpp", + "${examples_plat_dir}/uart.c", + ] + + deps += [ + "$dir_pw_hdlc:default_addresses", + "$dir_pw_hdlc:rpc_channel_output", + "$dir_pw_stream:sys_io_stream", + "${chip_root}/config/qpg/lib/pw_rpc:pw_rpc", + "${chip_root}/examples/common/pigweed:attributes_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:button_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:device_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:locking_service.nanopb_rpc", + "${examples_plat_dir}/pw_sys_io:pw_sys_io_qpg", + ] + + deps += pw_build_LINK_DEPS + + include_dirs += [ + "${chip_root}/examples/common", + "${chip_root}/examples/common/pigweed/qpg", + ] + } else { + # The below gives compiler erros in pigweed, therefore it is only enabled + # when rpc is not + cflags = [ "-Wconversion" ] + } + + if (chip_build_libshell) { + deps += [ "${examples_plat_dir}:qpg-matter-shell" ] + } else { + if (chip_openthread_ftd) { + deps += + [ "${chip_root}/third_party/openthread/repo:libopenthread-cli-ftd" ] + } else { + deps += + [ "${chip_root}/third_party/openthread/repo:libopenthread-cli-mtd" ] + } + } + + ldscript = "${qpg_sdk_root}/Libraries/Qorvo/QorvoStack/gen/QorvoStack_${qpg_target_ic}/QorvoStack_${qpg_target_ic}.ld" + + inputs = [ ldscript ] + + ldflags = [ "-T" + rebase_path(ldscript, root_build_dir) ] + + output_dir = root_out_dir +} + +group("qpg") { + deps = [ ":thermostat" ] +} + +group("default") { + deps = [ ":qpg" ] +} diff --git a/examples/window-app/nrfconnect/main/AppTask.cpp b/examples/window-app/nrfconnect/main/AppTask.cpp index f607114da6..7c50aedc97 100644 --- a/examples/window-app/nrfconnect/main/AppTask.cpp +++ b/examples/window-app/nrfconnect/main/AppTask.cpp @@ -24,8 +24,9 @@ #include +#include #include -#include +#include #include #include #include @@ -188,9 +189,12 @@ CHIP_ERROR AppTask::Init() #endif static CommonCaseDeviceServerInitParams initParams; - static OTATestEventTriggerDelegate testEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + static SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; + static OTATestEventTriggerHandler sOtaTestEventTriggerHandler{}; + VerifyOrDie(sTestEventTriggerDelegate.Init(ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR); + VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sOtaTestEventTriggerHandler) == CHIP_NO_ERROR); (void) initParams.InitializeStaticResourcesBeforeServerInit(); - initParams.testEventTriggerDelegate = &testEventTriggerDelegate; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; ReturnErrorOnFailure(chip::Server::GetInstance().Init(initParams)); AppFabricTableDelegate::Init(); diff --git a/src/app/TestEventTriggerDelegate.h b/src/app/TestEventTriggerDelegate.h index 5f72415d02..33869d8c09 100644 --- a/src/app/TestEventTriggerDelegate.h +++ b/src/app/TestEventTriggerDelegate.h @@ -18,12 +18,27 @@ #pragma once #include +#include #include #include #include namespace chip { +class TestEventTriggerHandler : public IntrusiveListNodeBase +{ +public: + virtual ~TestEventTriggerHandler() = default; + /** + * Handles the test event trigger based on `eventTrigger` provided. + * + * @param[in] eventTrigger Event trigger to handle. + * + * @return CHIP_NO_ERROR on success or another CHIP_ERROR on failure + */ + virtual CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) = 0; +}; + class TestEventTriggerDelegate { public: @@ -39,14 +54,76 @@ class TestEventTriggerDelegate virtual bool DoesEnableKeyMatch(const ByteSpan & enableKey) const = 0; /** - * Expectation is that the caller has already validated the enable key before calling this. * Handles the test event trigger based on `eventTrigger` provided. * + * By default, this iterates over handlers added via `AddEventTriggerHandler`. + * + * If more specific behavior is desired, the method can be completely overridden. + * * @param[in] eventTrigger Event trigger to handle. * * @return CHIP_NO_ERROR on success or another CHIP_ERROR on failure */ - virtual CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) = 0; + virtual CHIP_ERROR HandleEventTriggers(uint64_t eventTrigger) + { + CHIP_ERROR last_error = CHIP_ERROR_INVALID_ARGUMENT; + for (TestEventTriggerHandler & handler : mHandlerList) + { + last_error = handler.HandleEventTrigger(eventTrigger); + if (last_error == CHIP_NO_ERROR) + { + break; + } + } + + return last_error; + } + + CHIP_ERROR AddHandler(TestEventTriggerHandler * handler) + { + VerifyOrReturnError(!mHandlerList.Contains(handler), CHIP_ERROR_INVALID_ARGUMENT); + mHandlerList.PushBack(handler); + return CHIP_NO_ERROR; + } + + void RemoveHandler(TestEventTriggerHandler * handler) + { + VerifyOrReturn(mHandlerList.Contains(handler)); + mHandlerList.Remove(handler); + } + + void ClearAllHandlers() { mHandlerList.Clear(); } + +protected: + IntrusiveList mHandlerList; +}; + +/** + * @brief TestEventTriggerDelegate that owns its enable key in RAM. + * + */ +class SimpleTestEventTriggerDelegate : public TestEventTriggerDelegate +{ +public: + SimpleTestEventTriggerDelegate() { memset(&mEnableKey[0], 0, sizeof(mEnableKey)); } + + /** + * @brief Initialize the delegate with a key + * + * @param enableKey - EnableKey to use for this instance. + * @return CHIP_NO_ERROR on success, CHIP_ERROR_INVALID_ARGUMENT if enableKey is wrong size. + */ + CHIP_ERROR Init(ByteSpan enableKey) + { + VerifyOrReturnError(enableKey.size() == sizeof(mEnableKey), CHIP_ERROR_INVALID_ARGUMENT); + MutableByteSpan ourEnableKeySpan(mEnableKey); + return CopySpanToMutableSpan(enableKey, ourEnableKeySpan); + } + + bool DoesEnableKeyMatch(const ByteSpan & enableKey) const override { return enableKey.data_equal(ByteSpan(mEnableKey)); } + +private: + uint8_t mEnableKey[TestEventTriggerDelegate::kEnableKeyLength]; }; } // namespace chip diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index 85cc2025a5..60b3738ae1 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -255,8 +255,8 @@ template("chip_data_model") { "${_app_root}/clusters/${cluster}/DefaultOTARequestorUserConsent.h", "${_app_root}/clusters/${cluster}/ExtendedOTARequestorDriver.cpp", "${_app_root}/clusters/${cluster}/OTARequestorStorage.h", - "${_app_root}/clusters/${cluster}/OTATestEventTriggerDelegate.cpp", - "${_app_root}/clusters/${cluster}/OTATestEventTriggerDelegate.h", + "${_app_root}/clusters/${cluster}/OTATestEventTriggerHandler.cpp", + "${_app_root}/clusters/${cluster}/OTATestEventTriggerHandler.h", ] } else if (cluster == "bindings") { sources += [ @@ -312,14 +312,37 @@ template("chip_data_model") { sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp", "${_app_root}/clusters/${cluster}/${cluster}.h", - "${_app_root}/clusters/${cluster}/SmokeCOTestEventTriggerDelegate.cpp", - "${_app_root}/clusters/${cluster}/SmokeCOTestEventTriggerDelegate.h", + "${_app_root}/clusters/${cluster}/SmokeCOTestEventTriggerHandler.h", + ] + } else if (cluster == "boolean-state-configuration-server") { + sources += [ + "${_app_root}/clusters/${cluster}/${cluster}.cpp", + "${_app_root}/clusters/${cluster}/${cluster}.h", + "${_app_root}/clusters/${cluster}/BooleanStateConfigurationTestEventTriggerHandler.cpp", + "${_app_root}/clusters/${cluster}/BooleanStateConfigurationTestEventTriggerHandler.h", ] } else if (cluster == "air-quality-server") { sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp", "${_app_root}/clusters/${cluster}/${cluster}.h", ] + } else if (cluster == "energy-evse-server") { + sources += [ + "${_app_root}/clusters/${cluster}/${cluster}.cpp", + "${_app_root}/clusters/${cluster}/${cluster}.h", + "${_app_root}/clusters/${cluster}/EnergyEvseTestEventTriggerHandler.h", + ] + } else if (cluster == "diagnostic-logs-server") { + sources += [ + "${_app_root}/clusters/${cluster}/${cluster}.cpp", + "${_app_root}/clusters/${cluster}/${cluster}.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}/EnergyReportingTestEventTriggerHandler.h", + ] } else { sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp" ] } diff --git a/src/app/clusters/smoke-co-alarm-server/SmokeCOTestEventTriggerDelegate.cpp b/src/app/clusters/boolean-state-configuration-server/BooleanStateConfigurationTestEventTriggerHandler.cpp similarity index 60% rename from src/app/clusters/smoke-co-alarm-server/SmokeCOTestEventTriggerDelegate.cpp rename to src/app/clusters/boolean-state-configuration-server/BooleanStateConfigurationTestEventTriggerHandler.cpp index 597737b14b..b2ffe7a160 100644 --- a/src/app/clusters/smoke-co-alarm-server/SmokeCOTestEventTriggerDelegate.cpp +++ b/src/app/clusters/boolean-state-configuration-server/BooleanStateConfigurationTestEventTriggerHandler.cpp @@ -15,27 +15,18 @@ * limitations under the License. */ -#include "SmokeCOTestEventTriggerDelegate.h" +#include "BooleanStateConfigurationTestEventTriggerHandler.h" -using namespace chip::app::Clusters::SmokeCoAlarm; +using namespace chip::app::Clusters::BooleanStateConfiguration; namespace chip { -bool SmokeCOTestEventTriggerDelegate::DoesEnableKeyMatch(const ByteSpan & enableKey) const +CHIP_ERROR BooleanStateConfigurationTestEventTriggerHandler::HandleEventTrigger(uint64_t eventTrigger) { - return !mEnableKey.empty() && mEnableKey.data_equal(enableKey); -} - -CHIP_ERROR SmokeCOTestEventTriggerDelegate::HandleEventTrigger(uint64_t eventTrigger) -{ - if (HandleSmokeCOTestEventTrigger(eventTrigger)) + if (HandleBooleanStateConfigurationTestEventTrigger(eventTrigger)) { return CHIP_NO_ERROR; } - if (mOtherDelegate != nullptr) - { - return mOtherDelegate->HandleEventTrigger(eventTrigger); - } return CHIP_ERROR_INVALID_ARGUMENT; } diff --git a/src/app/clusters/boolean-state-configuration-server/BooleanStateConfigurationTestEventTriggerHandler.h b/src/app/clusters/boolean-state-configuration-server/BooleanStateConfigurationTestEventTriggerHandler.h new file mode 100644 index 0000000000..92aa2e8a86 --- /dev/null +++ b/src/app/clusters/boolean-state-configuration-server/BooleanStateConfigurationTestEventTriggerHandler.h @@ -0,0 +1,49 @@ +/* + * + * 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. + */ + +#pragma once + +#include +#include + +namespace chip { + +enum class BooleanStateConfigurationTrigger : uint64_t +{ + kSensorTrigger = 0x0080000000000000, + kSensorUntrigger = 0x0080000000000001, +}; + +class BooleanStateConfigurationTestEventTriggerHandler : public TestEventTriggerHandler +{ +public: + CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override; +}; + +} // 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 HandleBooleanStateConfigurationTestEventTrigger(uint64_t eventTrigger); diff --git a/src/app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerHandler.h b/src/app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerHandler.h new file mode 100644 index 0000000000..d254d7a2db --- /dev/null +++ b/src/app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerHandler.h @@ -0,0 +1,70 @@ +/* + * + * 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 +#include + +/** + * @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); + +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 EnergyReportingTestEventTriggerHandler : public TestEventTriggerHandler +{ +public: + EnergyReportingTestEventTriggerHandler() {} + + CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override + { + if (HandleEnergyReportingTestEventTrigger(eventTrigger)) + { + return CHIP_NO_ERROR; + } + return CHIP_ERROR_INVALID_ARGUMENT; + } +}; + +} // namespace chip diff --git a/src/app/clusters/energy-evse-server/EnergyEvseTestEventTriggerHandler.h b/src/app/clusters/energy-evse-server/EnergyEvseTestEventTriggerHandler.h new file mode 100644 index 0000000000..307156e21f --- /dev/null +++ b/src/app/clusters/energy-evse-server/EnergyEvseTestEventTriggerHandler.h @@ -0,0 +1,89 @@ +/* + * + * 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. + */ + +#pragma once + +#include +#include + +/** + * @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 HandleEnergyEvseTestEventTrigger(uint64_t eventTrigger); + +namespace chip { + +/* + * These Test EventTrigger values are specified in the TC_EEVSE test plan + * and are defined conditions used in test events. + * + * They are sent along with the enableKey (manufacturer defined secret) + * in the General Diagnostic cluster TestEventTrigger command + */ +enum class EnergyEvseTrigger : uint64_t +{ + // Scenarios + // Basic Functionality Test Event | Simulate installation with _{A_CIRCUIT_CAPACITY}_=32A and + // _{A_USER_MAXIMUM_CHARGE_CURRENT}_=32A + kBasicFunctionality = 0x0099000000000000, + // Basic Functionality Test Event Clear | End simulation of installation + kBasicFunctionalityClear = 0x0099000000000001, + // EV Plugged-in Test Event | Simulate plugging the EV into the EVSE using a cable of 63A capacity + kEVPluggedIn = 0x0099000000000002, + // EV Plugged-in Test Event Clear | Simulate unplugging the EV + kEVPluggedInClear = 0x0099000000000003, + // EV Charge Demand Test Event | Simulate the EV presenting charge demand to the EVSE + kEVChargeDemand = 0x0099000000000004, + // EV Charge Demand Test Event Clear | Simulate the EV becoming fully charged + kEVChargeDemandClear = 0x0099000000000005, + // EVSE has a GroundFault fault + kEVSEGroundFault = 0x0099000000000010, + // EVSE has a OverTemperature fault + kEVSEOverTemperatureFault = 0x0099000000000011, + // EVSE faults have cleared + kEVSEFaultClear = 0x0099000000000012, + // EVSE Diagnostics Complete | Simulate diagnostics have been completed and return to normal + kEVSEDiagnosticsComplete = 0x0099000000000020, +}; + +class EnergyEvseTestEventTriggerHandler : public TestEventTriggerHandler +{ +public: + explicit EnergyEvseTestEventTriggerHandler() {} + + /** 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 + { + if (HandleEnergyEvseTestEventTrigger(eventTrigger)) + { + return CHIP_NO_ERROR; + } + return CHIP_ERROR_INVALID_ARGUMENT; + } +}; + +} // namespace chip diff --git a/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerDelegate.cpp b/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.cpp similarity index 90% rename from src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerDelegate.cpp rename to src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.cpp index 6c50b581e3..b733524839 100644 --- a/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerDelegate.cpp +++ b/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.cpp @@ -16,7 +16,7 @@ * limitations under the License. */ -#include "GenericFaultTestEventTriggerDelegate.h" +#include "GenericFaultTestEventTriggerHandler.h" #include #include @@ -25,15 +25,10 @@ using namespace ::chip::DeviceLayer; namespace chip { -bool GenericFaultTestEventTriggerDelegate::DoesEnableKeyMatch(const ByteSpan & enableKey) const -{ - return !mEnableKey.empty() && mEnableKey.data_equal(enableKey); -} - -CHIP_ERROR GenericFaultTestEventTriggerDelegate::HandleEventTrigger(uint64_t eventTrigger) +CHIP_ERROR GenericFaultTestEventTriggerHandler::HandleEventTrigger(uint64_t eventTrigger) { - if ((eventTrigger & ~kGenericFaultQueryFabricIndexMask) == kGenericFaultQueryTrigger) + if (eventTrigger == kGenericFaultQueryTrigger) { // Fault injection GeneralFaults hwFaultsPrevious; diff --git a/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerDelegate.h b/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.h similarity index 65% rename from src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerDelegate.h rename to src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.h index f304518c38..4dea743eec 100644 --- a/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerDelegate.h +++ b/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.h @@ -22,19 +22,12 @@ namespace chip { -class GenericFaultTestEventTriggerDelegate : public TestEventTriggerDelegate +class GenericFaultTestEventTriggerHandler : public TestEventTriggerHandler { public: - static constexpr uint64_t kGenericFaultQueryTrigger = 0xFFFF'FFFF'10D0'0001; - static constexpr uint64_t kGenericFaultQueryFabricIndexMask = 0xff; + static constexpr uint64_t kGenericFaultQueryTrigger = 0x3333'FFFF'10D0'0001; - explicit GenericFaultTestEventTriggerDelegate(const ByteSpan & enableKey) : mEnableKey(enableKey) {} - - bool DoesEnableKeyMatch(const ByteSpan & enableKey) const override; CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override; - -private: - ByteSpan mEnableKey; }; } // namespace chip diff --git a/src/app/clusters/general-diagnostics-server/general-diagnostics-server.cpp b/src/app/clusters/general-diagnostics-server/general-diagnostics-server.cpp index 994104abf2..4a7ab241f4 100644 --- a/src/app/clusters/general-diagnostics-server/general-diagnostics-server.cpp +++ b/src/app/clusters/general-diagnostics-server/general-diagnostics-server.cpp @@ -377,7 +377,7 @@ bool emberAfGeneralDiagnosticsClusterTestEventTriggerCallback(CommandHandler * c return true; } - CHIP_ERROR handleEventTriggerResult = triggerDelegate->HandleEventTrigger(commandData.eventTrigger); + CHIP_ERROR handleEventTriggerResult = triggerDelegate->HandleEventTriggers(commandData.eventTrigger); // When HandleEventTrigger fails, we simply convert any error to INVALID_COMMAND commandObj->AddStatus(commandPath, (handleEventTriggerResult != CHIP_NO_ERROR) ? Status::InvalidCommand : Status::Success); diff --git a/src/app/clusters/ota-requestor/OTATestEventTriggerDelegate.cpp b/src/app/clusters/ota-requestor/OTATestEventTriggerHandler.cpp similarity index 80% rename from src/app/clusters/ota-requestor/OTATestEventTriggerDelegate.cpp rename to src/app/clusters/ota-requestor/OTATestEventTriggerHandler.cpp index a879f817ed..885aced7fe 100644 --- a/src/app/clusters/ota-requestor/OTATestEventTriggerDelegate.cpp +++ b/src/app/clusters/ota-requestor/OTATestEventTriggerHandler.cpp @@ -16,7 +16,7 @@ * limitations under the License. */ -#include "OTATestEventTriggerDelegate.h" +#include "OTATestEventTriggerHandler.h" #include "OTARequestorInterface.h" @@ -24,12 +24,7 @@ namespace chip { -bool OTATestEventTriggerDelegate::DoesEnableKeyMatch(const ByteSpan & enableKey) const -{ - return !mEnableKey.empty() && mEnableKey.data_equal(enableKey); -} - -CHIP_ERROR OTATestEventTriggerDelegate::HandleEventTrigger(uint64_t eventTrigger) +CHIP_ERROR OTATestEventTriggerHandler::HandleEventTrigger(uint64_t eventTrigger) { if ((eventTrigger & ~kOtaQueryFabricIndexMask) == kOtaQueryTrigger) { diff --git a/src/app/clusters/ota-requestor/OTATestEventTriggerDelegate.h b/src/app/clusters/ota-requestor/OTATestEventTriggerHandler.h similarity index 73% rename from src/app/clusters/ota-requestor/OTATestEventTriggerDelegate.h rename to src/app/clusters/ota-requestor/OTATestEventTriggerHandler.h index 6c78a16a76..bb49a3d23d 100644 --- a/src/app/clusters/ota-requestor/OTATestEventTriggerDelegate.h +++ b/src/app/clusters/ota-requestor/OTATestEventTriggerHandler.h @@ -22,19 +22,15 @@ namespace chip { -class OTATestEventTriggerDelegate : public TestEventTriggerDelegate +class OTATestEventTriggerHandler : public TestEventTriggerHandler { public: - static constexpr uint64_t kOtaQueryTrigger = 0x0100'0000'0000'0100; + static constexpr uint64_t kOtaQueryTrigger = 0x002a'0000'0000'0100; static constexpr uint64_t kOtaQueryFabricIndexMask = 0xff; - explicit OTATestEventTriggerDelegate(const ByteSpan & enableKey) : mEnableKey(enableKey) {} + OTATestEventTriggerHandler() {} - bool DoesEnableKeyMatch(const ByteSpan & enableKey) const override; CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override; - -private: - ByteSpan mEnableKey; }; } // namespace chip diff --git a/src/app/clusters/smoke-co-alarm-server/SmokeCOTestEventTriggerDelegate.h b/src/app/clusters/smoke-co-alarm-server/SmokeCOTestEventTriggerDelegate.h deleted file mode 100644 index 599eb6ab2f..0000000000 --- a/src/app/clusters/smoke-co-alarm-server/SmokeCOTestEventTriggerDelegate.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * - * 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. - */ - -#pragma once - -#include -#include - -namespace chip { - -enum class SmokeCOTrigger : uint64_t -{ - // Force alarm commands - kForceSmokeWarning = 0xffffffff00000090, - kForceCOWarning = 0xffffffff00000091, - kForceSmokeInterconnect = 0xffffffff00000092, - kForceMalfunction = 0xffffffff00000093, - kForceCOInterconnect = 0xffffffff00000094, - kForceLowBatteryWarning = 0xffffffff00000095, - kForceSmokeContaminationHigh = 0xffffffff00000096, - kForceSmokeContaminationLow = 0xffffffff00000097, - kForceSmokeSensitivityHigh = 0xffffffff00000098, - kForceSmokeSensitivityLow = 0xffffffff00000099, - kForceEndOfLife = 0xffffffff0000009a, - kForceSilence = 0xffffffff0000009b, - kForceSmokeCritical = 0xffffffff0000009c, - kForceCOCritical = 0xffffffff0000009d, - kForceLowBatteryCritical = 0xffffffff0000009e, - // Clear alarm commands - kClearSmoke = 0xffffffff000000a0, - kClearCO = 0xffffffff000000a1, - kClearSmokeInterconnect = 0xffffffff000000a2, - kClearMalfunction = 0xffffffff000000a3, - kClearCOInterconnect = 0xffffffff000000a4, - kClearBatteryLevelLow = 0xffffffff000000a5, - kClearContamination = 0xffffffff000000a6, - kClearSensitivity = 0xffffffff000000a8, - kClearEndOfLife = 0xffffffff000000aa, - kClearSilence = 0xffffffff000000ab -}; - -class SmokeCOTestEventTriggerDelegate : public TestEventTriggerDelegate -{ -public: - explicit SmokeCOTestEventTriggerDelegate(const ByteSpan & enableKey, TestEventTriggerDelegate * otherDelegate) : - mEnableKey(enableKey), mOtherDelegate(otherDelegate) - {} - - bool DoesEnableKeyMatch(const ByteSpan & enableKey) const override; - 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 HandleSmokeCOTestEventTrigger(uint64_t eventTrigger); diff --git a/src/app/clusters/smoke-co-alarm-server/SmokeCOTestEventTriggerHandler.h b/src/app/clusters/smoke-co-alarm-server/SmokeCOTestEventTriggerHandler.h new file mode 100644 index 0000000000..a64ab5fc5d --- /dev/null +++ b/src/app/clusters/smoke-co-alarm-server/SmokeCOTestEventTriggerHandler.h @@ -0,0 +1,78 @@ +/* + * + * 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. + */ + +#pragma once + +#include +#include + +/** + * @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 HandleSmokeCOTestEventTrigger(uint64_t eventTrigger); + +namespace chip { + +enum class SmokeCOTrigger : uint64_t +{ + // Force alarm commands + kForceSmokeWarning = 0x005c'0000'00000090, + kForceCOWarning = 0x005c'0000'00000091, + kForceSmokeInterconnect = 0x005c'0000'00000092, + kForceMalfunction = 0x005c'0000'00000093, + kForceCOInterconnect = 0x005c'0000'00000094, + kForceLowBatteryWarning = 0x005c'0000'00000095, + kForceSmokeContaminationHigh = 0x005c'0000'00000096, + kForceSmokeContaminationLow = 0x005c'0000'00000097, + kForceSmokeSensitivityHigh = 0x005c'0000'00000098, + kForceSmokeSensitivityLow = 0x005c'0000'00000099, + kForceEndOfLife = 0x005c'0000'0000009a, + kForceSilence = 0x005c'0000'0000009b, + kForceSmokeCritical = 0x005c'0000'0000009c, + kForceCOCritical = 0x005c'0000'0000009d, + kForceLowBatteryCritical = 0x005c'0000'0000009e, + // Clear alarm commands + kClearSmoke = 0x005c'0000'000000a0, + kClearCO = 0x005c'0000'000000a1, + kClearSmokeInterconnect = 0x005c'0000'000000a2, + kClearMalfunction = 0x005c'0000'000000a3, + kClearCOInterconnect = 0x005c'0000'000000a4, + kClearBatteryLevelLow = 0x005c'0000'000000a5, + kClearContamination = 0x005c'0000'000000a6, + kClearSensitivity = 0x005c'0000'000000a8, + kClearEndOfLife = 0x005c'0000'000000aa, + kClearSilence = 0x005c'0000'000000ab +}; + +class SmokeCOTestEventTriggerHandler : public TestEventTriggerHandler +{ +public: + SmokeCOTestEventTriggerHandler() = default; + CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override + { + return HandleSmokeCOTestEventTrigger(eventTrigger) ? CHIP_NO_ERROR : CHIP_ERROR_INVALID_ARGUMENT; + } +}; + +} // namespace chip diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index 26c0cb0693..05ba7570c7 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -149,6 +149,7 @@ chip_test_suite_using_nltest("tests") { "TestReportingEngine.cpp", "TestStatusIB.cpp", "TestStatusResponseMessage.cpp", + "TestTestEventTriggerDelegate.cpp", "TestTimeSyncDataProvider.cpp", "TestTimedHandler.cpp", "TestWriteInteraction.cpp", diff --git a/src/app/tests/TestTestEventTriggerDelegate.cpp b/src/app/tests/TestTestEventTriggerDelegate.cpp new file mode 100644 index 0000000000..cd7f3ae9cd --- /dev/null +++ b/src/app/tests/TestTestEventTriggerDelegate.cpp @@ -0,0 +1,192 @@ +/* + * + * Copyright (c) 2024 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 + +#include +#include +#include +#include + +using namespace chip; + +namespace { + +class TestEventHandler : public TestEventTriggerHandler +{ +public: + TestEventHandler() = delete; + + explicit TestEventHandler(uint64_t supportedEventTriggerValue) : mSupportedEventTriggerValue(supportedEventTriggerValue) {} + + CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override + { + if (eventTrigger == mSupportedEventTriggerValue) + { + ++mCount; + return CHIP_NO_ERROR; + } + return CHIP_ERROR_INVALID_ARGUMENT; + } + + int GetCount() const { return mCount; } + void ClearCount() { mCount = 0; } + +private: + uint64_t mSupportedEventTriggerValue; + int mCount = 0; +}; + +class TestEventDelegate : public TestEventTriggerDelegate +{ +public: + explicit TestEventDelegate(const ByteSpan & enableKey) : mEnableKey(enableKey) {} + + bool DoesEnableKeyMatch(const ByteSpan & enableKey) const override + { + return !mEnableKey.empty() && mEnableKey.data_equal(enableKey); + } + +private: + ByteSpan mEnableKey; +}; + +void TestKeyChecking(nlTestSuite * aSuite, void * aContext) +{ + const uint8_t kTestKey[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + const uint8_t kBadKey[16] = { 255, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + const uint8_t kDiffLenBadKey[17] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; + TestEventDelegate delegate{ ByteSpan{ kTestKey } }; + + NL_TEST_ASSERT(aSuite, delegate.DoesEnableKeyMatch(ByteSpan{ kTestKey }) == true); + NL_TEST_ASSERT(aSuite, delegate.DoesEnableKeyMatch(ByteSpan{ kBadKey }) == false); + NL_TEST_ASSERT(aSuite, delegate.DoesEnableKeyMatch(ByteSpan{ kDiffLenBadKey }) == false); + NL_TEST_ASSERT(aSuite, delegate.DoesEnableKeyMatch(ByteSpan{}) == false); +} + +void TestHandlerManagement(nlTestSuite * aSuite, void * aContext) +{ + const uint8_t kTestKey[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + + TestEventDelegate delegate{ ByteSpan{ kTestKey } }; + + TestEventHandler event1Handler{ 1 }; + TestEventHandler event2Handler{ 2 }; + + // Add 2, check 2 works 1 doesn't. + NL_TEST_ASSERT(aSuite, delegate.HandleEventTriggers(1) != CHIP_NO_ERROR); + + NL_TEST_ASSERT(aSuite, delegate.AddHandler(&event2Handler) == CHIP_NO_ERROR); + NL_TEST_ASSERT(aSuite, delegate.AddHandler(&event2Handler) != CHIP_NO_ERROR); + + NL_TEST_ASSERT(aSuite, delegate.HandleEventTriggers(2) == CHIP_NO_ERROR); + NL_TEST_ASSERT(aSuite, delegate.HandleEventTriggers(1) != CHIP_NO_ERROR); + NL_TEST_ASSERT(aSuite, delegate.HandleEventTriggers(2) == CHIP_NO_ERROR); + + NL_TEST_ASSERT(aSuite, event1Handler.GetCount() == 0); + NL_TEST_ASSERT(aSuite, event2Handler.GetCount() == 2); + + event1Handler.ClearCount(); + event2Handler.ClearCount(); + + // Add 1, check 1 and 2 work. + NL_TEST_ASSERT(aSuite, delegate.AddHandler(&event1Handler) == CHIP_NO_ERROR); + NL_TEST_ASSERT(aSuite, delegate.AddHandler(&event1Handler) != CHIP_NO_ERROR); + + NL_TEST_ASSERT(aSuite, delegate.HandleEventTriggers(1) == CHIP_NO_ERROR); + NL_TEST_ASSERT(aSuite, delegate.HandleEventTriggers(2) == CHIP_NO_ERROR); + NL_TEST_ASSERT(aSuite, delegate.HandleEventTriggers(1) == CHIP_NO_ERROR); + + NL_TEST_ASSERT(aSuite, event1Handler.GetCount() == 2); + NL_TEST_ASSERT(aSuite, event2Handler.GetCount() == 1); + + event1Handler.ClearCount(); + event2Handler.ClearCount(); + + // Remove 2, check 1 works. + delegate.RemoveHandler(&event2Handler); + + NL_TEST_ASSERT(aSuite, delegate.HandleEventTriggers(1) == CHIP_NO_ERROR); + NL_TEST_ASSERT(aSuite, delegate.HandleEventTriggers(2) != CHIP_NO_ERROR); + + NL_TEST_ASSERT(aSuite, event1Handler.GetCount() == 1); + NL_TEST_ASSERT(aSuite, event2Handler.GetCount() == 0); + + // Remove again, should be NO-OP. + delegate.RemoveHandler(&event2Handler); + NL_TEST_ASSERT(aSuite, delegate.HandleEventTriggers(2) != CHIP_NO_ERROR); + NL_TEST_ASSERT(aSuite, event2Handler.GetCount() == 0); + + event1Handler.ClearCount(); + event2Handler.ClearCount(); + + // Add 2 again, check 1 and 2 work. + NL_TEST_ASSERT(aSuite, delegate.AddHandler(&event2Handler) == CHIP_NO_ERROR); + NL_TEST_ASSERT(aSuite, delegate.AddHandler(&event2Handler) != CHIP_NO_ERROR); + + NL_TEST_ASSERT(aSuite, delegate.HandleEventTriggers(1) == CHIP_NO_ERROR); + NL_TEST_ASSERT(aSuite, delegate.HandleEventTriggers(2) == CHIP_NO_ERROR); + + NL_TEST_ASSERT(aSuite, event1Handler.GetCount() == 1); + NL_TEST_ASSERT(aSuite, event2Handler.GetCount() == 1); + + event1Handler.ClearCount(); + event2Handler.ClearCount(); + + // Remove all handlers, check neither works. + delegate.ClearAllHandlers(); + + NL_TEST_ASSERT(aSuite, delegate.HandleEventTriggers(1) != CHIP_NO_ERROR); + NL_TEST_ASSERT(aSuite, delegate.HandleEventTriggers(2) != CHIP_NO_ERROR); + + NL_TEST_ASSERT(aSuite, event1Handler.GetCount() == 0); + NL_TEST_ASSERT(aSuite, event2Handler.GetCount() == 0); + + // Add a handler at the end: having it remaining should not cause crashes/leaks. + NL_TEST_ASSERT(aSuite, delegate.AddHandler(&event2Handler) == CHIP_NO_ERROR); +} + +int TestSetup(void * inContext) +{ + return SUCCESS; +} + +int TestTeardown(void * inContext) +{ + return SUCCESS; +} + +} // namespace + +int TestTestEventTriggerDelegate() +{ + static nlTest sTests[] = { NL_TEST_DEF("TestKeyChecking", TestKeyChecking), + NL_TEST_DEF("TestHandlerManagement", TestHandlerManagement), NL_TEST_SENTINEL() }; + + nlTestSuite theSuite = { + "TestTestEventTriggerDelegate", + &sTests[0], + TestSetup, + TestTeardown, + }; + + nlTestRunner(&theSuite, nullptr); + return (nlTestRunnerStats(&theSuite)); +} + +CHIP_REGISTER_TEST_SUITE(TestTestEventTriggerDelegate) diff --git a/src/app/tests/suites/certification/Test_TC_SMOKECO_2_2.yaml b/src/app/tests/suites/certification/Test_TC_SMOKECO_2_2.yaml index 8a38c7cd50..d4d4a72911 100644 --- a/src/app/tests/suites/certification/Test_TC_SMOKECO_2_2.yaml +++ b/src/app/tests/suites/certification/Test_TC_SMOKECO_2_2.yaml @@ -29,13 +29,13 @@ config: defaultValue: "hex:00112233445566778899aabbccddeeff" TEST_EVENT_TRIGGER_WARNING_SMOKE_ALARM: type: int64u - defaultValue: "0xffffffff00000090" + defaultValue: "0x005c000000000090" TEST_EVENT_TRIGGER_CRITICAL_SMOKE_ALARM: type: int64u - defaultValue: "0xffffffff0000009c" + defaultValue: "0x005c00000000009c" TEST_EVENT_TRIGGER_SMOKE_ALARM_CLEAR: type: int64u - defaultValue: "0xffffffff000000a0" + defaultValue: "0x005c0000000000a0" tests: - label: "Step 1: Commission DUT to TH" diff --git a/src/app/tests/suites/certification/Test_TC_SMOKECO_2_5.yaml b/src/app/tests/suites/certification/Test_TC_SMOKECO_2_5.yaml index 8fb355e1ed..a2c6ebdceb 100644 --- a/src/app/tests/suites/certification/Test_TC_SMOKECO_2_5.yaml +++ b/src/app/tests/suites/certification/Test_TC_SMOKECO_2_5.yaml @@ -29,58 +29,58 @@ config: defaultValue: "hex:00112233445566778899aabbccddeeff" TEST_EVENT_TRIGGER_WARNING_SMOKE_ALARM: type: int64u - defaultValue: "0xffffffff00000090" + defaultValue: "0x005c000000000090" TEST_EVENT_TRIGGER_CRITICAL_SMOKE_ALARM: type: int64u - defaultValue: "0xffffffff0000009c" + defaultValue: "0x005c00000000009c" TEST_EVENT_TRIGGER_SMOKE_ALARM_CLEAR: type: int64u - defaultValue: "0xffffffff000000a0" + defaultValue: "0x005c0000000000a0" TEST_EVENT_TRIGGER_WARNING_CO_ALARM: type: int64u - defaultValue: "0xffffffff00000091" + defaultValue: "0x005c000000000091" TEST_EVENT_TRIGGER_CRITICAL_CO_ALARM: type: int64u - defaultValue: "0xffffffff0000009d" + defaultValue: "0x005c00000000009d" TEST_EVENT_TRIGGER_CO_ALARM_CLEAR: type: int64u - defaultValue: "0xffffffff000000a1" + defaultValue: "0x005c0000000000a1" TEST_EVENT_TRIGGER_DEVICE_MUTED: type: int64u - defaultValue: "0xffffffff0000009b" + defaultValue: "0x005c00000000009b" TEST_EVENT_TRIGGER_DEVICE_MUTED_CLEAR: type: int64u - defaultValue: "0xffffffff000000ab" + defaultValue: "0x005c0000000000ab" TEST_EVENT_TRIGGER_INTERCONNECT_SMOKE_ALARM: type: int64u - defaultValue: "0xffffffff00000092" + defaultValue: "0x005c000000000092" TEST_EVENT_TRIGGER_INTERCONNECT_SMOKE_ALARM_CLEAR: type: int64u - defaultValue: "0xffffffff000000a2" + defaultValue: "0x005c0000000000a2" TEST_EVENT_TRIGGER_INTERCONNECT_CO_ALARM: type: int64u - defaultValue: "0xffffffff00000094" + defaultValue: "0x005c000000000094" TEST_EVENT_TRIGGER_INTERCONNECT_CO_ALARM_CLEAR: type: int64u - defaultValue: "0xffffffff000000a4" + defaultValue: "0x005c0000000000a4" TEST_EVENT_TRIGGER_CONTAMINATION_STATE_HIGH: type: int64u - defaultValue: "0xffffffff00000096" + defaultValue: "0x005c000000000096" TEST_EVENT_TRIGGER_CONTAMINATION_STATE_LOW: type: int64u - defaultValue: "0xffffffff00000097" + defaultValue: "0x005c000000000097" TTEST_EVENT_TRIGGER_CONTAMINATION_STATE_CLEAR: type: int64u - defaultValue: "0xffffffff000000a6" + defaultValue: "0x005c0000000000a6" TEST_EVENT_TRIGGER_SENSITIVITY_LEVEL_HIGH: type: int64u - defaultValue: "0xffffffff00000098" + defaultValue: "0x005c000000000098" TEST_EVENT_TRIGGER_SENSITIVITY_LEVEL_LOW: type: int64u - defaultValue: "0xffffffff00000099" + defaultValue: "0x005c000000000099" TTEST_EVENT_TRIGGER_SENSITIVITY_LEVEL_CLEAR: type: int64u - defaultValue: "0xffffffff000000a8" + defaultValue: "0x005c0000000000a8" tests: - label: "Step 1: Commission DUT to TH" diff --git a/src/app/tests/suites/certification/Test_TC_SMOKECO_2_6.yaml b/src/app/tests/suites/certification/Test_TC_SMOKECO_2_6.yaml index 00ecd8b85a..288933602d 100644 --- a/src/app/tests/suites/certification/Test_TC_SMOKECO_2_6.yaml +++ b/src/app/tests/suites/certification/Test_TC_SMOKECO_2_6.yaml @@ -29,34 +29,34 @@ config: defaultValue: "hex:00112233445566778899aabbccddeeff" TEST_EVENT_TRIGGER_WARNING_SMOKE_ALARM: type: int64u - defaultValue: "0xffffffff00000090" + defaultValue: "0x005c000000000090" TEST_EVENT_TRIGGER_SMOKE_ALARM_CLEAR: type: int64u - defaultValue: "0xffffffff000000a0" + defaultValue: "0x005c0000000000a0" TEST_EVENT_TRIGGER_WARNING_CO_ALARM: type: int64u - defaultValue: "0xffffffff00000091" + defaultValue: "0x005c000000000091" TEST_EVENT_TRIGGER_CO_ALARM_CLEAR: type: int64u - defaultValue: "0xffffffff000000a1" + defaultValue: "0x005c0000000000a1" TEST_EVENT_TRIGGER_WARNING_BATTERY_ALERT: type: int64u - defaultValue: "0xffffffff00000095" + defaultValue: "0x005c000000000095" TEST_EVENT_TRIGGER_BATTERY_ALERT_CLEAR: type: int64u - defaultValue: "0xffffffff000000a5" + defaultValue: "0x005c0000000000a5" TEST_EVENT_TRIGGER_INTERCONNECT_SMOKE_ALARM: type: int64u - defaultValue: "0xffffffff00000092" + defaultValue: "0x005c000000000092" TEST_EVENT_TRIGGER_INTERCONNECT_SMOKE_ALARM_CLEAR: type: int64u - defaultValue: "0xffffffff000000a2" + defaultValue: "0x005c0000000000a2" TEST_EVENT_TRIGGER_INTERCONNECT_CO_ALARM: type: int64u - defaultValue: "0xffffffff00000094" + defaultValue: "0x005c000000000094" TEST_EVENT_TRIGGER_INTERCONNECT_CO_ALARM_CLEAR: type: int64u - defaultValue: "0xffffffff000000a4" + defaultValue: "0x005c0000000000a4" HIEST_PRI_ALARM: type: int8u defaultValue: 1 diff --git a/src/lib/support/IntrusiveList.h b/src/lib/support/IntrusiveList.h index 8bd0afb0da..961113e8d1 100644 --- a/src/lib/support/IntrusiveList.h +++ b/src/lib/support/IntrusiveList.h @@ -439,6 +439,14 @@ class IntrusiveList : public IntrusiveListBase void Remove(T * value) { IntrusiveListBase::Remove(Hook::ToNode(value)); } void Replace(T * original, T * replacement) { IntrusiveListBase::Replace(Hook::ToNode(original), Hook::ToNode(replacement)); } bool Contains(const T * value) const { return IntrusiveListBase::Contains(Hook::ToNode(value)); } + + void Clear() + { + while (begin() != end()) + { + Remove(&(*begin())); + } + } }; } // namespace chip diff --git a/src/lib/support/tests/TestIntrusiveList.cpp b/src/lib/support/tests/TestIntrusiveList.cpp index c961a643ed..7d611655bb 100644 --- a/src/lib/support/tests/TestIntrusiveList.cpp +++ b/src/lib/support/tests/TestIntrusiveList.cpp @@ -131,6 +131,35 @@ void TestContains(nlTestSuite * inSuite, void * inContext) list.Remove(&b); } +void TestClear(nlTestSuite * inSuite, void * inContext) +{ + ListNode a, b, c; + IntrusiveList list; + + NL_TEST_ASSERT(inSuite, !list.Contains(&a)); + NL_TEST_ASSERT(inSuite, !list.Contains(&b)); + NL_TEST_ASSERT(inSuite, !list.Contains(&c)); + + list.PushBack(&a); + list.PushFront(&c); + + NL_TEST_ASSERT(inSuite, list.Contains(&a)); + NL_TEST_ASSERT(inSuite, !list.Contains(&b)); + NL_TEST_ASSERT(inSuite, list.Contains(&c)); + + list.PushBack(&b); + + NL_TEST_ASSERT(inSuite, list.Contains(&a)); + NL_TEST_ASSERT(inSuite, list.Contains(&b)); + NL_TEST_ASSERT(inSuite, list.Contains(&c)); + + list.Clear(); + + NL_TEST_ASSERT(inSuite, !list.Contains(&a)); + NL_TEST_ASSERT(inSuite, !list.Contains(&b)); + NL_TEST_ASSERT(inSuite, !list.Contains(&c)); +} + void TestReplaceNode(nlTestSuite * inSuite, void * inContext) { ListNode a, b; @@ -242,6 +271,7 @@ static const nlTest sTests[] = { NL_TEST_DEF_FN(TestReplaceNode), // NL_TEST_DEF_FN(TestMoveList), // NL_TEST_DEF_FN(TestAutoUnlink), // + NL_TEST_DEF_FN(TestClear), // NL_TEST_SENTINEL(), // }; diff --git a/src/platform/nxp/k32w/k32w0/DefaultTestEventTriggerDelegate.cpp b/src/platform/nxp/k32w/k32w0/DefaultTestEventTriggerDelegate.cpp index 1a01acfdb6..69935bb024 100644 --- a/src/platform/nxp/k32w/k32w0/DefaultTestEventTriggerDelegate.cpp +++ b/src/platform/nxp/k32w/k32w0/DefaultTestEventTriggerDelegate.cpp @@ -28,15 +28,4 @@ bool DefaultTestEventTriggerDelegate::DoesEnableKeyMatch(const ByteSpan & enable return !mEnableKey.empty() && mEnableKey.data_equal(enableKey); } -CHIP_ERROR DefaultTestEventTriggerDelegate::HandleEventTrigger(uint64_t eventTrigger) -{ - if (eventTrigger == kQueryTrigger) - { - ChipLogProgress(DeviceLayer, "DefaultTestEventTriggerDelegate: event triggered"); - return CHIP_NO_ERROR; - } - - return CHIP_ERROR_INVALID_ARGUMENT; -} - } // namespace chip diff --git a/src/platform/nxp/k32w/k32w0/DefaultTestEventTriggerDelegate.h b/src/platform/nxp/k32w/k32w0/DefaultTestEventTriggerDelegate.h index cf71fb37d9..0bfd4c5b0f 100644 --- a/src/platform/nxp/k32w/k32w0/DefaultTestEventTriggerDelegate.h +++ b/src/platform/nxp/k32w/k32w0/DefaultTestEventTriggerDelegate.h @@ -25,12 +25,9 @@ namespace chip { class DefaultTestEventTriggerDelegate : public TestEventTriggerDelegate { public: - static constexpr uint64_t kQueryTrigger = 1234; - explicit DefaultTestEventTriggerDelegate(const ByteSpan & enableKey) : mEnableKey(enableKey) {} bool DoesEnableKeyMatch(const ByteSpan & enableKey) const override; - CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override; private: ByteSpan mEnableKey; diff --git a/src/platform/nxp/k32w/k32w1/DefaultTestEventTriggerDelegate.cpp b/src/platform/nxp/k32w/k32w1/DefaultTestEventTriggerDelegate.cpp index 1a01acfdb6..69935bb024 100644 --- a/src/platform/nxp/k32w/k32w1/DefaultTestEventTriggerDelegate.cpp +++ b/src/platform/nxp/k32w/k32w1/DefaultTestEventTriggerDelegate.cpp @@ -28,15 +28,4 @@ bool DefaultTestEventTriggerDelegate::DoesEnableKeyMatch(const ByteSpan & enable return !mEnableKey.empty() && mEnableKey.data_equal(enableKey); } -CHIP_ERROR DefaultTestEventTriggerDelegate::HandleEventTrigger(uint64_t eventTrigger) -{ - if (eventTrigger == kQueryTrigger) - { - ChipLogProgress(DeviceLayer, "DefaultTestEventTriggerDelegate: event triggered"); - return CHIP_NO_ERROR; - } - - return CHIP_ERROR_INVALID_ARGUMENT; -} - } // namespace chip diff --git a/src/platform/nxp/k32w/k32w1/DefaultTestEventTriggerDelegate.h b/src/platform/nxp/k32w/k32w1/DefaultTestEventTriggerDelegate.h index cf71fb37d9..0bfd4c5b0f 100644 --- a/src/platform/nxp/k32w/k32w1/DefaultTestEventTriggerDelegate.h +++ b/src/platform/nxp/k32w/k32w1/DefaultTestEventTriggerDelegate.h @@ -25,12 +25,9 @@ namespace chip { class DefaultTestEventTriggerDelegate : public TestEventTriggerDelegate { public: - static constexpr uint64_t kQueryTrigger = 1234; - explicit DefaultTestEventTriggerDelegate(const ByteSpan & enableKey) : mEnableKey(enableKey) {} bool DoesEnableKeyMatch(const ByteSpan & enableKey) const override; - CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override; private: ByteSpan mEnableKey; diff --git a/src/python_testing/TC_BOOLCFG_4_2.py b/src/python_testing/TC_BOOLCFG_4_2.py new file mode 100644 index 0000000000..c0a74a9c46 --- /dev/null +++ b/src/python_testing/TC_BOOLCFG_4_2.py @@ -0,0 +1,177 @@ +# +# 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. +# + +import logging + +import chip.clusters as Clusters +from chip.interaction_model import InteractionModelError, Status +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts + +sensorTrigger = 0x0080_0000_0000_0000 +sensorUntrigger = 0x0080_0000_0000_0001 + + +class TC_BOOLCFG_4_2(MatterBaseTest): + async def read_boolcfg_attribute_expect_success(self, endpoint, attribute): + cluster = Clusters.Objects.BooleanStateConfiguration + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + def desc_TC_BOOLCFG_4_2(self) -> str: + return "[TC-BOOLCFG-4.2] AlarmsActive attribute with DUT as Server" + + def steps_TC_BOOLCFG_4_2(self) -> list[TestStep]: + steps = [ + TestStep(1, "Commissioning, already done", is_commissioning=True), + TestStep("2a", "Read FeatureMap attribute"), + TestStep("2b", "Read AttributeList attribute"), + TestStep("3a", "Create variable named enabledAlarms"), + TestStep("3b", "If VIS is supported, set bit 0 to 1"), + TestStep("3c", "If AUD is supported, set bit 1 to 1"), + TestStep("3d", "Set AlarmsEnabled attribute to value of enabledAlarms using EnableDisableAlarm command"), + TestStep(4, "Send TestEventTrigger with SensorTrigger event"), + TestStep(5, "Read AlarmsActive attribute"), + TestStep("6a", "Verify VIS alarm is active, if supported"), + TestStep("6b", "Verify VIS alarm is not active, if not supported"), + TestStep("7a", "Verify AUD alarm is active, if supported"), + TestStep("7b", "Verify AUD alarm is not active, if not supported"), + TestStep(8, "Send TestEventTrigger with SensorUntrigger event"), + TestStep(9, "Read AlarmsActive attribute"), + ] + return steps + + def pics_TC_BOOLCFG_4_2(self) -> list[str]: + pics = [ + "BOOLCFG.S", + ] + return pics + + @async_test_body + async def test_TC_BOOLCFG_4_2(self): + + asserts.assert_true('PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY' in self.matter_test_config.global_test_params, + "PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY must be included on the command line in " + "the --hex-arg flag as PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY:, " + "e.g. --hex-arg PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY:000102030405060708090a0b0c0d0e0f") + + endpoint = self.user_params.get("endpoint", 1) + enableKey = self.matter_test_config.global_test_params['PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY'] + + self.step(1) + attributes = Clusters.BooleanStateConfiguration.Attributes + + self.step("2a") + feature_map = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.FeatureMap) + + is_vis_feature_supported = feature_map & Clusters.BooleanStateConfiguration.Bitmaps.Feature.kVisual + is_aud_feature_supported = feature_map & Clusters.BooleanStateConfiguration.Bitmaps.Feature.kAudible + + self.step("2b") + attribute_list = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AttributeList) + + self.step("3a") + enabledAlarms = 0 + + self.step("3b") + if attributes.AlarmsEnabled.attribute_id in attribute_list and is_vis_feature_supported: + enabledAlarms |= Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kVisual + else: + logging.info("Test step skipped") + + self.step("3c") + if attributes.AlarmsEnabled.attribute_id in attribute_list and is_aud_feature_supported: + enabledAlarms |= Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kAudible + else: + logging.info("Test step skipped") + + self.step("3d") + if attributes.AlarmsEnabled.attribute_id in attribute_list: + try: + await self.send_single_cmd(cmd=Clusters.Objects.BooleanStateConfiguration.Commands.EnableDisableAlarm(alarmsToEnableDisable=enabledAlarms), endpoint=endpoint) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + self.step(4) + if is_vis_feature_supported or is_aud_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=enableKey, eventTrigger=sensorTrigger), endpoint=0) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + self.step(5) + activeAlarms = 0 + + if is_vis_feature_supported or is_aud_feature_supported: + activeAlarms = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsActive) + asserts.assert_not_equal(activeAlarms, 0, "AlarmsActive is 0") + else: + logging.info("Test step skipped") + + self.step("6a") + if is_vis_feature_supported: + asserts.assert_not_equal( + (activeAlarms & Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kVisual), 0, "Bit 0 in AlarmsActive is not 1") + else: + logging.info("Test step skipped") + + self.step("6b") + if not is_vis_feature_supported: + asserts.assert_equal((activeAlarms & Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kVisual), + 0, "Bit 0 in AlarmsActive is not 0") + else: + logging.info("Test step skipped") + + self.step("7a") + if is_aud_feature_supported: + asserts.assert_not_equal( + (activeAlarms & Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kAudible), 0, "Bit 1 in AlarmsActive is not 1") + else: + logging.info("Test step skipped") + + self.step("7b") + if not is_aud_feature_supported: + asserts.assert_equal((activeAlarms & Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kAudible), + 0, "Bit 1 in AlarmsActive is not 0") + else: + logging.info("Test step skipped") + + self.step(8) + if is_vis_feature_supported or is_aud_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=enableKey, eventTrigger=sensorUntrigger), endpoint=0) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + self.step(9) + if is_vis_feature_supported or is_aud_feature_supported: + activeAlarms = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsActive) + asserts.assert_equal(activeAlarms, 0, "AlarmsActive is not 0") + else: + logging.info("Test step skipped") + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_BOOLCFG_4_3.py b/src/python_testing/TC_BOOLCFG_4_3.py new file mode 100644 index 0000000000..a9d28e5bb6 --- /dev/null +++ b/src/python_testing/TC_BOOLCFG_4_3.py @@ -0,0 +1,286 @@ +# +# 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. +# + +import logging + +import chip.clusters as Clusters +from chip.interaction_model import InteractionModelError, Status +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts + +sensorTrigger = 0x0080_0000_0000_0000 +sensorUntrigger = 0x0080_0000_0000_0001 + + +class TC_BOOLCFG_4_3(MatterBaseTest): + async def read_boolcfg_attribute_expect_success(self, endpoint, attribute): + cluster = Clusters.Objects.BooleanStateConfiguration + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + def desc_TC_BOOLCFG_4_3(self) -> str: + return "[TC-BOOLCFG-4.3] AlarmsEnabled functionality for inactive alarms with DUT as Server" + + def steps_TC_BOOLCFG_4_3(self) -> list[TestStep]: + steps = [ + TestStep(1, "Commissioning, already done", is_commissioning=True), + TestStep("2a", "Read FeatureMap attribute"), + TestStep("2b", "Read AttributeList attribute"), + TestStep(3, "Verify AlarmsEnabled is supported"), + TestStep(4, "Create enabledAlarms and set to 0"), + TestStep("5a", "Enable VIS alarm in enabledAlarms"), + TestStep("5b", "Set AlarmsEnabled attribute to value of enabledAlarms using AlarmsToEnableDisable command"), + TestStep(6, "Send TestEventTrigger with SensorTrigger event"), + TestStep(7, "Read AlarmsActive attribute"), + TestStep(8, "Send TestEventTrigger with SensorUntrigger event"), + TestStep(9, "Read AlarmsActive attribute"), + TestStep(10, "Set enabledAlarms to 0"), + TestStep(11, "Set AlarmsEnabled attribute to value of enabledAlarms using AlarmsToEnableDisable command"), + TestStep(12, "Send TestEventTrigger with SensorTrigger event"), + TestStep(13, "Read AlarmsActive attribute"), + TestStep(14, "Send TestEventTrigger with SensorUntrigger event"), + TestStep("15a", "Enable AUD alarm in enabledAlarms"), + TestStep("15b", "Set AlarmsEnabled attribute to value of enabledAlarms using AlarmsToEnableDisable command"), + TestStep(16, "Send TestEventTrigger with SensorTrigger event"), + TestStep(17, "Read AlarmsActive attribute"), + TestStep(18, "Send TestEventTrigger with SensorUntrigger event"), + TestStep(19, "Read AlarmsActive attribute"), + TestStep(20, "Set enabledAlarms to 0"), + TestStep(21, "Set AlarmsEnabled attribute to value of enabledAlarms using AlarmsToEnableDisable command"), + TestStep(22, "Send TestEventTrigger with SensorTrigger event"), + TestStep(23, "Read AlarmsActive attribute"), + TestStep(24, "Send TestEventTrigger with SensorUntrigger event"), + ] + return steps + + def pics_TC_BOOLCFG_4_3(self) -> list[str]: + pics = [ + "BOOLCFG.S", + ] + return pics + + @async_test_body + async def test_TC_BOOLCFG_4_3(self): + + asserts.assert_true('PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY' in self.matter_test_config.global_test_params, + "PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY must be included on the command line in " + "the --hex-arg flag as PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY:, " + "e.g. --hex-arg PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY:000102030405060708090a0b0c0d0e0f") + + endpoint = self.user_params.get("endpoint", 1) + enableKey = self.matter_test_config.global_test_params['PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY'] + + self.step(1) + attributes = Clusters.BooleanStateConfiguration.Attributes + + self.step("2a") + feature_map = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.FeatureMap) + + is_vis_feature_supported = feature_map & Clusters.BooleanStateConfiguration.Bitmaps.Feature.kVisual + is_aud_feature_supported = feature_map & Clusters.BooleanStateConfiguration.Bitmaps.Feature.kAudible + + self.step("2b") + attribute_list = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AttributeList) + + self.step(3) + if attributes.AlarmsEnabled.attribute_id not in attribute_list: + logging.info("AlarmsEnabled not supported skipping test case") + + # Skipping all remaining steps + for step in self.get_test_steps(self.current_test_info.name)[self.current_step_index:]: + self.step(step.test_plan_number) + logging.info("Test step skipped") + + return + else: + logging.info("Test step skipped") + + self.step(4) + enabledAlarms = 0 + + self.step("5a") + if is_vis_feature_supported: + enabledAlarms |= Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kVisual + else: + logging.info("Test step skipped") + + self.step("5b") + try: + await self.send_single_cmd(cmd=Clusters.Objects.BooleanStateConfiguration.Commands.EnableDisableAlarm(alarmsToEnableDisable=enabledAlarms), endpoint=endpoint) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + + self.step(6) + if is_vis_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=enableKey, eventTrigger=sensorTrigger), endpoint=0) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + self.step(7) + if is_vis_feature_supported: + activeAlarms = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsActive) + asserts.assert_equal(activeAlarms, Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kVisual, + "AlarmsActive does not match expected value") + else: + logging.info("Test step skipped") + + self.step(8) + if is_vis_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=enableKey, eventTrigger=sensorUntrigger), endpoint=0) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + self.step(9) + if is_vis_feature_supported: + activeAlarms = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsActive) + asserts.assert_equal(activeAlarms, 0, "AlarmsActive does not match expected value") + else: + logging.info("Test step skipped") + + self.step(10) + enabledAlarms = 0 + + self.step(11) + try: + await self.send_single_cmd(cmd=Clusters.Objects.BooleanStateConfiguration.Commands.EnableDisableAlarm(alarmsToEnableDisable=enabledAlarms), endpoint=endpoint) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + + self.step(12) + if is_vis_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=enableKey, eventTrigger=sensorTrigger), endpoint=0) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + self.step(13) + if is_vis_feature_supported: + activeAlarms = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsActive) + asserts.assert_equal(activeAlarms, 0, "AlarmsActive does not match expected value") + else: + logging.info("Test step skipped") + + self.step(14) + if is_vis_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=enableKey, eventTrigger=sensorUntrigger), endpoint=0) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + self.step("15a") + if is_aud_feature_supported: + enabledAlarms |= Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kAudible + else: + logging.info("Test step skipped") + + self.step("15b") + try: + await self.send_single_cmd(cmd=Clusters.Objects.BooleanStateConfiguration.Commands.EnableDisableAlarm(alarmsToEnableDisable=enabledAlarms), endpoint=endpoint) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + + self.step(16) + if is_aud_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=enableKey, eventTrigger=sensorTrigger), endpoint=0) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + self.step(17) + if is_aud_feature_supported: + activeAlarms = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsActive) + asserts.assert_equal(activeAlarms, Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kAudible, + "AlarmsActive does not match expected value") + else: + logging.info("Test step skipped") + + self.step(18) + if is_aud_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=enableKey, eventTrigger=sensorUntrigger), endpoint=0) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + self.step(19) + if is_aud_feature_supported: + activeAlarms = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsActive) + asserts.assert_equal(activeAlarms, 0, "AlarmsActive does not match expected value") + else: + logging.info("Test step skipped") + + self.step(20) + enabledAlarms = 0 + + self.step(21) + try: + await self.send_single_cmd(cmd=Clusters.Objects.BooleanStateConfiguration.Commands.EnableDisableAlarm(alarmsToEnableDisable=enabledAlarms), endpoint=endpoint) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + + self.step(22) + if is_aud_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=enableKey, eventTrigger=sensorTrigger), endpoint=0) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + self.step(23) + if is_aud_feature_supported: + activeAlarms = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsActive) + asserts.assert_equal(activeAlarms, 0, "AlarmsActive does not match expected value") + else: + logging.info("Test step skipped") + + self.step(24) + if is_aud_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=enableKey, eventTrigger=sensorUntrigger), endpoint=0) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_BOOLCFG_4_4.py b/src/python_testing/TC_BOOLCFG_4_4.py new file mode 100644 index 0000000000..05159116a9 --- /dev/null +++ b/src/python_testing/TC_BOOLCFG_4_4.py @@ -0,0 +1,208 @@ +# +# 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. +# + +import logging + +import chip.clusters as Clusters +from chip.interaction_model import InteractionModelError, Status +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts + +sensorTrigger = 0x0080_0000_0000_0000 +sensorUntrigger = 0x0080_0000_0000_0001 + + +class TC_BOOLCFG_4_4(MatterBaseTest): + async def read_boolcfg_attribute_expect_success(self, endpoint, attribute): + cluster = Clusters.Objects.BooleanStateConfiguration + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + def desc_TC_BOOLCFG_4_4(self) -> str: + return "[TC-BOOLCFG-4.4] AlarmsEnabled functionality for active alarms with DUT as Server" + + def steps_TC_BOOLCFG_4_4(self) -> list[TestStep]: + steps = [ + TestStep(1, "Commissioning, already done", is_commissioning=True), + TestStep("2a", "Read FeatureMap attribute"), + TestStep("2b", "Read AttributeList attribute"), + TestStep(3, "Verify AlarmsEnabled is supported"), + TestStep(4, "Create enabledAlarms and set to 0"), + TestStep("5a", "Enable VIS alarm in enabledAlarms"), + TestStep("5b", "Enable AUD alarm in enabledAlarms"), + TestStep("5c", "Set AlarmsEnabled attribute to value of enabledAlarms using AlarmsToEnableDisable command"), + TestStep(6, "Send TestEventTrigger with SensorTrigger event"), + TestStep(7, "Read AlarmsActive attribute"), + TestStep(8, "Verify VIS alarm is active"), + TestStep("9a", "Disable VIS alarm in enabledAlarms"), + TestStep("9b", "Set AlarmsEnabled attribute to value of enabledAlarms using AlarmsToEnableDisable command"), + TestStep(10, "Read AlarmsActive attribute"), + TestStep(11, "Verify AUD alarm is active"), + TestStep("12a", "Disable VIS alarm in enabledAlarms"), + TestStep("12b", "Set AlarmsEnabled attribute to value of enabledAlarms using AlarmsToEnableDisable command"), + TestStep(13, "Read AlarmsActive attribute"), + TestStep(14, "Send TestEventTrigger with SensorUntrigger event"), + ] + return steps + + def pics_TC_BOOLCFG_4_4(self) -> list[str]: + pics = [ + "BOOLCFG.S", + ] + return pics + + @async_test_body + async def test_TC_BOOLCFG_4_4(self): + + asserts.assert_true('PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY' in self.matter_test_config.global_test_params, + "PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY must be included on the command line in " + "the --hex-arg flag as PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY:, " + "e.g. --hex-arg PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY:000102030405060708090a0b0c0d0e0f") + + endpoint = self.user_params.get("endpoint", 1) + enableKey = self.matter_test_config.global_test_params['PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY'] + + self.step(1) + attributes = Clusters.BooleanStateConfiguration.Attributes + + self.step("2a") + feature_map = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.FeatureMap) + + is_vis_feature_supported = feature_map & Clusters.BooleanStateConfiguration.Bitmaps.Feature.kVisual + is_aud_feature_supported = feature_map & Clusters.BooleanStateConfiguration.Bitmaps.Feature.kAudible + + self.step("2b") + attribute_list = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AttributeList) + + self.step(3) + if attributes.AlarmsEnabled.attribute_id not in attribute_list: + logging.info("AlarmsEnabled not supported skipping test case") + + # Skipping all remaining steps + for step in self.get_test_steps(self.current_test_info.name)[self.current_step_index:]: + self.step(step.test_plan_number) + logging.info("Test step skipped") + + return + else: + logging.info("Test step skipped") + + self.step(4) + enabledAlarms = 0 + + self.step("5a") + if is_vis_feature_supported: + enabledAlarms |= Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kVisual + else: + logging.info("Test step skipped") + + self.step("5b") + if is_aud_feature_supported: + enabledAlarms |= Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kAudible + else: + logging.info("Test step skipped") + + self.step("5c") + try: + await self.send_single_cmd(cmd=Clusters.Objects.BooleanStateConfiguration.Commands.EnableDisableAlarm(alarmsToEnableDisable=enabledAlarms), endpoint=endpoint) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + + self.step(6) + if is_vis_feature_supported or is_aud_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=enableKey, eventTrigger=sensorTrigger), endpoint=0) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + self.step(7) + activeAlarms = 0 + + if is_vis_feature_supported or is_aud_feature_supported: + activeAlarms = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsActive) + asserts.assert_not_equal(activeAlarms, 0, "AlarmsActive does not match expected value") + else: + logging.info("Test step skipped") + + self.step(8) + if is_vis_feature_supported: + asserts.assert_not_equal((activeAlarms & 0b01), 0, "Bit 0 in AlarmsActive is not 1") + else: + logging.info("Test step skipped") + + self.step("9a") + if is_vis_feature_supported: + enabledAlarms &= ~(Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kVisual) + else: + logging.info("Test step skipped") + + self.step("9b") + try: + await self.send_single_cmd(cmd=Clusters.Objects.BooleanStateConfiguration.Commands.EnableDisableAlarm(alarmsToEnableDisable=enabledAlarms), endpoint=endpoint) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + + self.step(10) + if is_vis_feature_supported: + activeAlarms = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsActive) + asserts.assert_equal((activeAlarms & 0b01), 0, "Bit 0 in AlarmsActive is not 0") + else: + logging.info("Test step skipped") + + self.step(11) + if is_aud_feature_supported: + asserts.assert_not_equal((activeAlarms & 0b10), 0, "Bit 1 in AlarmsActive is not 1") + else: + logging.info("Test step skipped") + + self.step("12a") + if is_aud_feature_supported: + enabledAlarms &= ~(Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kAudible) + else: + logging.info("Test step skipped") + + self.step("12b") + try: + await self.send_single_cmd(cmd=Clusters.Objects.BooleanStateConfiguration.Commands.EnableDisableAlarm(alarmsToEnableDisable=enabledAlarms), endpoint=endpoint) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + + self.step(13) + if is_aud_feature_supported: + activeAlarms = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsActive) + asserts.assert_equal((activeAlarms & 0b10), 0, "Bit 1 in AlarmsActive is not 0") + else: + logging.info("Test step skipped") + + self.step(14) + if is_vis_feature_supported or is_aud_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=enableKey, eventTrigger=sensorUntrigger), endpoint=0) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_BOOLCFG_5_1.py b/src/python_testing/TC_BOOLCFG_5_1.py new file mode 100644 index 0000000000..fd1e064678 --- /dev/null +++ b/src/python_testing/TC_BOOLCFG_5_1.py @@ -0,0 +1,178 @@ +# +# 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. +# + +import logging + +import chip.clusters as Clusters +from chip.interaction_model import InteractionModelError, Status +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts + +sensorTrigger = 0x0080_0000_0000_0000 +sensorUntrigger = 0x0080_0000_0000_0001 + + +class TC_BOOLCFG_5_1(MatterBaseTest): + async def read_boolcfg_attribute_expect_success(self, endpoint, attribute): + cluster = Clusters.Objects.BooleanStateConfiguration + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + def desc_TC_BOOLCFG_5_1(self) -> str: + return "[TC-BOOLCFG-5.1] SuppressAlarm functionality for inactive alarms with DUT as Server" + + def steps_TC_BOOLCFG_5_1(self) -> list[TestStep]: + steps = [ + TestStep(1, "Commissioning, already done", is_commissioning=True), + TestStep(2, "Read FeatureMap attribute"), + TestStep(3, "Verify SPRS feature is supported"), + TestStep(4, "Create enabledAlarms and set to 0"), + TestStep("5a", "Enable VIS alarm in enabledAlarms"), + TestStep("5b", "Enable AUD alarm in enabledAlarms"), + TestStep("5c", "Set AlarmsEnabled attribute to value of enabledAlarms using AlarmsToEnableDisable command"), + TestStep(6, "Send TestEventTrigger with SensorUntrigger event"), + TestStep("7a", "Suppress VIS alarm using SuppressAlarm command"), + TestStep("7b", "Suppress VIS alarm using SuppressAlarm command"), + TestStep(8, "Read AlarmsSuppressed attribute"), + TestStep("9a", "Suppress AUD alarm using SuppressAlarm command"), + TestStep("9b", "Suppress AUD alarm using SuppressAlarm command"), + TestStep(10, "Read AlarmsSuppressed attribute"), + ] + return steps + + def pics_TC_BOOLCFG_5_1(self) -> list[str]: + pics = [ + "BOOLCFG.S", + ] + return pics + + @async_test_body + async def test_TC_BOOLCFG_5_1(self): + + asserts.assert_true('PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY' in self.matter_test_config.global_test_params, + "PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY must be included on the command line in " + "the --hex-arg flag as PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY:, " + "e.g. --hex-arg PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY:000102030405060708090a0b0c0d0e0f") + + endpoint = self.user_params.get("endpoint", 1) + enableKey = self.matter_test_config.global_test_params['PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY'] + + self.step(1) + attributes = Clusters.BooleanStateConfiguration.Attributes + + self.step(2) + feature_map = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.FeatureMap) + + is_sprs_feature_supported = feature_map & Clusters.BooleanStateConfiguration.Bitmaps.Feature.kAlarmSuppress + is_vis_feature_supported = feature_map & Clusters.BooleanStateConfiguration.Bitmaps.Feature.kVisual + is_aud_feature_supported = feature_map & Clusters.BooleanStateConfiguration.Bitmaps.Feature.kAudible + + self.step(3) + if not is_sprs_feature_supported: + logging.info("AlarmSuppress feature not supported skipping test case") + + # Skipping all remaining steps + for step in self.get_test_steps(self.current_test_info.name)[self.current_step_index:]: + self.step(step.test_plan_number) + logging.info("Test step skipped") + + return + else: + logging.info("Test step skipped") + + self.step(4) + enabledAlarms = 0 + + self.step("5a") + if is_vis_feature_supported: + enabledAlarms |= Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kVisual + else: + logging.info("Test step skipped") + + self.step("5b") + if is_aud_feature_supported: + enabledAlarms |= Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kAudible + else: + logging.info("Test step skipped") + + self.step("5c") + try: + await self.send_single_cmd(cmd=Clusters.Objects.BooleanStateConfiguration.Commands.EnableDisableAlarm(alarmsToEnableDisable=enabledAlarms), endpoint=endpoint) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + + self.step(6) + if is_vis_feature_supported or is_aud_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=enableKey, eventTrigger=sensorUntrigger), endpoint=0) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + + self.step("7a") + if is_vis_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.BooleanStateConfiguration.Commands.SuppressAlarm(alarmsToSuppress=Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kVisual), endpoint=endpoint) + asserts.fail("Received Success response when an INVALID_IN_STATE was expected") + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.InvalidInState, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + self.step("7b") + if not is_vis_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.BooleanStateConfiguration.Commands.SuppressAlarm(alarmsToSuppress=Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kVisual), endpoint=endpoint) + asserts.fail("Received Success response when an CONSTRAINT_ERROR was expected") + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.ConstraintError, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + self.step(8) + alarms_suppressed_dut = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsSuppressed) + asserts.assert_equal(alarms_suppressed_dut, 0, "AlarmsSuppressed is not the expected value") + + self.step("9a") + if is_aud_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.BooleanStateConfiguration.Commands.SuppressAlarm(alarmsToSuppress=Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kAudible), endpoint=endpoint) + asserts.fail("Received Success response when an INVALID_IN_STATE was expected") + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.InvalidInState, "Unexpected error returned") + pass + + self.step("9b") + if not is_aud_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.BooleanStateConfiguration.Commands.SuppressAlarm(alarmsToSuppress=Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kAudible), endpoint=endpoint) + asserts.fail("Received Success response when an CONSTRAINT_ERROR was expected") + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.ConstraintError, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + self.step(10) + alarms_suppressed_dut = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsSuppressed) + asserts.assert_equal(alarms_suppressed_dut, 0, "AlarmsSuppressed is not the expected value") + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_BOOLCFG_5_2.py b/src/python_testing/TC_BOOLCFG_5_2.py new file mode 100644 index 0000000000..83781e5a61 --- /dev/null +++ b/src/python_testing/TC_BOOLCFG_5_2.py @@ -0,0 +1,171 @@ +# +# 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. +# + +import logging + +import chip.clusters as Clusters +from chip.interaction_model import InteractionModelError, Status +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts + +sensorTrigger = 0x0080_0000_0000_0000 +sensorUntrigger = 0x0080_0000_0000_0001 + + +class TC_BOOLCFG_5_2(MatterBaseTest): + async def read_boolcfg_attribute_expect_success(self, endpoint, attribute): + cluster = Clusters.Objects.BooleanStateConfiguration + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + def desc_TC_BOOLCFG_5_2(self) -> str: + return "[TC-BOOLCFG-5.2] SuppressAlarm functionality for active alarms with DUT as Server" + + def steps_TC_BOOLCFG_5_2(self) -> list[TestStep]: + steps = [ + TestStep(1, "Commissioning, already done", is_commissioning=True), + TestStep(2, "Read FeatureMap attribute"), + TestStep(3, "Verify SPRS feature is supported"), + TestStep(4, "Create enabledAlarms and set to 0"), + TestStep("5a", "Enable VIS alarm in enabledAlarms"), + TestStep("5b", "Enable AUD alarm in enabledAlarms"), + TestStep("5c", "Set AlarmsEnabled attribute to value of enabledAlarms using AlarmsToEnableDisable command"), + TestStep(6, "Send TestEventTrigger with SensorTrigger event"), + TestStep(7, "Suppress VIS alarm using SuppressAlarm command"), + TestStep(8, "Read AlarmsSuppressed attribute"), + TestStep(9, "Suppress AUD alarm using SuppressAlarm command"), + TestStep(10, "Read AlarmsActive attribute"), + TestStep(11, "Send TestEventTrigger with SensorUntrigger event") + ] + return steps + + def pics_TC_BOOLCFG_5_2(self) -> list[str]: + pics = [ + "BOOLCFG.S", + ] + return pics + + @async_test_body + async def test_TC_BOOLCFG_5_2(self): + + asserts.assert_true('PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY' in self.matter_test_config.global_test_params, + "PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY must be included on the command line in " + "the --hex-arg flag as PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY:, " + "e.g. --hex-arg PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY:000102030405060708090a0b0c0d0e0f") + + endpoint = self.user_params.get("endpoint", 1) + enableKey = self.matter_test_config.global_test_params['PIXIT.BOOLCFG.TEST_EVENT_TRIGGER_KEY'] + + self.step(1) + attributes = Clusters.BooleanStateConfiguration.Attributes + + self.step(2) + feature_map = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.FeatureMap) + + is_sprs_feature_supported = feature_map & Clusters.BooleanStateConfiguration.Bitmaps.Feature.kAlarmSuppress + is_vis_feature_supported = feature_map & Clusters.BooleanStateConfiguration.Bitmaps.Feature.kVisual + is_aud_feature_supported = feature_map & Clusters.BooleanStateConfiguration.Bitmaps.Feature.kAudible + + self.step(3) + if not is_sprs_feature_supported: + logging.info("AlarmSuppress feature not supported skipping test case") + + # Skipping all remaining steps + for step in self.get_test_steps(self.current_test_info.name)[self.current_step_index:]: + self.step(step.test_plan_number) + logging.info("Test step skipped") + + return + else: + logging.info("Test step skipped") + + self.step(4) + enabledAlarms = 0 + + self.step("5a") + if is_vis_feature_supported: + enabledAlarms |= Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kVisual + else: + logging.info("Test step skipped") + + self.step("5b") + if is_aud_feature_supported: + enabledAlarms |= Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kAudible + else: + logging.info("Test step skipped") + + self.step("5c") + try: + await self.send_single_cmd(cmd=Clusters.Objects.BooleanStateConfiguration.Commands.EnableDisableAlarm(alarmsToEnableDisable=enabledAlarms), endpoint=endpoint) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + + self.step(6) + if is_vis_feature_supported or is_aud_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=enableKey, eventTrigger=sensorTrigger), endpoint=0) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + self.step(7) + if is_vis_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.BooleanStateConfiguration.Commands.SuppressAlarm(alarmsToSuppress=Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kVisual), endpoint=endpoint) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + self.step(8) + if is_vis_feature_supported: + alarms_suppressed_dut = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsSuppressed) + asserts.assert_not_equal((alarms_suppressed_dut & 0b01), 0, "Bit 0 in AlarmsSuppressed is not 1") + else: + logging.info("Test step skipped") + + self.step(9) + if is_aud_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.BooleanStateConfiguration.Commands.SuppressAlarm(alarmsToSuppress=Clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kAudible), endpoint=endpoint) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + else: + logging.info("Test step skipped") + + self.step(10) + if is_aud_feature_supported: + alarms_suppressed_dut = await self.read_boolcfg_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsSuppressed) + asserts.assert_not_equal((alarms_suppressed_dut & 0b10), 0, "Bit 1 in AlarmsSuppressed is not 1") + else: + logging.info("Test step skipped") + + self.step(11) + if is_vis_feature_supported or is_aud_feature_supported: + try: + await self.send_single_cmd(cmd=Clusters.Objects.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=enableKey, eventTrigger=sensorUntrigger), endpoint=0) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass + + +if __name__ == "__main__": + default_matter_test_main()