From 6a98248d5e1083bb52ac801ab2469e2d8e8cc7bd Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Wed, 24 Jul 2024 19:26:28 -0400 Subject: [PATCH 01/49] Fix global scope for data provider in tv casting app (#34483) * Fix global scope for data provider * Restyle --------- Co-authored-by: Andrei Litvin --- examples/tv-casting-app/linux/simple-app-helper.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/tv-casting-app/linux/simple-app-helper.cpp b/examples/tv-casting-app/linux/simple-app-helper.cpp index 3cf13fac8606f2..ac60b7fb4740f4 100644 --- a/examples/tv-casting-app/linux/simple-app-helper.cpp +++ b/examples/tv-casting-app/linux/simple-app-helper.cpp @@ -41,6 +41,7 @@ bool gCommissionerGeneratedPasscodeFlowRunning = false; DiscoveryDelegateImpl * DiscoveryDelegateImpl::_discoveryDelegateImpl = nullptr; bool gAwaitingCommissionerPasscodeInput = false; +LinuxCommissionableDataProvider gSimpleAppCommissionableDataProvider; std::shared_ptr targetCastingPlayer; DiscoveryDelegateImpl * DiscoveryDelegateImpl::GetInstance() @@ -470,9 +471,8 @@ CHIP_ERROR CommandHandler(int argc, char ** argv) // Commissioner-generated passcode, and then update the CastigApp's AppParameters to update the commissioning session's // passcode. LinuxDeviceOptions::GetInstance().payload.setUpPINCode = userEnteredPasscode; - LinuxCommissionableDataProvider gCommissionableDataProvider; - CHIP_ERROR err = CHIP_NO_ERROR; - err = InitCommissionableDataProvider(gCommissionableDataProvider, LinuxDeviceOptions::GetInstance()); + CHIP_ERROR err = CHIP_NO_ERROR; + err = InitCommissionableDataProvider(gSimpleAppCommissionableDataProvider, LinuxDeviceOptions::GetInstance()); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, @@ -482,7 +482,8 @@ CHIP_ERROR CommandHandler(int argc, char ** argv) } // Update the CommissionableDataProvider stored in this CastingApp's AppParameters and the CommissionableDataProvider to // be used for the commissioning session. - err = matter::casting::core::CastingApp::GetInstance()->UpdateCommissionableDataProvider(&gCommissionableDataProvider); + err = matter::casting::core::CastingApp::GetInstance()->UpdateCommissionableDataProvider( + &gSimpleAppCommissionableDataProvider); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, From c4a25e68e235440df7958c3d79f10b3a2afc0358 Mon Sep 17 00:00:00 2001 From: Junior Martinez <67972863+jmartinez-silabs@users.noreply.github.com> Date: Wed, 24 Jul 2024 22:34:04 -0400 Subject: [PATCH 02/49] Add checks for OffWithEffect command parameters contraints (#34396) * Add checks for OffWithEffect command parameters contraints * Update src/app/clusters/on-off-server/on-off-server.cpp Co-authored-by: Andrei Litvin * address comment * For unknownd effect variant, Default to the respective 0 value enum variant * Update src/app/clusters/on-off-server/on-off-server.cpp Co-authored-by: Boris Zbarsky * fix up rename --------- Co-authored-by: Andrei Litvin Co-authored-by: Boris Zbarsky --- .../clusters/on-off-server/on-off-server.cpp | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/app/clusters/on-off-server/on-off-server.cpp b/src/app/clusters/on-off-server/on-off-server.cpp index 8332403d68ebd4..726da2d29f7c4d 100644 --- a/src/app/clusters/on-off-server/on-off-server.cpp +++ b/src/app/clusters/on-off-server/on-off-server.cpp @@ -89,6 +89,12 @@ void UpdateModeBaseCurrentModeToOnMode(EndpointId endpoint) #endif // MATTER_DM_PLUGIN_MODE_BASE +template +bool IsKnownEnumValue(EnumType value) +{ + return (EnsureKnownEnumValue(value) != EnumType::kUnknownEnumValue); +} + } // namespace #ifdef MATTER_DM_PLUGIN_LEVEL_CONTROL @@ -609,6 +615,35 @@ bool OnOffServer::offWithEffectCommand(app::CommandHandler * commandObj, const a chip::EndpointId endpoint = commandPath.mEndpointId; Status status = Status::Success; + if (effectId != EffectIdentifierEnum::kUnknownEnumValue) + { + // Depending on effectId value, effectVariant enum type varies. + // The following check validates that effectVariant value is valid in relation to the applicable enum type. + // DelayedAllOffEffectVariantEnum or DyingLightEffectVariantEnum + if (effectId == EffectIdentifierEnum::kDelayedAllOff && + !IsKnownEnumValue(static_cast(effectVariant))) + { + // The server does not support the given variant, it SHALL use the default variant. + effectVariant = to_underlying(DelayedAllOffEffectVariantEnum::kDelayedOffFastFade); + } + else if (effectId == EffectIdentifierEnum::kDyingLight && + !IsKnownEnumValue(static_cast(effectVariant))) + { + // The server does not support the given variant, it SHALL use the default variant. + effectVariant = to_underlying(DyingLightEffectVariantEnum::kDyingLightFadeOff); + } + } + else + { + status = Status::ConstraintError; + } + + if (status != Status::Success) + { + commandObj->AddStatus(commandPath, status); + return true; + } + if (SupportsLightingApplications(endpoint)) { #ifdef MATTER_DM_PLUGIN_SCENES_MANAGEMENT From 21a5bd6a402b699c0d64283918e266092a771bd1 Mon Sep 17 00:00:00 2001 From: fesseha-eve <88329315+fessehaeve@users.noreply.github.com> Date: Thu, 25 Jul 2024 08:53:51 +0200 Subject: [PATCH 03/49] update constraints for LocalTemperatureCalibration and MinSetpointDeadBand attributes (#34473) --- .../outputs/all-clusters-app/app-templates/endpoint_config.h | 2 +- .../zap-templates/zcl/data-model/chip/thermostat-cluster.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h index f2c082b1d0d4a1..a77bd74f11f267 100644 --- a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h +++ b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h @@ -307,7 +307,7 @@ { (uint16_t) 0xBB8, (uint16_t) -0x6AB3, (uint16_t) 0x7FFF }, /* MaxHeatSetpointLimit */ \ { (uint16_t) 0x640, (uint16_t) -0x6AB3, (uint16_t) 0x7FFF }, /* MinCoolSetpointLimit */ \ { (uint16_t) 0xC80, (uint16_t) -0x6AB3, (uint16_t) 0x7FFF }, /* MaxCoolSetpointLimit */ \ - { (uint16_t) 0x19, (uint16_t) 0x0, (uint16_t) 0x19 }, /* MinSetpointDeadBand */ \ + { (uint16_t) 0x19, (uint16_t) 0x0, (uint16_t) 0x7F }, /* MinSetpointDeadBand */ \ { (uint16_t) 0x4, (uint16_t) 0x0, (uint16_t) 0x5 }, /* ControlSequenceOfOperation */ \ { (uint16_t) 0x1, (uint16_t) 0x0, (uint16_t) 0x9 }, /* SystemMode */ \ \ diff --git a/src/app/zap-templates/zcl/data-model/chip/thermostat-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/thermostat-cluster.xml index 91a89497d7c8cc..c2979487e45ae3 100644 --- a/src/app/zap-templates/zcl/data-model/chip/thermostat-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/thermostat-cluster.xml @@ -337,7 +337,7 @@ limitations under the License. - + LocalTemperatureCalibration @@ -361,7 +361,7 @@ limitations under the License. MaxCoolSetpointLimit - + MinSetpointDeadBand From 335ac96cdde78fd81d69d6fb451427e769b7f413 Mon Sep 17 00:00:00 2001 From: fesseha-eve <88329315+fessehaeve@users.noreply.github.com> Date: Thu, 25 Jul 2024 08:54:38 +0200 Subject: [PATCH 04/49] update tests and thermostat server cluster for new constraints for LocalTemperatureCalibration and MinSetpointDeadBand (#34474) --- src/app/clusters/thermostat-server/thermostat-server.cpp | 2 +- src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/clusters/thermostat-server/thermostat-server.cpp b/src/app/clusters/thermostat-server/thermostat-server.cpp index c650a30c871989..e802a9201f33dc 100644 --- a/src/app/clusters/thermostat-server/thermostat-server.cpp +++ b/src/app/clusters/thermostat-server/thermostat-server.cpp @@ -409,7 +409,7 @@ MatterThermostatClusterServerPreAttributeChangedCallback(const app::ConcreteAttr requested = *value; if (!AutoSupported) return imcode::UnsupportedAttribute; - if (requested < 0 || requested > 25) + if (requested < 0 || requested > 127) return imcode::InvalidValue; return imcode::Success; } diff --git a/src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml b/src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml index faf34fdea43e92..4558b08745a949 100644 --- a/src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml +++ b/src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml @@ -243,8 +243,8 @@ tests: response: constraints: type: int8s - minValue: -25 - maxValue: 25 + minValue: -127 + maxValue: 127 - label: "Step 13a: TH reads attribute OccupiedCoolingSetpoint from the DUT" PICS: TSTAT.S.F01 && TSTAT.S.A0017 && TSTAT.S.A0018 @@ -426,7 +426,7 @@ tests: constraints: type: int8s minValue: 0 - maxValue: 25 + maxValue: 127 - label: "Step 22: TH reads the RemoteSensing attribute from the DUT" PICS: TSTAT.S.A001a From 34462f1ffadf26ec31195c85d2ec24a3922cad69 Mon Sep 17 00:00:00 2001 From: Abdul Samad Date: Thu, 25 Jul 2024 01:56:13 -0500 Subject: [PATCH 05/49] Add target endpoint to `CommissioningWindowOpener` (#34425) * Add target endpoint id to commissioning window opener params Co-authored-by: Yufeng Wang * Set endpoint id in fabric-admin app * Set root endpoint id by default in commissioning window params Co-authored-by: Boris Zbarsky --------- Co-authored-by: Yufeng Wang Co-authored-by: Boris Zbarsky --- .../commands/pairing/OpenCommissioningWindowCommand.cpp | 2 ++ src/controller/CommissioningWindowOpener.cpp | 6 +++--- src/controller/CommissioningWindowOpener.h | 3 ++- src/controller/CommissioningWindowParams.h | 9 +++++++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.cpp b/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.cpp index 480cfbca35a20b..b2d811fdc8b114 100644 --- a/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.cpp +++ b/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.cpp @@ -38,6 +38,7 @@ CHIP_ERROR OpenCommissioningWindowCommand::RunCommand() VerifyOrReturnError(mSalt.HasValue(), CHIP_ERROR_INVALID_ARGUMENT); return mWindowOpener->OpenCommissioningWindow(Controller::CommissioningWindowVerifierParams() .SetNodeId(mNodeId) + .SetEndpointId(mEndpointId) .SetTimeout(mCommissioningWindowTimeout) .SetIteration(mIteration) .SetDiscriminator(mDiscriminator) @@ -50,6 +51,7 @@ CHIP_ERROR OpenCommissioningWindowCommand::RunCommand() SetupPayload ignored; return mWindowOpener->OpenCommissioningWindow(Controller::CommissioningWindowPasscodeParams() .SetNodeId(mNodeId) + .SetEndpointId(mEndpointId) .SetTimeout(mCommissioningWindowTimeout) .SetIteration(mIteration) .SetDiscriminator(mDiscriminator) diff --git a/src/controller/CommissioningWindowOpener.cpp b/src/controller/CommissioningWindowOpener.cpp index 35011e69565c4a..e8d1b29cb6437d 100644 --- a/src/controller/CommissioningWindowOpener.cpp +++ b/src/controller/CommissioningWindowOpener.cpp @@ -126,6 +126,7 @@ CHIP_ERROR CommissioningWindowOpener::OpenCommissioningWindow(const Commissionin mCommissioningWindowVerifierCallback = nullptr; mNodeId = params.GetNodeId(); mCommissioningWindowTimeout = params.GetTimeout(); + mTargetEndpointId = params.GetEndpointId(); if (params.GetReadVIDPIDAttributes()) { @@ -162,6 +163,7 @@ CHIP_ERROR CommissioningWindowOpener::OpenCommissioningWindow(const Commissionin mPBKDFIterations = params.GetIteration(); mCommissioningWindowOption = CommissioningWindowOption::kTokenWithProvidedPIN; mDiscriminator.SetLongValue(params.GetDiscriminator()); + mTargetEndpointId = params.GetEndpointId(); mNextStep = Step::kOpenCommissioningWindow; @@ -173,9 +175,7 @@ CHIP_ERROR CommissioningWindowOpener::OpenCommissioningWindowInternal(Messaging: { ChipLogProgress(Controller, "OpenCommissioningWindow for device ID 0x" ChipLogFormatX64, ChipLogValueX64(mNodeId)); - constexpr EndpointId kAdministratorCommissioningClusterEndpoint = 0; - - ClusterBase cluster(exchangeMgr, sessionHandle, kAdministratorCommissioningClusterEndpoint); + ClusterBase cluster(exchangeMgr, sessionHandle, mTargetEndpointId); if (mCommissioningWindowOption != CommissioningWindowOption::kOriginalSetupCode) { diff --git a/src/controller/CommissioningWindowOpener.h b/src/controller/CommissioningWindowOpener.h index b657345ca53219..28a25d77f7a014 100644 --- a/src/controller/CommissioningWindowOpener.h +++ b/src/controller/CommissioningWindowOpener.h @@ -165,7 +165,8 @@ class CommissioningWindowOpener Callback::Callback * mBasicCommissioningWindowCallback = nullptr; SetupPayload mSetupPayload; SetupDiscriminator mDiscriminator{}; - NodeId mNodeId = kUndefinedNodeId; + NodeId mNodeId = kUndefinedNodeId; + EndpointId mTargetEndpointId = kRootEndpointId; // Default endpoint for Administrator Commissioning Cluster System::Clock::Seconds16 mCommissioningWindowTimeout = System::Clock::kZero; CommissioningWindowOption mCommissioningWindowOption = CommissioningWindowOption::kOriginalSetupCode; Crypto::Spake2pVerifier mVerifier; // Used for non-basic commissioning. diff --git a/src/controller/CommissioningWindowParams.h b/src/controller/CommissioningWindowParams.h index a4f0e43fa096c2..c845ecf2c80135 100644 --- a/src/controller/CommissioningWindowParams.h +++ b/src/controller/CommissioningWindowParams.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -53,6 +54,13 @@ class CommissioningWindowCommonParams return static_cast(*this); } + EndpointId GetEndpointId() const { return mEndpointId; } + Derived & SetEndpointId(EndpointId endpointId) + { + mEndpointId = endpointId; + return static_cast(*this); + } + System::Clock::Seconds16 GetTimeout() const { return mTimeout; } // The duration for which the commissioning window should remain open. Derived & SetTimeout(System::Clock::Seconds16 timeout) @@ -82,6 +90,7 @@ class CommissioningWindowCommonParams private: NodeId mNodeId = kUndefinedNodeId; + EndpointId mEndpointId = kRootEndpointId; // Default endpoint for Administrator Commissioning Cluster System::Clock::Seconds16 mTimeout = System::Clock::Seconds16(300); // Defaulting uint32_t mIteration = 1000; // Defaulting Optional mDiscriminator = NullOptional; // Using optional type to avoid picking a sentinnel in valid range From 1b83c3a3dd124d2fe6f4938430bc6ed418e6ce9f Mon Sep 17 00:00:00 2001 From: C Freeman Date: Thu, 25 Jul 2024 08:22:16 -0400 Subject: [PATCH 06/49] TC-IDM-10.2: Add tests for choice conformance (#34445) * TC-IDM-10.2: Add tests for choice conformance Adds functions to evaluate conformance on elements (features, attributes and commands). Tests: - unit tests included - tested on all-clusters-app, and all-clusters-app with switch feature conformance changed to be incorrect. * styling and linter * Restyled by autopep8 * Restyled by isort * Apply suggestions from code review Co-authored-by: Andrei Litvin --------- Co-authored-by: Restyled.io Co-authored-by: Andrei Litvin --- src/python_testing/TC_DeviceConformance.py | 14 +- .../TestChoiceConformanceSupport.py | 211 ++++++++++++++++++ .../choice_conformance_support.py | 74 ++++++ 3 files changed, 298 insertions(+), 1 deletion(-) create mode 100644 src/python_testing/TestChoiceConformanceSupport.py create mode 100644 src/python_testing/choice_conformance_support.py diff --git a/src/python_testing/TC_DeviceConformance.py b/src/python_testing/TC_DeviceConformance.py index 851921df60f0a4..c7fa19c45cec2c 100644 --- a/src/python_testing/TC_DeviceConformance.py +++ b/src/python_testing/TC_DeviceConformance.py @@ -32,6 +32,8 @@ import chip.clusters as Clusters from basic_composition_support import BasicCompositionTests from chip.tlv import uint +from choice_conformance_support import (evaluate_attribute_choice_conformance, evaluate_command_choice_conformance, + evaluate_feature_choice_conformance) from conformance_support import ConformanceDecision, conformance_allowed from global_attribute_ids import GlobalAttributeIds from matter_testing_support import (AttributePathLocation, ClusterPathLocation, CommandPathLocation, MatterBaseTest, ProblemNotice, @@ -188,7 +190,17 @@ def check_spec_conformance_for_commands(command_type: CommandType): check_spec_conformance_for_commands(CommandType.ACCEPTED) check_spec_conformance_for_commands(CommandType.GENERATED) - # TODO: Add choice checkers + feature_choice_problems = evaluate_feature_choice_conformance( + endpoint_id, cluster_id, self.xml_clusters, feature_map, attribute_list, all_command_list) + attribute_choice_problems = evaluate_attribute_choice_conformance( + endpoint_id, cluster_id, self.xml_clusters, feature_map, attribute_list, all_command_list) + command_choice_problem = evaluate_command_choice_conformance( + endpoint_id, cluster_id, self.xml_clusters, feature_map, attribute_list, all_command_list) + + if feature_choice_problems or attribute_choice_problems or command_choice_problem: + success = False + problems.extend(feature_choice_problems + attribute_choice_problems + command_choice_problem) + print(f'success = {success}') return success, problems diff --git a/src/python_testing/TestChoiceConformanceSupport.py b/src/python_testing/TestChoiceConformanceSupport.py new file mode 100644 index 00000000000000..8436bc8418a804 --- /dev/null +++ b/src/python_testing/TestChoiceConformanceSupport.py @@ -0,0 +1,211 @@ +# +# 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 itertools +import xml.etree.ElementTree as ElementTree + +import jinja2 +from choice_conformance_support import (evaluate_attribute_choice_conformance, evaluate_command_choice_conformance, + evaluate_feature_choice_conformance) +from matter_testing_support import MatterBaseTest, ProblemNotice, default_matter_test_main +from mobly import asserts +from spec_parsing_support import XmlCluster, add_cluster_data_from_xml + +FEATURE_TEMPLATE = '''\ + + + {%- if XXX %}' + + {% endif %} + + +''' + +ATTRIBUTE_TEMPLATE = ( + ' \n' + ' \n' + ' {% if XXX %}' + ' \n' + ' {% endif %}' + ' \n' + ' \n' +) + +COMMAND_TEMPLATE = ( + ' \n' + ' \n' + ' {% if XXX %}' + ' \n' + ' {% endif %}' + ' \n' + ' \n' +) + +CLUSTER_TEMPLATE = ( + '\n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' {{ feature_string }}\n' + ' \n' + ' \n' + ' {{ attribute_string}}\n' + ' \n' + ' \n' + ' {{ command_string }}\n' + ' \n' + '\n') + + +def _create_elements(template_str: str, base_name: str) -> list[str]: + xml_str = [] + + def add_elements(curr_choice: str, starting_id: int, more: str, XXX: bool): + for i in range(3): + element_name = f'{base_name}{curr_choice.upper()*(i+1)}' + environment = jinja2.Environment() + template = environment.from_string(template_str) + xml_str.append(template.render(id=(i + starting_id), name=element_name, choice=curr_choice, more=more, XXX=XXX)) + add_elements('a', 1, 'false', False) + add_elements('b', 4, 'true', False) + add_elements('c', 7, 'false', True) + add_elements('d', 10, 'true', True) + + return xml_str + +# TODO: this setup makes my life easy because it assumes that choice conformances apply only within one table +# if this is not true (ex, you can have choose 1 of a feature or an attribute), then this gets more complex +# in this case we need to have this test evaluate the same choice conformance value between multiple tables, and all +# the conformances need to be assessed for the entire element set. +# I've done it this way specifically so I can hardcode the choice values and test the features, attributes and commands +# separately, even though I load them all at the start. + +# Cluster with choices on all elements +# 3 of each element with O.a +# 3 of each element with O.b+ +# 3 of each element with [XXX].c +# 3 of each element with [XXX].d+ +# 1 element named XXX + + +def _create_features(): + xml = _create_elements(FEATURE_TEMPLATE, 'F') + xxx = (' \n' + ' \n' + ' \n') + xml.append(xxx) + return '\n'.join(xml) + + +def _create_attributes(): + xml = _create_elements(ATTRIBUTE_TEMPLATE, "attr") + xxx = (' \n' + ' \n' + ' \n') + xml.append(xxx) + return '\n'.join(xml) + + +def _create_commands(): + xml = _create_elements(COMMAND_TEMPLATE, 'cmd') + xxx = (' \n' + ' \n' + ' \n') + xml.append(xxx) + return '\n'.join(xml) + + +def _create_cluster(): + environment = jinja2.Environment() + template = environment.from_string(CLUSTER_TEMPLATE) + return template.render(feature_string=_create_features(), attribute_string=_create_attributes(), command_string=_create_commands()) + + +class TestConformanceSupport(MatterBaseTest): + def setup_class(self): + super().setup_class() + + clusters: dict[int, XmlCluster] = {} + pure_base_clusters: dict[str, XmlCluster] = {} + ids_by_name: dict[str, int] = {} + problems: list[ProblemNotice] = [] + cluster_xml = ElementTree.fromstring(_create_cluster()) + add_cluster_data_from_xml(cluster_xml, clusters, pure_base_clusters, ids_by_name, problems) + self.clusters = clusters + # each element type uses 13 IDs from 1-13 (or bits for the features) and we want to test all the combinations + num_elements = 13 + ids = range(1, num_elements + 1) + self.all_id_combos = [] + combos = [] + for r in range(1, num_elements + 1): + combos.extend(list(itertools.combinations(ids, r))) + for combo in combos: + # The first three IDs are all O.a, so we need exactly one for the conformance to be valid + expected_failures = set() + if len(set([1, 2, 3]) & set(combo)) != 1: + expected_failures.add('a') + if len(set([4, 5, 6]) & set(combo)) < 1: + expected_failures.add('b') + # For these, we are checking that choice conformance checkers + # - Correctly report errors and correct cases when the gating feature is ON + # - Do not report any errors when the gating features is off. + # Errors where we incorrectly set disallowed features based on the gating feature are checked + # elsewhere in the cert test in a comprehensive way. We just want to ensure that we are not + # incorrectly reporting choice conformance error as well + if 13 in combo and ((len(set([7, 8, 9]) & set(combo)) != 1)): + expected_failures.add('c') + if 13 in combo and (len(set([10, 11, 12]) & set(combo)) < 1): + expected_failures.add('d') + + self.all_id_combos.append((combo, expected_failures)) + + def _evaluate_problems(self, problems, expected_failures=list[str]): + if len(expected_failures) != len(problems): + print(problems) + asserts.assert_equal(len(expected_failures), len(problems), 'Unexpected number of choice conformance problems') + actual_failures = set([p.choice.marker for p in problems]) + asserts.assert_equal(actual_failures, expected_failures, "Mismatch between failures") + + def test_features(self): + def make_feature_map(combo: tuple[int]) -> int: + feature_map = 0 + for bit in combo: + feature_map += pow(2, bit) + return feature_map + + for combo, expected_failures in self.all_id_combos: + problems = evaluate_feature_choice_conformance(0, 1, self.clusters, make_feature_map(combo), [], []) + self._evaluate_problems(problems, expected_failures) + + def test_attributes(self): + for combo, expected_failures in self.all_id_combos: + problems = evaluate_attribute_choice_conformance(0, 1, self.clusters, 0, list(combo), []) + self._evaluate_problems(problems, expected_failures) + + def test_commands(self): + for combo, expected_failures in self.all_id_combos: + problems = evaluate_command_choice_conformance(0, 1, self.clusters, 0, [], list(combo)) + self._evaluate_problems(problems, expected_failures) + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/choice_conformance_support.py b/src/python_testing/choice_conformance_support.py new file mode 100644 index 00000000000000..58d37bf10180b0 --- /dev/null +++ b/src/python_testing/choice_conformance_support.py @@ -0,0 +1,74 @@ +from chip.tlv import uint +from conformance_support import Choice, ConformanceDecisionWithChoice +from global_attribute_ids import GlobalAttributeIds +from matter_testing_support import AttributePathLocation, ProblemNotice, ProblemSeverity +from spec_parsing_support import XmlCluster + + +class ChoiceConformanceProblemNotice(ProblemNotice): + def __init__(self, location: AttributePathLocation, choice: Choice, count: int): + problem = f'Problem with choice conformance {choice} - {count} selected' + super().__init__(test_name='Choice conformance', location=location, severity=ProblemSeverity.ERROR, problem=problem, spec_location='') + self.choice = choice + self.count = count + + +def _add_to_counts_if_required(conformance_decision_with_choice: ConformanceDecisionWithChoice, element_present: bool, counts: dict[Choice, int]): + choice = conformance_decision_with_choice.choice + if not choice: + return + counts[choice] = counts.get(choice, 0) + if element_present: + counts[choice] += 1 + + +def _evaluate_choices(location: AttributePathLocation, counts: dict[Choice, int]) -> list[ChoiceConformanceProblemNotice]: + problems: list[ChoiceConformanceProblemNotice] = [] + for choice, count in counts.items(): + if count == 0 or (not choice.more and count > 1): + problems.append(ChoiceConformanceProblemNotice(location, choice, count)) + return problems + + +def evaluate_feature_choice_conformance(endpoint_id: int, cluster_id: int, xml_clusters: dict[int, XmlCluster], feature_map: uint, attribute_list: list[uint], all_command_list: list[uint]) -> list[ChoiceConformanceProblemNotice]: + all_features = [1 << i for i in range(32)] + all_features = [f for f in all_features if f in xml_clusters[cluster_id].features.keys()] + + # Other pieces of the 10.2 test check for unknown features, so just remove them here to check choice conformance + counts: dict[Choice, int] = {} + for f in all_features: + xml_feature = xml_clusters[cluster_id].features[f] + conformance_decision_with_choice = xml_feature.conformance(feature_map, attribute_list, all_command_list) + _add_to_counts_if_required(conformance_decision_with_choice, (feature_map & f), counts) + + location = AttributePathLocation(endpoint_id=endpoint_id, cluster_id=cluster_id, + attribute_id=GlobalAttributeIds.FEATURE_MAP_ID) + return _evaluate_choices(location, counts) + + +def evaluate_attribute_choice_conformance(endpoint_id: int, cluster_id: int, xml_clusters: dict[int, XmlCluster], feature_map: uint, attribute_list: list[uint], all_command_list: list[uint]) -> list[ChoiceConformanceProblemNotice]: + all_attributes = xml_clusters[cluster_id].attributes.keys() + + counts: dict[Choice, int] = {} + for attribute_id in all_attributes: + conformance_decision_with_choice = xml_clusters[cluster_id].attributes[attribute_id].conformance( + feature_map, attribute_list, all_command_list) + _add_to_counts_if_required(conformance_decision_with_choice, attribute_id in attribute_list, counts) + + location = AttributePathLocation(endpoint_id=endpoint_id, cluster_id=cluster_id, + attribute_id=GlobalAttributeIds.ATTRIBUTE_LIST_ID) + return _evaluate_choices(location, counts) + + +def evaluate_command_choice_conformance(endpoint_id: int, cluster_id: int, xml_clusters: dict[int, XmlCluster], feature_map: uint, attribute_list: list[uint], all_command_list: list[uint]) -> list[ChoiceConformanceProblemNotice]: + all_commands = xml_clusters[cluster_id].accepted_commands.keys() + + counts: dict[Choice, int] = {} + for command_id in all_commands: + conformance_decision_with_choice = xml_clusters[cluster_id].accepted_commands[command_id].conformance( + feature_map, attribute_list, all_command_list) + _add_to_counts_if_required(conformance_decision_with_choice, command_id in all_command_list, counts) + + location = AttributePathLocation(endpoint_id=endpoint_id, cluster_id=cluster_id, + attribute_id=GlobalAttributeIds.ACCEPTED_COMMAND_LIST_ID) + return _evaluate_choices(location, counts) From 1b1340f36557a7cf1bac3f4b99ee0ce622269c40 Mon Sep 17 00:00:00 2001 From: Kamil Kasperczyk <66371704+kkasperczyk-no@users.noreply.github.com> Date: Thu, 25 Jul 2024 15:46:38 +0200 Subject: [PATCH 07/49] [examples] Fixed shell build for nrfconnect (#34500) Shell example build for nrfconnect was broken due to missing sysbuild.conf file that enabled Matter. --- examples/shell/nrfconnect/sysbuild.conf | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 examples/shell/nrfconnect/sysbuild.conf diff --git a/examples/shell/nrfconnect/sysbuild.conf b/examples/shell/nrfconnect/sysbuild.conf new file mode 100644 index 00000000000000..e63a92c6b2bbab --- /dev/null +++ b/examples/shell/nrfconnect/sysbuild.conf @@ -0,0 +1,18 @@ +# +# 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. +# + +SB_CONFIG_MATTER=y +SB_CONFIG_MATTER_OTA=n From bfa80b941762706d415a2bf3f0953223f49bbf23 Mon Sep 17 00:00:00 2001 From: Adam Bodurka Date: Thu, 25 Jul 2024 16:03:53 +0200 Subject: [PATCH 08/49] [QPG] QPG examples bulk update (#34501) * [QPG] Add TotalOperationalHours counter for switch and TRV * [QPG] fix for switch to use correct previousPosition in ShortRelease event * [QPG] Use identify cluster on endpoint 2 for switch * feat(multicast): Add multicast binding for Matter Combo Switch Refs: APPSCS-4571 * [QPG] MultiPress and LongPress implementation for switch * [INTERNAL] Fix qPinCfg.h error * Set groups cluster to server in switch.zap and improve command handling for unicast and multicast * [QPG] PowerSource cluster added * Restyled by whitespace * Restyled by clang-format * Fixed .matter file --------- Co-authored-by: Dieter Van der Meulen Co-authored-by: lucicop Co-authored-by: Restyled.io --- .../light-switch-app/qpg/include/AppTask.h | 3 + .../qpg/include/SwitchManager.h | 6 +- examples/light-switch-app/qpg/src/AppTask.cpp | 158 ++++++- .../qpg/src/SwitchManager.cpp | 83 +++- .../qpg/src/binding-handler.cpp | 137 +++++- .../light-switch-app/qpg/zap/switch.matter | 308 +++++++++++- examples/light-switch-app/qpg/zap/switch.zap | 440 +++++++++++++++++- examples/lighting-app/qpg/src/AppTask.cpp | 15 +- examples/lock-app/qpg/src/AppTask.cpp | 15 +- examples/thermostat/qpg/include/AppTask.h | 1 + examples/thermostat/qpg/src/AppTask.cpp | 44 ++ 11 files changed, 1151 insertions(+), 59 deletions(-) diff --git a/examples/light-switch-app/qpg/include/AppTask.h b/examples/light-switch-app/qpg/include/AppTask.h index 9284e068ab8509..caf56977225280 100644 --- a/examples/light-switch-app/qpg/include/AppTask.h +++ b/examples/light-switch-app/qpg/include/AppTask.h @@ -58,6 +58,9 @@ class AppTask static void FunctionHandler(AppEvent * aEvent); static void TimerEventHandler(chip::System::Layer * aLayer, void * aAppState); + static void TotalHoursTimerHandler(chip::System::Layer * aLayer, void * aAppState); + static void MultiPressTimeoutHandler(chip::System::Layer * aLayer, void * aAppState); + static void LongPressTimeoutHandler(chip::System::Layer * aLayer, void * aAppState); static void MatterEventHandler(const chip::DeviceLayer::ChipDeviceEvent * event, intptr_t arg); static void UpdateLEDs(void); diff --git a/examples/light-switch-app/qpg/include/SwitchManager.h b/examples/light-switch-app/qpg/include/SwitchManager.h index 41eedd10af4f13..df09260285c96d 100644 --- a/examples/light-switch-app/qpg/include/SwitchManager.h +++ b/examples/light-switch-app/qpg/include/SwitchManager.h @@ -55,7 +55,11 @@ class SwitchManager void Init(void); static void GenericSwitchInitialPressHandler(AppEvent * aEvent); - static void GenericSwitchReleasePressHandler(AppEvent * aEvent); + static void GenericSwitchShortReleaseHandler(AppEvent * aEvent); + static void GenericSwitchLongReleaseHandler(AppEvent * aEvent); + static void GenericSwitchLongPressHandler(AppEvent * aEvent); + static void GenericSwitchMultipressCompleteHandler(AppEvent * aEvent); + static void GenericSwitchMultipressOngoingHandler(AppEvent * aEvent); static void ToggleHandler(AppEvent * aEvent); static void LevelHandler(AppEvent * aEvent); static void ColorHandler(AppEvent * aEvent); diff --git a/examples/light-switch-app/qpg/src/AppTask.cpp b/examples/light-switch-app/qpg/src/AppTask.cpp index 4be2f4eeefbf39..7cb0a41a452aca 100644 --- a/examples/light-switch-app/qpg/src/AppTask.cpp +++ b/examples/light-switch-app/qpg/src/AppTask.cpp @@ -42,6 +42,10 @@ using namespace ::chip; #include #include +#if defined(QORVO_QPINCFG_ENABLE) +#include "qPinCfg.h" +#endif // QORVO_QPINCFG_ENABLE + #include #include @@ -58,9 +62,16 @@ using namespace ::chip::DeviceLayer; #define FACTORY_RESET_TRIGGER_TIMEOUT 3000 #define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000 +#define SWITCH_MULTIPRESS_WINDOW_MS 500 +#define SWITCH_LONGPRESS_WINDOW_MS 3000 +#define SWITCH_BUTTON_PRESSED 1 +#define SWITCH_BUTTON_UNPRESSED 0 + #define APP_TASK_STACK_SIZE (2 * 1024) #define APP_TASK_PRIORITY 2 #define APP_EVENT_QUEUE_SIZE 10 +#define SECONDS_IN_HOUR (3600) // we better keep this 3600 +#define TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS (1 * SECONDS_IN_HOUR) // increment every hour namespace { TaskHandle_t sAppTaskHandle; @@ -70,6 +81,9 @@ bool sIsThreadProvisioned = false; bool sIsThreadEnabled = false; bool sHaveBLEConnections = false; bool sIsBLEAdvertisingEnabled = false; +bool sIsMultipressOngoing = false; +bool sLongPressDetected = false; +uint8_t sSwitchButtonState = SWITCH_BUTTON_UNPRESSED; // 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, @@ -141,11 +155,19 @@ void OnTriggerIdentifyEffect(Identify * identify) } } -Identify gIdentify = { +Identify gIdentifyEp1 = { chip::EndpointId{ 1 }, [](Identify *) { ChipLogProgress(Zcl, "onIdentifyStart"); }, [](Identify *) { ChipLogProgress(Zcl, "onIdentifyStop"); }, - Clusters::Identify::IdentifyTypeEnum::kVisibleIndicator, + Clusters::Identify::IdentifyTypeEnum::kNone, + OnTriggerIdentifyEffect, +}; + +Identify gIdentifyEp2 = { + chip::EndpointId{ 2 }, + [](Identify *) { ChipLogProgress(Zcl, "onIdentifyStart"); }, + [](Identify *) { ChipLogProgress(Zcl, "onIdentifyStop"); }, + Clusters::Identify::IdentifyTypeEnum::kNone, OnTriggerIdentifyEffect, }; @@ -224,6 +246,14 @@ CHIP_ERROR AppTask::Init() { CHIP_ERROR err = CHIP_NO_ERROR; +#if defined(QORVO_QPINCFG_ENABLE) + qResult_t res = Q_OK; + res = qPinCfg_Init(NULL); + if (res != Q_OK) + { + ChipLogError(NotSpecified, "qPinCfg_Init failed: %d", res); + } +#endif // QORVO_QPINCFG_ENABLE PlatformMgr().AddEventHandler(MatterEventHandler, 0); ChipLogProgress(NotSpecified, "Current Software Version: %s", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING); @@ -255,6 +285,14 @@ CHIP_ERROR AppTask::Init() sIsBLEAdvertisingEnabled = ConnectivityMgr().IsBLEAdvertisingEnabled(); UpdateLEDs(); + err = chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds32(TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS), + TotalHoursTimerHandler, this); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "StartTimer failed %s: ", chip::ErrorStr(err)); + } + return err; } @@ -275,7 +313,9 @@ void AppTask::AppTaskMain(void * pvParameter) void AppTask::ButtonEventHandler(uint8_t btnIdx, bool btnPressed) { - ChipLogProgress(NotSpecified, "ButtonEventHandler %d, %d", btnIdx, btnPressed); + CHIP_ERROR err = CHIP_NO_ERROR; + + ChipLogDetail(NotSpecified, "ButtonEventHandler %d, %d", btnIdx, btnPressed); AppEvent button_event = {}; button_event.Type = AppEvent::kEventType_Button; @@ -297,13 +337,54 @@ void AppTask::ButtonEventHandler(uint8_t btnIdx, bool btnPressed) case APP_FUNCTION2_SWITCH: { if (!btnPressed) { - ChipLogProgress(NotSpecified, "Switch release press"); - button_event.Handler = SwitchMgr().GenericSwitchReleasePressHandler; + ChipLogDetail(NotSpecified, "Switch button released"); + + button_event.Handler = + sLongPressDetected ? SwitchMgr().GenericSwitchLongReleaseHandler : SwitchMgr().GenericSwitchShortReleaseHandler; + + sIsMultipressOngoing = true; + sSwitchButtonState = SWITCH_BUTTON_UNPRESSED; + sLongPressDetected = false; + + chip::DeviceLayer::SystemLayer().CancelTimer(MultiPressTimeoutHandler, NULL); + // we start the MultiPress feature window after releasing the button + err = chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(SWITCH_MULTIPRESS_WINDOW_MS), + MultiPressTimeoutHandler, NULL); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "StartTimer failed %s: ", chip::ErrorStr(err)); + } } else { - ChipLogProgress(NotSpecified, "Switch initial press"); - button_event.Handler = SwitchMgr().GenericSwitchInitialPressHandler; + ChipLogDetail(NotSpecified, "Switch button pressed"); + + sSwitchButtonState = SWITCH_BUTTON_PRESSED; + + chip::DeviceLayer::SystemLayer().CancelTimer(LongPressTimeoutHandler, NULL); + // we need to check if this is short or long press + err = chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(SWITCH_LONGPRESS_WINDOW_MS), + LongPressTimeoutHandler, NULL); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "StartTimer failed %s: ", chip::ErrorStr(err)); + } + + // if we have active multipress window we need to send extra event + if (sIsMultipressOngoing) + { + ChipLogDetail(NotSpecified, "Switch MultipressOngoing"); + button_event.Handler = SwitchMgr().GenericSwitchInitialPressHandler; + sAppTask.PostEvent(&button_event); + chip::DeviceLayer::SystemLayer().CancelTimer(MultiPressTimeoutHandler, NULL); + button_event.Handler = SwitchMgr().GenericSwitchMultipressOngoingHandler; + } + else + { + button_event.Handler = SwitchMgr().GenericSwitchInitialPressHandler; + } } break; } @@ -348,6 +429,69 @@ void AppTask::TimerEventHandler(chip::System::Layer * aLayer, void * aAppState) sAppTask.PostEvent(&event); } +void AppTask::MultiPressTimeoutHandler(chip::System::Layer * aLayer, void * aAppState) +{ + ChipLogDetail(NotSpecified, "MultiPressTimeoutHandler"); + + sIsMultipressOngoing = false; + + AppEvent multipress_event = {}; + multipress_event.Type = AppEvent::kEventType_Button; + multipress_event.Handler = SwitchMgr().GenericSwitchMultipressCompleteHandler; + + sAppTask.PostEvent(&multipress_event); +} + +void AppTask::LongPressTimeoutHandler(chip::System::Layer * aLayer, void * aAppState) +{ + ChipLogDetail(NotSpecified, "LongPressTimeoutHandler"); + + // if the button is still pressed after threshold time, this is a LongPress, otherwise jsut ignore it + if (sSwitchButtonState == SWITCH_BUTTON_PRESSED) + { + sLongPressDetected = true; + AppEvent longpress_event = {}; + longpress_event.Type = AppEvent::kEventType_Button; + longpress_event.Handler = SwitchMgr().GenericSwitchLongPressHandler; + + sAppTask.PostEvent(&longpress_event); + } +} + +void AppTask::TotalHoursTimerHandler(chip::System::Layer * aLayer, void * aAppState) +{ + ChipLogDetail(NotSpecified, "HourlyTimer"); + + CHIP_ERROR err; + uint32_t totalOperationalHours = 0; + + err = ConfigurationMgr().GetTotalOperationalHours(totalOperationalHours); + + if (err == CHIP_NO_ERROR) + { + ConfigurationMgr().StoreTotalOperationalHours(totalOperationalHours + + (TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS / SECONDS_IN_HOUR)); + } + else if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) + { + totalOperationalHours = 0; // set this explicitly to 0 for safety + ConfigurationMgr().StoreTotalOperationalHours(totalOperationalHours + + (TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS / SECONDS_IN_HOUR)); + } + else + { + ChipLogError(DeviceLayer, "Failed to get total operational hours of the Node"); + } + + err = chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds32(TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS), + TotalHoursTimerHandler, nullptr); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "StartTimer failed %s: ", chip::ErrorStr(err)); + } +} + void AppTask::FunctionTimerEventHandler(AppEvent * aEvent) { if (aEvent->Type != AppEvent::kEventType_Timer) diff --git a/examples/light-switch-app/qpg/src/SwitchManager.cpp b/examples/light-switch-app/qpg/src/SwitchManager.cpp index 58b2f1d9bd8889..170bb8b454c765 100644 --- a/examples/light-switch-app/qpg/src/SwitchManager.cpp +++ b/examples/light-switch-app/qpg/src/SwitchManager.cpp @@ -26,10 +26,12 @@ SwitchManager SwitchManager::sSwitch; using namespace ::chip; using namespace chip::DeviceLayer; +static uint8_t multiPressCount = 1; void SwitchManager::Init(void) { - // init - TODO + uint8_t multiPressMax = 2; + chip::app::Clusters::Switch::Attributes::MultiPressMax::Set(GENERICSWITCH_ENDPOINT_ID, multiPressMax); } void SwitchManager::ToggleHandler(AppEvent * aEvent) @@ -46,6 +48,7 @@ void SwitchManager::ToggleHandler(AppEvent * aEvent) data->localEndpointId = SWITCH_ENDPOINT_ID; data->clusterId = chip::app::Clusters::OnOff::Id; data->commandId = chip::app::Clusters::OnOff::Commands::Toggle::Id; + data->isGroup = true; DeviceLayer::PlatformMgr().ScheduleWork(SwitchWorkerFunction, reinterpret_cast(data)); } @@ -118,7 +121,7 @@ void SwitchManager::GenericSwitchInitialPressHandler(AppEvent * aEvent) return; } - ChipLogProgress(NotSpecified, "GenericSwitchInitialPress new position %d", newPosition); + ChipLogDetail(NotSpecified, "GenericSwitchInitialPress new position %d", newPosition); SystemLayer().ScheduleLambda([newPosition] { chip::app::Clusters::Switch::Attributes::CurrentPosition::Set(GENERICSWITCH_ENDPOINT_ID, newPosition); // InitialPress event takes newPosition as event data @@ -126,10 +129,10 @@ void SwitchManager::GenericSwitchInitialPressHandler(AppEvent * aEvent) }); } -void SwitchManager::GenericSwitchReleasePressHandler(AppEvent * aEvent) +void SwitchManager::GenericSwitchLongPressHandler(AppEvent * aEvent) { - // Release moves Position from 1 (press) to 0 - uint8_t newPosition = 0; + // Press moves Position from 0 (idle) to 1 (press) + uint8_t newPosition = 1; if (aEvent->Type != AppEvent::kEventType_Button) { @@ -137,10 +140,76 @@ void SwitchManager::GenericSwitchReleasePressHandler(AppEvent * aEvent) return; } - ChipLogProgress(NotSpecified, "GenericSwitchReleasePress new position %d", newPosition); + ChipLogDetail(NotSpecified, "GenericSwitchLongPress new position %d", newPosition); SystemLayer().ScheduleLambda([newPosition] { + // LongPress event takes newPosition as event data + chip::app::Clusters::SwitchServer::Instance().OnLongPress(GENERICSWITCH_ENDPOINT_ID, newPosition); + }); +} + +void SwitchManager::GenericSwitchShortReleaseHandler(AppEvent * aEvent) +{ + // Release moves Position from 1 (press) to 0 + uint8_t newPosition = 0; + uint8_t previousPosition = 1; + + if (aEvent->Type != AppEvent::kEventType_Button) + { + ChipLogError(NotSpecified, "Event type not supported!"); + return; + } + + ChipLogDetail(NotSpecified, "GenericSwitchShortRelease new position %d", newPosition); + SystemLayer().ScheduleLambda([newPosition, previousPosition] { chip::app::Clusters::Switch::Attributes::CurrentPosition::Set(GENERICSWITCH_ENDPOINT_ID, newPosition); // Short Release event takes newPosition as event data - chip::app::Clusters::SwitchServer::Instance().OnShortRelease(GENERICSWITCH_ENDPOINT_ID, newPosition); + chip::app::Clusters::SwitchServer::Instance().OnShortRelease(GENERICSWITCH_ENDPOINT_ID, previousPosition); + }); +} + +void SwitchManager::GenericSwitchLongReleaseHandler(AppEvent * aEvent) +{ + // Release moves Position from 1 (press) to 0 + uint8_t newPosition = 0; + uint8_t previousPosition = 1; + + if (aEvent->Type != AppEvent::kEventType_Button) + { + ChipLogError(NotSpecified, "Event type not supported!"); + return; + } + + ChipLogDetail(NotSpecified, "GenericSwitchLongRelease new position %d", newPosition); + SystemLayer().ScheduleLambda([newPosition, previousPosition] { + chip::app::Clusters::Switch::Attributes::CurrentPosition::Set(GENERICSWITCH_ENDPOINT_ID, newPosition); + // LongRelease event takes newPosition as event data + chip::app::Clusters::SwitchServer::Instance().OnLongRelease(GENERICSWITCH_ENDPOINT_ID, previousPosition); + }); +} + +void SwitchManager::GenericSwitchMultipressOngoingHandler(AppEvent * aEvent) +{ + uint8_t newPosition = 1; + + multiPressCount++; + + ChipLogDetail(NotSpecified, "GenericSwitchMultiPressOngoing (%d)", multiPressCount); + + SystemLayer().ScheduleLambda([newPosition] { + chip::app::Clusters::SwitchServer::Instance().OnMultiPressOngoing(GENERICSWITCH_ENDPOINT_ID, newPosition, multiPressCount); }); } + +void SwitchManager::GenericSwitchMultipressCompleteHandler(AppEvent * aEvent) +{ + uint8_t previousPosition = 0; + + ChipLogProgress(NotSpecified, "GenericSwitchMultiPressComplete (%d)", multiPressCount); + + SystemLayer().ScheduleLambda([previousPosition] { + chip::app::Clusters::SwitchServer::Instance().OnMultiPressComplete(GENERICSWITCH_ENDPOINT_ID, previousPosition, + multiPressCount); + }); + + multiPressCount = 1; +} diff --git a/examples/light-switch-app/qpg/src/binding-handler.cpp b/examples/light-switch-app/qpg/src/binding-handler.cpp index 4dae4e12b44155..f2a7c266d62338 100644 --- a/examples/light-switch-app/qpg/src/binding-handler.cpp +++ b/examples/light-switch-app/qpg/src/binding-handler.cpp @@ -38,31 +38,120 @@ static void ProcessSwitchUnicastBindingCommand(CommandId commandId, const EmberB ChipLogError(NotSpecified, "Switch command failed: %" CHIP_ERROR_FORMAT, error.Format()); }; - switch (commandId) + switch (data->clusterId) { - case Clusters::OnOff::Commands::Toggle::Id: - Clusters::OnOff::Commands::Toggle::Type toggleCommand; - Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, toggleCommand, onSuccess, onFailure); + case Clusters::OnOff::Id: + switch (commandId) + { + case Clusters::OnOff::Commands::Toggle::Id: + Clusters::OnOff::Commands::Toggle::Type toggleCommand; + Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, toggleCommand, onSuccess, onFailure); + break; + case Clusters::OnOff::Commands::On::Id: + Clusters::OnOff::Commands::On::Type onCommand; + Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, onCommand, onSuccess, onFailure); + break; + + case Clusters::OnOff::Commands::Off::Id: + Clusters::OnOff::Commands::Off::Type offCommand; + Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, offCommand, onSuccess, onFailure); + break; + default: + ChipLogError(NotSpecified, "Unsupported Command Id"); + break; + } break; - case Clusters::LevelControl::Commands::MoveToLevel::Id: { - Clusters::LevelControl::Commands::MoveToLevel::Type moveToLevelCommand; - moveToLevelCommand.level = data->level; - Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, moveToLevelCommand, onSuccess, onFailure); - } - break; - case Clusters::ColorControl::Commands::MoveToColor::Id: { + case Clusters::LevelControl::Id: + if (commandId == Clusters::LevelControl::Commands::MoveToLevel::Id) + { + Clusters::LevelControl::Commands::MoveToLevel::Type moveToLevelCommand; + moveToLevelCommand.level = data->level; + Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, moveToLevelCommand, onSuccess, onFailure); + } + else + { + ChipLogError(NotSpecified, "Unsupported Command Id"); + } + break; - Clusters::ColorControl::Commands::MoveToColor::Type moveToColorCommand; + case Clusters::ColorControl::Id: + if (commandId == Clusters::ColorControl::Commands::MoveToColor::Id) + { + Clusters::ColorControl::Commands::MoveToColor::Type moveToColorCommand; + moveToColorCommand.colorX = data->colorXY.x; + moveToColorCommand.colorY = data->colorXY.y; + Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, moveToColorCommand, onSuccess, onFailure); + } + else + { + ChipLogError(NotSpecified, "Unsupported Command Id"); + } + break; - moveToColorCommand.colorX = data->colorXY.x; - moveToColorCommand.colorY = data->colorXY.y; - Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, moveToColorCommand, onSuccess, onFailure); + default: + ChipLogError(NotSpecified, "Unsupported Cluster Id"); + break; } - break; +} + +static void ProcessSwitchGroupBindingCommand(CommandId commandId, const EmberBindingTableEntry & binding, BindingCommandData * data) +{ + Messaging::ExchangeManager & exchangeMgr = Server::GetInstance().GetExchangeManager(); + + switch (data->clusterId) + { + case Clusters::OnOff::Id: + switch (commandId) + { + case Clusters::OnOff::Commands::Toggle::Id: + Clusters::OnOff::Commands::Toggle::Type toggleCommand; + Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, toggleCommand); + break; + case Clusters::OnOff::Commands::On::Id: + Clusters::OnOff::Commands::On::Type onCommand; + Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, onCommand); + + break; + case Clusters::OnOff::Commands::Off::Id: + Clusters::OnOff::Commands::Off::Type offCommand; + Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, offCommand); + break; + default: + ChipLogError(NotSpecified, "Unsupported Command Id"); + break; + } + break; + + case Clusters::LevelControl::Id: + if (commandId == Clusters::LevelControl::Commands::MoveToLevel::Id) + { + Clusters::LevelControl::Commands::MoveToLevel::Type moveToLevelCommand; + moveToLevelCommand.level = data->level; + Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, moveToLevelCommand); + } + else + { + ChipLogError(NotSpecified, "Unsupported Command Id"); + } + break; + + case Clusters::ColorControl::Id: + if (commandId == Clusters::ColorControl::Commands::MoveToColor::Id) + { + Clusters::ColorControl::Commands::MoveToColor::Type moveToColorCommand; + moveToColorCommand.colorX = data->colorXY.x; + moveToColorCommand.colorY = data->colorXY.y; + Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, moveToColorCommand); + } + else + { + ChipLogError(NotSpecified, "Unsupported Command Id"); + } + break; default: - ChipLogError(NotSpecified, "Unsupported Command Id"); + ChipLogError(NotSpecified, "Unsupported Cluster Id"); break; } } @@ -70,10 +159,20 @@ static void ProcessSwitchUnicastBindingCommand(CommandId commandId, const EmberB static void LightSwitchChangedHandler(const EmberBindingTableEntry & binding, OperationalDeviceProxy * peer_device, void * context) { VerifyOrReturn(context != nullptr, ChipLogError(NotSpecified, "nullptr pointer passed")); - BindingCommandData * data = static_cast(context); - if (binding.type == MATTER_UNICAST_BINDING) + if (binding.type == MATTER_MULTICAST_BINDING && data->isGroup) + { + switch (data->clusterId) + { + case Clusters::OnOff::Id: + case Clusters::LevelControl::Id: + case Clusters::ColorControl::Id: + ProcessSwitchGroupBindingCommand(data->commandId, binding, data); + break; + } + } + else if (binding.type == MATTER_UNICAST_BINDING && !data->isGroup) { switch (data->clusterId) { diff --git a/examples/light-switch-app/qpg/zap/switch.matter b/examples/light-switch-app/qpg/zap/switch.matter index fa848c35b526ac..73968414b217e9 100644 --- a/examples/light-switch-app/qpg/zap/switch.matter +++ b/examples/light-switch-app/qpg/zap/switch.matter @@ -755,6 +755,265 @@ cluster OtaSoftwareUpdateRequestor = 42 { command AnnounceOTAProvider(AnnounceOTAProviderRequest): DefaultSuccess = 0; } +/** This cluster is used to describe the configuration and capabilities of a physical power source that provides power to the Node. */ +cluster PowerSource = 47 { + revision 1; // NOTE: Default/not specifically set + + enum BatApprovedChemistryEnum : enum16 { + kUnspecified = 0; + kAlkaline = 1; + kLithiumCarbonFluoride = 2; + kLithiumChromiumOxide = 3; + kLithiumCopperOxide = 4; + kLithiumIronDisulfide = 5; + kLithiumManganeseDioxide = 6; + kLithiumThionylChloride = 7; + kMagnesium = 8; + kMercuryOxide = 9; + kNickelOxyhydride = 10; + kSilverOxide = 11; + kZincAir = 12; + kZincCarbon = 13; + kZincChloride = 14; + kZincManganeseDioxide = 15; + kLeadAcid = 16; + kLithiumCobaltOxide = 17; + kLithiumIon = 18; + kLithiumIonPolymer = 19; + kLithiumIronPhosphate = 20; + kLithiumSulfur = 21; + kLithiumTitanate = 22; + kNickelCadmium = 23; + kNickelHydrogen = 24; + kNickelIron = 25; + kNickelMetalHydride = 26; + kNickelZinc = 27; + kSilverZinc = 28; + kSodiumIon = 29; + kSodiumSulfur = 30; + kZincBromide = 31; + kZincCerium = 32; + } + + enum BatChargeFaultEnum : enum8 { + kUnspecified = 0; + kAmbientTooHot = 1; + kAmbientTooCold = 2; + kBatteryTooHot = 3; + kBatteryTooCold = 4; + kBatteryAbsent = 5; + kBatteryOverVoltage = 6; + kBatteryUnderVoltage = 7; + kChargerOverVoltage = 8; + kChargerUnderVoltage = 9; + kSafetyTimeout = 10; + } + + enum BatChargeLevelEnum : enum8 { + kOK = 0; + kWarning = 1; + kCritical = 2; + } + + enum BatChargeStateEnum : enum8 { + kUnknown = 0; + kIsCharging = 1; + kIsAtFullCharge = 2; + kIsNotCharging = 3; + } + + enum BatCommonDesignationEnum : enum16 { + kUnspecified = 0; + kAAA = 1; + kAA = 2; + kC = 3; + kD = 4; + k4v5 = 5; + k6v0 = 6; + k9v0 = 7; + k12AA = 8; + kAAAA = 9; + kA = 10; + kB = 11; + kF = 12; + kN = 13; + kNo6 = 14; + kSubC = 15; + kA23 = 16; + kA27 = 17; + kBA5800 = 18; + kDuplex = 19; + k4SR44 = 20; + k523 = 21; + k531 = 22; + k15v0 = 23; + k22v5 = 24; + k30v0 = 25; + k45v0 = 26; + k67v5 = 27; + kJ = 28; + kCR123A = 29; + kCR2 = 30; + k2CR5 = 31; + kCRP2 = 32; + kCRV3 = 33; + kSR41 = 34; + kSR43 = 35; + kSR44 = 36; + kSR45 = 37; + kSR48 = 38; + kSR54 = 39; + kSR55 = 40; + kSR57 = 41; + kSR58 = 42; + kSR59 = 43; + kSR60 = 44; + kSR63 = 45; + kSR64 = 46; + kSR65 = 47; + kSR66 = 48; + kSR67 = 49; + kSR68 = 50; + kSR69 = 51; + kSR516 = 52; + kSR731 = 53; + kSR712 = 54; + kLR932 = 55; + kA5 = 56; + kA10 = 57; + kA13 = 58; + kA312 = 59; + kA675 = 60; + kAC41E = 61; + k10180 = 62; + k10280 = 63; + k10440 = 64; + k14250 = 65; + k14430 = 66; + k14500 = 67; + k14650 = 68; + k15270 = 69; + k16340 = 70; + kRCR123A = 71; + k17500 = 72; + k17670 = 73; + k18350 = 74; + k18500 = 75; + k18650 = 76; + k19670 = 77; + k25500 = 78; + k26650 = 79; + k32600 = 80; + } + + enum BatFaultEnum : enum8 { + kUnspecified = 0; + kOverTemp = 1; + kUnderTemp = 2; + } + + enum BatReplaceabilityEnum : enum8 { + kUnspecified = 0; + kNotReplaceable = 1; + kUserReplaceable = 2; + kFactoryReplaceable = 3; + } + + enum PowerSourceStatusEnum : enum8 { + kUnspecified = 0; + kActive = 1; + kStandby = 2; + kUnavailable = 3; + } + + enum WiredCurrentTypeEnum : enum8 { + kAC = 0; + kDC = 1; + } + + enum WiredFaultEnum : enum8 { + kUnspecified = 0; + kOverVoltage = 1; + kUnderVoltage = 2; + } + + bitmap Feature : bitmap32 { + kWired = 0x1; + kBattery = 0x2; + kRechargeable = 0x4; + kReplaceable = 0x8; + } + + struct BatChargeFaultChangeType { + BatChargeFaultEnum current[] = 0; + BatChargeFaultEnum previous[] = 1; + } + + struct BatFaultChangeType { + BatFaultEnum current[] = 0; + BatFaultEnum previous[] = 1; + } + + struct WiredFaultChangeType { + WiredFaultEnum current[] = 0; + WiredFaultEnum previous[] = 1; + } + + info event WiredFaultChange = 0 { + WiredFaultEnum current[] = 0; + WiredFaultEnum previous[] = 1; + } + + info event BatFaultChange = 1 { + BatFaultEnum current[] = 0; + BatFaultEnum previous[] = 1; + } + + info event BatChargeFaultChange = 2 { + BatChargeFaultEnum current[] = 0; + BatChargeFaultEnum previous[] = 1; + } + + readonly attribute PowerSourceStatusEnum status = 0; + readonly attribute int8u order = 1; + readonly attribute char_string<60> description = 2; + readonly attribute optional nullable int32u wiredAssessedInputVoltage = 3; + readonly attribute optional nullable int16u wiredAssessedInputFrequency = 4; + readonly attribute optional WiredCurrentTypeEnum wiredCurrentType = 5; + readonly attribute optional nullable int32u wiredAssessedCurrent = 6; + readonly attribute optional int32u wiredNominalVoltage = 7; + readonly attribute optional int32u wiredMaximumCurrent = 8; + readonly attribute optional boolean wiredPresent = 9; + readonly attribute optional WiredFaultEnum activeWiredFaults[] = 10; + readonly attribute optional nullable int32u batVoltage = 11; + readonly attribute optional nullable int8u batPercentRemaining = 12; + readonly attribute optional nullable int32u batTimeRemaining = 13; + readonly attribute optional BatChargeLevelEnum batChargeLevel = 14; + readonly attribute optional boolean batReplacementNeeded = 15; + readonly attribute optional BatReplaceabilityEnum batReplaceability = 16; + readonly attribute optional boolean batPresent = 17; + readonly attribute optional BatFaultEnum activeBatFaults[] = 18; + readonly attribute optional char_string<60> batReplacementDescription = 19; + readonly attribute optional BatCommonDesignationEnum batCommonDesignation = 20; + readonly attribute optional char_string<20> batANSIDesignation = 21; + readonly attribute optional char_string<20> batIECDesignation = 22; + readonly attribute optional BatApprovedChemistryEnum batApprovedChemistry = 23; + readonly attribute optional int32u batCapacity = 24; + readonly attribute optional int8u batQuantity = 25; + readonly attribute optional BatChargeStateEnum batChargeState = 26; + readonly attribute optional nullable int32u batTimeToFullCharge = 27; + readonly attribute optional boolean batFunctionalWhileCharging = 28; + readonly attribute optional nullable int32u batChargingCurrent = 29; + readonly attribute optional BatChargeFaultEnum activeBatChargeFaults[] = 30; + readonly attribute endpoint_no endpointList[] = 31; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; +} + /** This cluster is used to manage global aspects of the Commissioning flow. */ cluster GeneralCommissioning = 48 { revision 1; // NOTE: Default/not specifically set @@ -2391,6 +2650,27 @@ endpoint 0 { handle command AnnounceOTAProvider; } + server cluster PowerSource { + ram attribute status; + ram attribute order; + ram attribute description; + ram attribute batVoltage; + ram attribute batPercentRemaining; + ram attribute batChargeLevel; + ram attribute batReplacementNeeded; + ram attribute batReplaceability; + ram attribute batReplacementDescription; + ram attribute batCommonDesignation; + ram attribute batQuantity; + callback attribute endpointList; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 10; + ram attribute clusterRevision default = 1; + } + server cluster GeneralCommissioning { ram attribute breadcrumb default = 0x0000000000000000; callback attribute basicCommissioningInfo; @@ -2675,7 +2955,6 @@ endpoint 1 { device type ma_colordimmerswitch = 261, version 1; binding cluster Identify; - binding cluster Groups; binding cluster OnOff; binding cluster LevelControl; binding cluster ScenesManagement; @@ -2695,6 +2974,26 @@ endpoint 1 { handle command TriggerEffect; } + server cluster Groups { + ram attribute nameSupport; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 4; + + handle command AddGroup; + handle command AddGroupResponse; + handle command ViewGroup; + handle command ViewGroupResponse; + handle command GetGroupMembership; + handle command GetGroupMembershipResponse; + handle command RemoveGroup; + handle command RemoveGroupResponse; + handle command RemoveAllGroups; + handle command AddGroupIfIdentifying; + } + server cluster Descriptor { callback attribute deviceTypeList; callback attribute serverList; @@ -2751,14 +3050,19 @@ endpoint 2 { server cluster Switch { emits event InitialPress; + emits event LongPress; emits event ShortRelease; + emits event LongRelease; + emits event MultiPressOngoing; + emits event MultiPressComplete; ram attribute numberOfPositions default = 2; ram attribute currentPosition default = 0; + ram attribute multiPressMax default = 2; callback attribute generatedCommandList; callback attribute acceptedCommandList; callback attribute eventList; callback attribute attributeList; - ram attribute featureMap default = 6; + ram attribute featureMap default = 30; ram attribute clusterRevision default = 1; } } diff --git a/examples/light-switch-app/qpg/zap/switch.zap b/examples/light-switch-app/qpg/zap/switch.zap index 7f00138fc26fbe..5578a0ff63b5e8 100644 --- a/examples/light-switch-app/qpg/zap/switch.zap +++ b/examples/light-switch-app/qpg/zap/switch.zap @@ -1185,6 +1185,304 @@ } ] }, + { + "name": "Power Source", + "code": 47, + "mfgCode": null, + "define": "POWER_SOURCE_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "Status", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "PowerSourceStatusEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "Order", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "Description", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatVoltage", + "code": 11, + "mfgCode": null, + "side": "server", + "type": "int32u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatPercentRemaining", + "code": 12, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatChargeLevel", + "code": 14, + "mfgCode": null, + "side": "server", + "type": "BatChargeLevelEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatReplacementNeeded", + "code": 15, + "mfgCode": null, + "side": "server", + "type": "boolean", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatReplaceability", + "code": 16, + "mfgCode": null, + "side": "server", + "type": "BatReplaceabilityEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatReplacementDescription", + "code": 19, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatCommonDesignation", + "code": 20, + "mfgCode": null, + "side": "server", + "type": "BatCommonDesignationEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatQuantity", + "code": 25, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EndpointList", + "code": 31, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EventList", + "code": 65530, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "10", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, { "name": "General Commissioning", "code": 48, @@ -4909,7 +5207,7 @@ "code": 4, "mfgCode": null, "define": "GROUPS_CLUSTER", - "side": "client", + "side": "server", "enabled": 1, "commands": [ { @@ -4917,7 +5215,7 @@ "code": 0, "mfgCode": null, "source": "client", - "isIncoming": 0, + "isIncoming": 1, "isEnabled": 1 }, { @@ -4925,7 +5223,7 @@ "code": 0, "mfgCode": null, "source": "server", - "isIncoming": 1, + "isIncoming": 0, "isEnabled": 1 }, { @@ -4933,7 +5231,7 @@ "code": 1, "mfgCode": null, "source": "client", - "isIncoming": 0, + "isIncoming": 1, "isEnabled": 1 }, { @@ -4941,7 +5239,7 @@ "code": 1, "mfgCode": null, "source": "server", - "isIncoming": 1, + "isIncoming": 0, "isEnabled": 1 }, { @@ -4949,7 +5247,7 @@ "code": 2, "mfgCode": null, "source": "client", - "isIncoming": 0, + "isIncoming": 1, "isEnabled": 1 }, { @@ -4957,7 +5255,7 @@ "code": 2, "mfgCode": null, "source": "server", - "isIncoming": 1, + "isIncoming": 0, "isEnabled": 1 }, { @@ -4965,7 +5263,7 @@ "code": 3, "mfgCode": null, "source": "client", - "isIncoming": 0, + "isIncoming": 1, "isEnabled": 1 }, { @@ -4973,7 +5271,7 @@ "code": 3, "mfgCode": null, "source": "server", - "isIncoming": 1, + "isIncoming": 0, "isEnabled": 1 }, { @@ -4981,7 +5279,7 @@ "code": 4, "mfgCode": null, "source": "client", - "isIncoming": 0, + "isIncoming": 1, "isEnabled": 1 }, { @@ -4989,16 +5287,80 @@ "code": 5, "mfgCode": null, "source": "client", - "isIncoming": 0, + "isIncoming": 1, "isEnabled": 1 } ], "attributes": [ + { + "name": "NameSupport", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "NameSupportBitmap", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "FeatureMap", "code": 65532, "mfgCode": null, - "side": "client", + "side": "server", "type": "bitmap32", "included": 1, "storageOption": "RAM", @@ -5014,7 +5376,7 @@ "name": "ClusterRevision", "code": 65533, "mfgCode": null, - "side": "client", + "side": "server", "type": "int16u", "included": 1, "storageOption": "RAM", @@ -5022,9 +5384,9 @@ "bounded": 0, "defaultValue": "4", "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": null + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 } ] }, @@ -6020,6 +6382,22 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "MultiPressMax", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "2", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "GeneratedCommandList", "code": 65528, @@ -6094,7 +6472,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "6", + "defaultValue": "30", "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -6125,12 +6503,40 @@ "side": "server", "included": 1 }, + { + "name": "LongPress", + "code": 2, + "mfgCode": null, + "side": "server", + "included": 1 + }, { "name": "ShortRelease", "code": 3, "mfgCode": null, "side": "server", "included": 1 + }, + { + "name": "LongRelease", + "code": 4, + "mfgCode": null, + "side": "server", + "included": 1 + }, + { + "name": "MultiPressOngoing", + "code": 5, + "mfgCode": null, + "side": "server", + "included": 1 + }, + { + "name": "MultiPressComplete", + "code": 6, + "mfgCode": null, + "side": "server", + "included": 1 } ] } diff --git a/examples/lighting-app/qpg/src/AppTask.cpp b/examples/lighting-app/qpg/src/AppTask.cpp index d870ea24deeb13..90f2e30ecb03c5 100644 --- a/examples/lighting-app/qpg/src/AppTask.cpp +++ b/examples/lighting-app/qpg/src/AppTask.cpp @@ -69,7 +69,8 @@ using namespace ::chip::DeviceLayer; #define APP_TASK_PRIORITY 2 #define APP_EVENT_QUEUE_SIZE 10 #define QPG_LIGHT_ENDPOINT_ID (1) -#define TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS (1 * 3600) // this value must be multiplication of 3600 +#define SECONDS_IN_HOUR (3600) // we better keep this 3600 +#define TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS (1 * SECONDS_IN_HOUR) // increment every hour static uint8_t countdown = 0; @@ -454,10 +455,18 @@ void AppTask::TotalHoursTimerHandler(chip::System::Layer * aLayer, void * aAppSt CHIP_ERROR err; uint32_t totalOperationalHours = 0; - if (ConfigurationMgr().GetTotalOperationalHours(totalOperationalHours) == CHIP_NO_ERROR) + err = ConfigurationMgr().GetTotalOperationalHours(totalOperationalHours); + + if (err == CHIP_NO_ERROR) + { + ConfigurationMgr().StoreTotalOperationalHours(totalOperationalHours + + (TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS / SECONDS_IN_HOUR)); + } + else if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) { + totalOperationalHours = 0; // set this explicitly to 0 for safety ConfigurationMgr().StoreTotalOperationalHours(totalOperationalHours + - (TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS / 3600)); + (TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS / SECONDS_IN_HOUR)); } else { diff --git a/examples/lock-app/qpg/src/AppTask.cpp b/examples/lock-app/qpg/src/AppTask.cpp index d2f3298db9b779..8ae517dce2b7ad 100644 --- a/examples/lock-app/qpg/src/AppTask.cpp +++ b/examples/lock-app/qpg/src/AppTask.cpp @@ -64,7 +64,8 @@ using namespace ::chip::DeviceLayer; #define APP_TASK_PRIORITY 2 #define APP_EVENT_QUEUE_SIZE 10 #define QPG_LOCK_ENDPOINT_ID (1) -#define TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS (1 * 3600) // this value must be multiplication of 3600 +#define SECONDS_IN_HOUR (3600) // we better keep this 3600 +#define TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS (1 * SECONDS_IN_HOUR) // increment every hour #define NMBR_OF_RESETS_BLE_ADVERTISING (3) @@ -414,10 +415,18 @@ void AppTask::TotalHoursTimerHandler(chip::System::Layer * aLayer, void * aAppSt CHIP_ERROR err; uint32_t totalOperationalHours = 0; - if (ConfigurationMgr().GetTotalOperationalHours(totalOperationalHours) == CHIP_NO_ERROR) + err = ConfigurationMgr().GetTotalOperationalHours(totalOperationalHours); + + if (err == CHIP_NO_ERROR) + { + ConfigurationMgr().StoreTotalOperationalHours(totalOperationalHours + + (TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS / SECONDS_IN_HOUR)); + } + else if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) { + totalOperationalHours = 0; // set this explicitly to 0 for safety ConfigurationMgr().StoreTotalOperationalHours(totalOperationalHours + - (TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS / 3600)); + (TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS / SECONDS_IN_HOUR)); } else { diff --git a/examples/thermostat/qpg/include/AppTask.h b/examples/thermostat/qpg/include/AppTask.h index 557f83d08636c9..2d2caac653aabc 100644 --- a/examples/thermostat/qpg/include/AppTask.h +++ b/examples/thermostat/qpg/include/AppTask.h @@ -61,6 +61,7 @@ class AppTask static void FunctionTimerEventHandler(AppEvent * aEvent); static void FunctionHandler(AppEvent * aEvent); static void TimerEventHandler(chip::System::Layer * aLayer, void * aAppState); + static void TotalHoursTimerHandler(chip::System::Layer * aLayer, void * aAppState); static void MatterEventHandler(const chip::DeviceLayer::ChipDeviceEvent * event, intptr_t arg); static void UpdateLEDs(void); diff --git a/examples/thermostat/qpg/src/AppTask.cpp b/examples/thermostat/qpg/src/AppTask.cpp index 5bd1e9cc29ad9a..306661766a4af4 100644 --- a/examples/thermostat/qpg/src/AppTask.cpp +++ b/examples/thermostat/qpg/src/AppTask.cpp @@ -54,6 +54,8 @@ using namespace ::chip::DeviceLayer; #define APP_TASK_STACK_SIZE (2 * 1024) #define APP_TASK_PRIORITY 2 #define APP_EVENT_QUEUE_SIZE 10 +#define SECONDS_IN_HOUR (3600) // we better keep this 3600 +#define TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS (1 * SECONDS_IN_HOUR) // increment every hour namespace { TaskHandle_t sAppTaskHandle; @@ -233,6 +235,14 @@ CHIP_ERROR AppTask::Init() sIsBLEAdvertisingEnabled = ConnectivityMgr().IsBLEAdvertisingEnabled(); UpdateLEDs(); + err = chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds32(TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS), + TotalHoursTimerHandler, this); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "StartTimer failed %s: ", chip::ErrorStr(err)); + } + return err; } @@ -312,6 +322,40 @@ void AppTask::TimerEventHandler(chip::System::Layer * aLayer, void * aAppState) sAppTask.PostEvent(&event); } +void AppTask::TotalHoursTimerHandler(chip::System::Layer * aLayer, void * aAppState) +{ + ChipLogProgress(NotSpecified, "HourlyTimer"); + + CHIP_ERROR err; + uint32_t totalOperationalHours = 0; + + err = ConfigurationMgr().GetTotalOperationalHours(totalOperationalHours); + + if (err == CHIP_NO_ERROR) + { + ConfigurationMgr().StoreTotalOperationalHours(totalOperationalHours + + (TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS / SECONDS_IN_HOUR)); + } + else if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) + { + totalOperationalHours = 0; // set this explicitly to 0 for safety + ConfigurationMgr().StoreTotalOperationalHours(totalOperationalHours + + (TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS / SECONDS_IN_HOUR)); + } + else + { + ChipLogError(DeviceLayer, "Failed to get total operational hours of the Node"); + } + + err = chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds32(TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS), + TotalHoursTimerHandler, nullptr); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "StartTimer failed %s: ", chip::ErrorStr(err)); + } +} + void AppTask::FunctionTimerEventHandler(AppEvent * aEvent) { if (aEvent->Type != AppEvent::kEventType_Timer) From c91a779c5e937b1aabcd55a072f9268452f85a63 Mon Sep 17 00:00:00 2001 From: Tennessee Carmel-Veilleux Date: Thu, 25 Jul 2024 10:32:04 -0400 Subject: [PATCH 09/49] Implement TC-SWTCH-2.4 and all-clusters button simulator (#34406) * Implement TC-SWTCH-2.4 and all-clusters button simulator - Implement TC-SWTCH-2.4 - Implement action switch button simulator to drive the test Testing done: - New test (TC-SWTCH-2.4) works using the button simulator * Restyled by clang-format * Restyled by autopep8 * Restyled by isort * Enable TC_SWTCH.py in CI * Fix lint * Fix typo that arose on file saving * Handle EOF in reading user input in matter testing support * Do not provide a stdin on python test runner and handle EOF on matter_testing_support * Fix up PICS execution for CI --------- Co-authored-by: Restyled.io Co-authored-by: Andrei Litvin --- .github/workflows/tests.yaml | 1 + .../linux/AllClustersCommandDelegate.cpp | 151 +++++++++ examples/all-clusters-app/linux/BUILD.gn | 3 + .../linux/ButtonEventsSimulator.cpp | 210 +++++++++++++ .../linux/ButtonEventsSimulator.h | 143 +++++++++ scripts/tests/run_python_test.py | 6 +- src/python_testing/TC_SWTCH.py | 290 ++++++++++++++++++ src/python_testing/matter_testing_support.py | 74 ++++- 8 files changed, 865 insertions(+), 13 deletions(-) create mode 100644 examples/all-clusters-app/linux/ButtonEventsSimulator.cpp create mode 100644 examples/all-clusters-app/linux/ButtonEventsSimulator.h create mode 100644 src/python_testing/TC_SWTCH.py diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index d559612d2be60d..182a3bb3a9fadd 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -577,6 +577,7 @@ jobs: scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_RVCOPSTATE_2_3.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_RVCOPSTATE_2_4.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_SC_7_1.py' + scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_SWTCH.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --script "src/python_testing/TestConformanceSupport.py" --script-args "--trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --script "src/python_testing/TestMatterTestingSupport.py" --script-args "--trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --script "src/python_testing/TestSpecParsingSupport.py" --script-args "--trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"' diff --git a/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp b/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp index 8effe6426cc49d..3c42eb1dd6a32e 100644 --- a/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp +++ b/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp @@ -28,6 +28,7 @@ #include #include +#include "ButtonEventsSimulator.h" #include #include #include @@ -36,13 +37,155 @@ #include #include +#include #include +#include using namespace chip; using namespace chip::app; using namespace chip::app::Clusters; using namespace chip::DeviceLayer; +namespace { + +std::unique_ptr sButtonSimulatorInstance{ nullptr }; + +bool HasNumericField(Json::Value & jsonValue, const std::string & field) +{ + return jsonValue.isMember(field) && jsonValue[field].isNumeric(); +} + +/** + * Named pipe handler for simulated long press on an action switch. + * + * Usage example: + * echo '{"Name": "SimulateActionSwitchLongPress", "EndpointId": 3, "ButtonId": 1, "LongPressDelayMillis": 800, + * "LongPressDurationMillis": 1000}' > /tmp/chip_all_clusters_fifo_1146610 + * + * JSON Arguments: + * - "Name": Must be "SimulateActionSwitchLongPress" + * - "EndpointId": number of endpoint having a switch cluster + * - "ButtonId": switch position in the switch cluster for "down" button (not idle) + * - "LongPressDelayMillis": Time in milliseconds before the LongPress + * - "LongPressDurationMillis": Total duration in milliseconds from start of the press to LongRelease + * + * @param jsonValue - JSON payload from named pipe + */ +void HandleSimulateActionSwitchLongPress(Json::Value & jsonValue) +{ + if (sButtonSimulatorInstance != nullptr) + { + ChipLogError(NotSpecified, "Button simulation already in progress! Ignoring request."); + return; + } + + bool hasEndpointId = HasNumericField(jsonValue, "EndpointId"); + bool hasButtonId = HasNumericField(jsonValue, "ButtonId"); + bool hasLongPressDelayMillis = HasNumericField(jsonValue, "LongPressDelayMillis"); + bool hasLongPressDurationMillis = HasNumericField(jsonValue, "LongPressDurationMillis"); + if (!hasEndpointId || !hasButtonId || !hasLongPressDelayMillis || !hasLongPressDurationMillis) + { + std::string inputJson = jsonValue.toStyledString(); + ChipLogError( + NotSpecified, + "Missing or invalid value for one of EndpointId, ButtonId, LongPressDelayMillis or LongPressDurationMillis in %s", + inputJson.c_str()); + return; + } + + EndpointId endpointId = static_cast(jsonValue["EndpointId"].asUInt()); + uint8_t buttonId = static_cast(jsonValue["ButtonId"].asUInt()); + System::Clock::Milliseconds32 longPressDelayMillis{ static_cast(jsonValue["LongPressDelayMillis"].asUInt()) }; + System::Clock::Milliseconds32 longPressDurationMillis{ static_cast(jsonValue["LongPressDurationMillis"].asUInt()) }; + auto buttonSimulator = std::make_unique(); + + bool success = buttonSimulator->SetMode(ButtonEventsSimulator::Mode::kModeLongPress) + .SetLongPressDelayMillis(longPressDelayMillis) + .SetLongPressDurationMillis(longPressDurationMillis) + .SetIdleButtonId(0) + .SetPressedButtonId(buttonId) + .SetEndpointId(endpointId) + .Execute([]() { sButtonSimulatorInstance.reset(); }); + + if (!success) + { + ChipLogError(NotSpecified, "Failed to start execution of button simulator!"); + return; + } + + sButtonSimulatorInstance = std::move(buttonSimulator); +} + +/** + * Named pipe handler for simulated multi-press on an action switch. + * + * Usage example: + * echo '{"Name": "SimulateActionSwitchMultiPress", "EndpointId": 3, "ButtonId": 1, "MultiPressPressedTimeMillis": 100, + * "MultiPressReleasedTimeMillis": 350, "MultiPressNumPresses": 2}' > /tmp/chip_all_clusters_fifo_1146610 + * + * JSON Arguments: + * - "Name": Must be "SimulateActionSwitchMultiPress" + * - "EndpointId": number of endpoint having a switch cluster + * - "ButtonId": switch position in the switch cluster for "down" button (not idle) + * - "MultiPressPressedTimeMillis": Pressed time in milliseconds for each press + * - "MultiPressReleasedTimeMillis": Released time in milliseconds after each press + * - "MultiPressNumPresses": Number of presses to simulate + * + * @param jsonValue - JSON payload from named pipe + */ +void HandleSimulateActionSwitchMultiPress(Json::Value & jsonValue) +{ + if (sButtonSimulatorInstance != nullptr) + { + ChipLogError(NotSpecified, "Button simulation already in progress! Ignoring request."); + return; + } + + bool hasEndpointId = HasNumericField(jsonValue, "EndpointId"); + bool hasButtonId = HasNumericField(jsonValue, "ButtonId"); + bool hasMultiPressPressedTimeMillis = HasNumericField(jsonValue, "MultiPressPressedTimeMillis"); + bool hasMultiPressReleasedTimeMillis = HasNumericField(jsonValue, "MultiPressReleasedTimeMillis"); + bool hasMultiPressNumPresses = HasNumericField(jsonValue, "MultiPressNumPresses"); + if (!hasEndpointId || !hasButtonId || !hasMultiPressPressedTimeMillis || !hasMultiPressReleasedTimeMillis || + !hasMultiPressNumPresses) + { + std::string inputJson = jsonValue.toStyledString(); + ChipLogError(NotSpecified, + "Missing or invalid value for one of EndpointId, ButtonId, MultiPressPressedTimeMillis, " + "MultiPressReleasedTimeMillis or MultiPressNumPresses in %s", + inputJson.c_str()); + return; + } + + EndpointId endpointId = static_cast(jsonValue["EndpointId"].asUInt()); + uint8_t buttonId = static_cast(jsonValue["ButtonId"].asUInt()); + System::Clock::Milliseconds32 multiPressPressedTimeMillis{ static_cast( + jsonValue["MultiPressPressedTimeMillis"].asUInt()) }; + System::Clock::Milliseconds32 multiPressReleasedTimeMillis{ static_cast( + jsonValue["MultiPressReleasedTimeMillis"].asUInt()) }; + uint8_t multiPressNumPresses = static_cast(jsonValue["MultiPressNumPresses"].asUInt()); + auto buttonSimulator = std::make_unique(); + + bool success = buttonSimulator->SetMode(ButtonEventsSimulator::Mode::kModeMultiPress) + .SetMultiPressPressedTimeMillis(multiPressPressedTimeMillis) + .SetMultiPressReleasedTimeMillis(multiPressReleasedTimeMillis) + .SetMultiPressNumPresses(multiPressNumPresses) + .SetIdleButtonId(0) + .SetPressedButtonId(buttonId) + .SetEndpointId(endpointId) + .Execute([]() { sButtonSimulatorInstance.reset(); }); + + if (!success) + { + ChipLogError(NotSpecified, "Failed to start execution of button simulator!"); + return; + } + + sButtonSimulatorInstance = std::move(buttonSimulator); +} + +} // namespace + AllClustersAppCommandHandler * AllClustersAppCommandHandler::FromJSON(const char * json) { Json::Reader reader; @@ -190,6 +333,14 @@ void AllClustersAppCommandHandler::HandleCommand(intptr_t context) std::string operation = self->mJsonValue["Operation"].asString(); self->OnOperationalStateChange(device, operation, self->mJsonValue["Param"]); } + else if (name == "SimulateActionSwitchLongPress") + { + HandleSimulateActionSwitchLongPress(self->mJsonValue); + } + else if (name == "SimulateActionSwitchMultiPress") + { + HandleSimulateActionSwitchMultiPress(self->mJsonValue); + } else { ChipLogError(NotSpecified, "Unhandled command: Should never happens"); diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index 3d52ef748de90d..a2f4ff0ab1f781 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -66,7 +66,10 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/energy-management-app/energy-management-common/src/device-energy-management-mode.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/energy-evse-mode.cpp", "AllClustersCommandDelegate.cpp", + "AllClustersCommandDelegate.h", "AppOptions.cpp", + "ButtonEventsSimulator.cpp", + "ButtonEventsSimulator.h", "ValveControlDelegate.cpp", "WindowCoveringManager.cpp", "include/tv-callbacks.cpp", diff --git a/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp b/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp new file mode 100644 index 00000000000000..44bf5657f5c2f6 --- /dev/null +++ b/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp @@ -0,0 +1,210 @@ +/* + * + * 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 "ButtonEventsSimulator.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { + +namespace { + +void SetButtonPosition(EndpointId endpointId, uint8_t position) +{ + Clusters::Switch::Attributes::CurrentPosition::Set(endpointId, position); +} + +void EmitInitialPress(EndpointId endpointId, uint8_t newPosition) +{ + Clusters::Switch::Events::InitialPress::Type event{ newPosition }; + EventNumber eventNumber = 0; + + CHIP_ERROR err = LogEvent(event, endpointId, eventNumber); + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to log InitialPress event: %" CHIP_ERROR_FORMAT, err.Format()); + } + else + { + ChipLogProgress(NotSpecified, "Logged InitialPress(%u) on Endpoint %u", static_cast(newPosition), + static_cast(endpointId)); + } +} + +void EmitLongPress(EndpointId endpointId, uint8_t newPosition) +{ + Clusters::Switch::Events::LongPress::Type event{ newPosition }; + EventNumber eventNumber = 0; + + CHIP_ERROR err = LogEvent(event, endpointId, eventNumber); + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to log LongPress event: %" CHIP_ERROR_FORMAT, err.Format()); + } + else + { + ChipLogProgress(NotSpecified, "Logged LongPress(%u) on Endpoint %u", static_cast(newPosition), + static_cast(endpointId)); + } +} + +void EmitLongRelease(EndpointId endpointId, uint8_t previousPosition) +{ + Clusters::Switch::Events::LongRelease::Type event{}; + event.previousPosition = previousPosition; + EventNumber eventNumber = 0; + + CHIP_ERROR err = LogEvent(event, endpointId, eventNumber); + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to log LongRelease event: %" CHIP_ERROR_FORMAT, err.Format()); + } + else + { + ChipLogProgress(NotSpecified, "Logged LongRelease on Endpoint %u", static_cast(endpointId)); + } +} + +void EmitMultiPressComplete(EndpointId endpointId, uint8_t previousPosition, uint8_t count) +{ + Clusters::Switch::Events::MultiPressComplete::Type event{}; + event.previousPosition = previousPosition; + event.totalNumberOfPressesCounted = count; + EventNumber eventNumber = 0; + + CHIP_ERROR err = LogEvent(event, endpointId, eventNumber); + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to log MultiPressComplete event: %" CHIP_ERROR_FORMAT, err.Format()); + } + else + { + ChipLogProgress(NotSpecified, "Logged MultiPressComplete(count=%u) on Endpoint %u", static_cast(count), + static_cast(endpointId)); + } +} + +} // namespace + +void ButtonEventsSimulator::OnTimerDone(System::Layer * layer, void * appState) +{ + ButtonEventsSimulator * that = reinterpret_cast(appState); + that->Next(); +} + +bool ButtonEventsSimulator::Execute(DoneCallback && doneCallback) +{ + VerifyOrReturnValue(mIdleButtonId != mPressedButtonId, false); + + switch (mMode) + { + case Mode::kModeLongPress: + VerifyOrReturnValue(mLongPressDurationMillis > mLongPressDelayMillis, false); + SetState(State::kEmitStartOfLongPress); + break; + case Mode::kModeMultiPress: + VerifyOrReturnValue(mMultiPressPressedTimeMillis.count() > 0, false); + VerifyOrReturnValue(mMultiPressReleasedTimeMillis.count() > 0, false); + VerifyOrReturnValue(mMultiPressNumPresses > 0, false); + SetState(State::kEmitStartOfMultiPress); + break; + default: + return false; + } + mDoneCallback = std::move(doneCallback); + Next(); + return true; +} + +void ButtonEventsSimulator::SetState(ButtonEventsSimulator::State newState) +{ + ButtonEventsSimulator::State oldState = mState; + if (oldState != newState) + { + ChipLogProgress(NotSpecified, "ButtonEventsSimulator state change %u -> %u", static_cast(oldState), + static_cast(newState)); + } + + mState = newState; +} + +void ButtonEventsSimulator::StartTimer(System::Clock::Timeout duration) +{ + chip::DeviceLayer::SystemLayer().StartTimer(duration, &ButtonEventsSimulator::OnTimerDone, this); +} + +void ButtonEventsSimulator::Next() +{ + switch (mState) + { + case ButtonEventsSimulator::State::kIdle: { + ChipLogError(NotSpecified, "Found idle state where not expected!"); + break; + } + case ButtonEventsSimulator::State::kEmitStartOfLongPress: { + SetButtonPosition(mEndpointId, mPressedButtonId); + EmitInitialPress(mEndpointId, mPressedButtonId); + SetState(ButtonEventsSimulator::State::kEmitLongPress); + StartTimer(mLongPressDelayMillis); + break; + } + case ButtonEventsSimulator::State::kEmitLongPress: { + EmitLongPress(mEndpointId, mPressedButtonId); + SetState(ButtonEventsSimulator::State::kEmitLongRelease); + StartTimer(mLongPressDurationMillis - mLongPressDelayMillis); + break; + } + case ButtonEventsSimulator::State::kEmitLongRelease: { + SetButtonPosition(mEndpointId, mIdleButtonId); + EmitLongRelease(mEndpointId, mPressedButtonId); + SetState(ButtonEventsSimulator::State::kIdle); + mDoneCallback(); + break; + } + case ButtonEventsSimulator::State::kEmitStartOfMultiPress: { + EmitInitialPress(mEndpointId, mPressedButtonId); + StartTimer(mMultiPressNumPresses * (mMultiPressPressedTimeMillis + mMultiPressReleasedTimeMillis)); + SetState(ButtonEventsSimulator::State::kEmitEndOfMultiPress); + break; + } + case ButtonEventsSimulator::State::kEmitEndOfMultiPress: { + EmitMultiPressComplete(mEndpointId, mPressedButtonId, mMultiPressNumPresses); + SetState(ButtonEventsSimulator::State::kIdle); + mDoneCallback(); + break; + } + } +} + +} // namespace app +} // namespace chip diff --git a/examples/all-clusters-app/linux/ButtonEventsSimulator.h b/examples/all-clusters-app/linux/ButtonEventsSimulator.h new file mode 100644 index 00000000000000..658da98f14fefd --- /dev/null +++ b/examples/all-clusters-app/linux/ButtonEventsSimulator.h @@ -0,0 +1,143 @@ +/* + * + * 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. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace chip { +namespace app { + +/** + * State machine to emit button sequences. Configure with `SetXxx()` methods + * and then call `Execute()` with a functor to be called when done. + * + * The implementation has dependencies on SystemLayer (to start timers) and on + * EventLogging. + * + */ +class ButtonEventsSimulator +{ +public: + enum class Mode + { + kModeLongPress, + kModeMultiPress + }; + + using DoneCallback = std::function; + + ButtonEventsSimulator() = default; + + // Returns true on success to start execution, false on something going awry. + // `doneCallback` is called only if execution got started. + bool Execute(DoneCallback && doneCallback); + + ButtonEventsSimulator & SetLongPressDelayMillis(System::Clock::Milliseconds32 longPressDelayMillis) + { + mLongPressDelayMillis = longPressDelayMillis; + return *this; + } + + ButtonEventsSimulator & SetLongPressDurationMillis(System::Clock::Milliseconds32 longPressDurationMillis) + { + mLongPressDurationMillis = longPressDurationMillis; + return *this; + } + + ButtonEventsSimulator & SetMultiPressPressedTimeMillis(System::Clock::Milliseconds32 multiPressPressedTimeMillis) + { + mMultiPressPressedTimeMillis = multiPressPressedTimeMillis; + return *this; + } + + ButtonEventsSimulator & SetMultiPressReleasedTimeMillis(System::Clock::Milliseconds32 multiPressReleasedTimeMillis) + { + mMultiPressReleasedTimeMillis = multiPressReleasedTimeMillis; + return *this; + } + + ButtonEventsSimulator & SetMultiPressNumPresses(uint8_t multiPressNumPresses) + { + mMultiPressNumPresses = multiPressNumPresses; + return *this; + } + + ButtonEventsSimulator & SetIdleButtonId(uint8_t idleButtonId) + { + mIdleButtonId = idleButtonId; + return *this; + } + + ButtonEventsSimulator & SetPressedButtonId(uint8_t pressedButtonId) + { + mPressedButtonId = pressedButtonId; + return *this; + } + + ButtonEventsSimulator & SetMode(Mode mode) + { + mMode = mode; + return *this; + } + + ButtonEventsSimulator & SetEndpointId(EndpointId endpointId) + { + mEndpointId = endpointId; + return *this; + } + +private: + enum class State + { + kIdle = 0, + + kEmitStartOfLongPress = 1, + kEmitLongPress = 2, + kEmitLongRelease = 3, + + kEmitStartOfMultiPress = 4, + kEmitEndOfMultiPress = 5, + }; + + static void OnTimerDone(System::Layer * layer, void * appState); + void SetState(State newState); + void Next(); + void StartTimer(System::Clock::Timeout duration); + + DoneCallback mDoneCallback; + System::Clock::Milliseconds32 mLongPressDelayMillis{}; + System::Clock::Milliseconds32 mLongPressDurationMillis{}; + System::Clock::Milliseconds32 mMultiPressPressedTimeMillis{}; + System::Clock::Milliseconds32 mMultiPressReleasedTimeMillis{}; + uint8_t mMultiPressNumPresses{ 1 }; + uint8_t mIdleButtonId{ 0 }; + uint8_t mPressedButtonId{ 1 }; + EndpointId mEndpointId{ 1 }; + + Mode mMode{ Mode::kModeLongPress }; + State mState{ State::kIdle }; +}; + +} // namespace app +} // namespace chip diff --git a/scripts/tests/run_python_test.py b/scripts/tests/run_python_test.py index 7d7500c10f5a11..91ee73e00d0e8d 100755 --- a/scripts/tests/run_python_test.py +++ b/scripts/tests/run_python_test.py @@ -169,7 +169,8 @@ def main_impl(app: str, factoryreset: bool, factoryreset_app_only: bool, app_arg app_args = [app] + shlex.split(app_args) logging.info(f"Execute: {app_args}") app_process = subprocess.Popen( - app_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0) + app_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, bufsize=0) + app_process.stdin.close() app_pid = app_process.pid DumpProgramOutputToQueue( log_cooking_threads, Fore.GREEN + "APP " + Style.RESET_ALL, app_process, stream_output, log_queue) @@ -192,7 +193,8 @@ def main_impl(app: str, factoryreset: bool, factoryreset_app_only: bool, app_arg logging.info(f"Execute: {final_script_command}") test_script_process = subprocess.Popen( - final_script_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + final_script_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + test_script_process.stdin.close() DumpProgramOutputToQueue(log_cooking_threads, Fore.GREEN + "TEST" + Style.RESET_ALL, test_script_process, stream_output, log_queue) diff --git a/src/python_testing/TC_SWTCH.py b/src/python_testing/TC_SWTCH.py new file mode 100644 index 00000000000000..867897ec54ec9d --- /dev/null +++ b/src/python_testing/TC_SWTCH.py @@ -0,0 +1,290 @@ +# +# 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. + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${ALL_CLUSTERS_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --endpoint 3 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto --PICS src/app/tests/suites/certification/ci-pics-values +# === END CI TEST ARGUMENTS === + +import json +import logging +import queue +import time +from typing import Any + +import chip.clusters as Clusters +from chip.clusters import ClusterObjects as ClusterObjects +from chip.clusters.Attribute import EventReadResult, TypedAttributePath +from matter_testing_support import (AttributeValue, ClusterAttributeChangeAccumulator, EventChangeCallback, MatterBaseTest, + async_test_body, default_matter_test_main) +from mobly import asserts + +logger = logging.getLogger(__name__) + + +class TC_SwitchTests(MatterBaseTest): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def desc_TC_SWTCH_2_4(self) -> str: + """Returns a description of this test""" + return "[TC-SWTCH-2.4] Momentary Switch Long Press Verification" + + def pics_TC_SWTCH_2_4(self): + """ This function returns a list of PICS for this test case that must be True for the test to be run""" + return ["SWTCH.S", "SWTCH.S.F01"] + + # def steps_TC_SWTCH_2_4(self) -> list[TestStep]: + # steps = [ + # TestStep("0", "Commissioning, already done", is_commissioning=True), + # # TODO: fill when test is done + # ] + + # return steps + + def _send_named_pipe_command(self, command_dict: dict[str, Any]): + app_pid = self.matter_test_config.app_pid + if app_pid == 0: + asserts.fail("The --app-pid flag must be set when usage of button simulation named pipe is required (e.g. CI)") + + app_pipe = f"/tmp/chip_all_clusters_fifo_{app_pid}" + command = json.dumps(command_dict) + + # Sends an out-of-band command to the sample app + with open(app_pipe, "w") as outfile: + logging.info(f"Sending named pipe command to {app_pipe}: '{command}'") + outfile.write(command + "\n") + # Delay for pipe command to be processed (otherwise tests may be flaky). + time.sleep(0.1) + + def _use_button_simulator(self) -> bool: + return self.check_pics("PICS_SDK_CI_ONLY") or self.user_params.get("use_button_simulator", False) + + def _ask_for_switch_idle(self): + if not self._use_button_simulator(): + self.wait_for_user_input(prompt_msg="Ensure switch is idle") + + def _ask_for_long_press(self, endpoint_id: int, pressed_position: int): + if not self._use_button_simulator(): + self.wait_for_user_input( + prompt_msg=f"Press switch position {pressed_position} for a long time (around 5 seconds) on the DUT, then release it.") + else: + command_dict = {"Name": "SimulateActionSwitchLongPress", "EndpointId": endpoint_id, + "ButtonId": pressed_position, "LongPressDelayMillis": 5000, "LongPressDurationMillis": 5500} + self._send_named_pipe_command(command_dict) + + def _placeholder_for_step(self, step_id: str): + # TODO: Global search an replace of `self._placeholder_for_step` with `self.step` when done. + logging.info(f"Step {step_id}") + pass + + def _placeholder_for_skip(self, step_id: str): + logging.info(f"Skipped step {step_id}") + + def _await_sequence_of_reports(self, report_queue: queue.Queue, endpoint_id: int, attribute: TypedAttributePath, sequence: list[Any], timeout_sec: float): + start_time = time.time() + elapsed = 0.0 + time_remaining = timeout_sec + + sequence_idx = 0 + actual_values = [] + + while time_remaining > 0: + expected_value = sequence[sequence_idx] + logging.info(f"Expecting value {expected_value} for attribute {attribute} on endpoint {endpoint_id}") + try: + item: AttributeValue = report_queue.get(block=True, timeout=time_remaining) + + # Track arrival of all values for the given attribute. + if item.endpoint_id == endpoint_id and item.attribute == attribute: + actual_values.append(item.value) + + if item.value == expected_value: + logging.info(f"Got expected attribute change {sequence_idx+1}/{len(sequence)} for attribute {attribute}") + sequence_idx += 1 + else: + asserts.assert_equal(item.value, expected_value, + msg="Did not get expected attribute value in correct sequence.") + + # We are done waiting when we have accumulated all results. + if sequence_idx == len(sequence): + logging.info("Got all attribute changes, done waiting.") + return + except queue.Empty: + # No error, we update timeouts and keep going + pass + + elapsed = time.time() - start_time + time_remaining = timeout_sec - elapsed + + asserts.fail(f"Did not get full sequence {sequence} in {timeout_sec:.1f} seconds. Got {actual_values} before time-out.") + + def _await_sequence_of_events(self, event_queue: queue.Queue, endpoint_id: int, sequence: list[ClusterObjects.ClusterEvent], timeout_sec: float): + start_time = time.time() + elapsed = 0.0 + time_remaining = timeout_sec + + sequence_idx = 0 + actual_events = [] + + while time_remaining > 0: + logging.info(f"Expecting event {sequence[sequence_idx]} on endpoint {endpoint_id}") + try: + item: EventReadResult = event_queue.get(block=True, timeout=time_remaining) + expected_event = sequence[sequence_idx] + event_data = item.Data + + if item.Header.EndpointId == endpoint_id and item.Header.ClusterId == event_data.cluster_id: + actual_events.append(event_data) + + if event_data == expected_event: + logging.info(f"Got expected Event {sequence_idx+1}/{len(sequence)}: {event_data}") + sequence_idx += 1 + else: + asserts.assert_equal(event_data, expected_event, msg="Did not get expected event in correct sequence.") + + # We are done waiting when we have accumulated all results. + if sequence_idx == len(sequence): + logging.info("Got all expected events, done waiting.") + return + except queue.Empty: + # No error, we update timeouts and keep going + pass + + elapsed = time.time() - start_time + time_remaining = timeout_sec - elapsed + + asserts.fail(f"Did not get full sequence {sequence} in {timeout_sec:.1f} seconds. Got {actual_events} before time-out.") + + def _expect_no_events_for_cluster(self, event_queue: queue.Queue, endpoint_id: int, expected_cluster: ClusterObjects.Cluster, timeout_sec: float): + start_time = time.time() + elapsed = 0.0 + time_remaining = timeout_sec + + logging.info(f"Waiting {timeout_sec:.1f} seconds for no more events for cluster {expected_cluster} on endpoint {endpoint_id}") + while time_remaining > 0: + try: + item: EventReadResult = event_queue.get(block=True, timeout=time_remaining) + event_data = item.Data + + if item.Header.EndpointId == endpoint_id and item.Header.ClusterId == event_data.cluster_id and item.Header.ClusterId == expected_cluster.id: + asserts.fail(f"Got Event {event_data} when we expected no further events for {expected_cluster}") + except queue.Empty: + # No error, we update timeouts and keep going + pass + + elapsed = time.time() - start_time + time_remaining = timeout_sec - elapsed + + logging.info(f"Successfully waited for no further events on {expected_cluster} for {elapsed:.1f} seconds") + + @async_test_body + async def test_TC_SWTCH_2_4(self): + # TODO: Make this come from PIXIT + switch_pressed_position = 1 + post_prompt_settle_delay_seconds = 10.0 + + # Commission DUT - already done + + # Read feature map to set bool markers + cluster = Clusters.Objects.Switch + feature_map = await self.read_single_attribute_check_success(cluster, attribute=cluster.Attributes.FeatureMap) + + has_ms_feature = (feature_map & cluster.Bitmaps.Feature.kMomentarySwitch) != 0 + has_msr_feature = (feature_map & cluster.Bitmaps.Feature.kMomentarySwitchRelease) != 0 + has_msl_feature = (feature_map & cluster.Bitmaps.Feature.kMomentarySwitchLongPress) != 0 + has_as_feature = (feature_map & cluster.Bitmaps.Feature.kActionSwitch) != 0 + # has_msm_feature = (feature_map & cluster.Bitmaps.Feature.kMomentarySwitchMultiPress) != 0 + + if not has_ms_feature: + logging.info("Skipping rest of test: SWTCH.S.F01(MS) feature not present") + self.skip_all_remaining_steps("2") + + endpoint_id = self.matter_test_config.endpoint + + # Step 1: Set up subscription to all Switch cluster events + self._placeholder_for_step("1") + event_listener = EventChangeCallback(cluster) + attrib_listener = ClusterAttributeChangeAccumulator(cluster) + await event_listener.start(self.default_controller, self.dut_node_id, endpoint=endpoint_id) + await attrib_listener.start(self.default_controller, self.dut_node_id, endpoint=endpoint_id) + + # Step 2: Operator does not operate switch on the DUT + self._placeholder_for_step("2") + self._ask_for_switch_idle() + + # Step 3: TH reads the CurrentPosition attribute from the DUT + self._placeholder_for_step("3") + + # Verify that the value is 0 + current_position = await self.read_single_attribute_check_success(cluster, attribute=cluster.Attributes.CurrentPosition) + asserts.assert_equal(current_position, 0) + + # Step 4a: Operator operates switch (keep pressed for long time, e.g. 5 seconds) on the DUT, the release it + self._placeholder_for_step("4a") + self._ask_for_long_press(endpoint_id, switch_pressed_position) + + # Step 4b: TH expects report of CurrentPosition 1, followed by a report of Current Position 0. + self._placeholder_for_step("4b") + logging.info( + f"Starting to wait for {post_prompt_settle_delay_seconds:.1f} seconds for CurrentPosition to go {switch_pressed_position}, then 0.") + self._await_sequence_of_reports(report_queue=attrib_listener.attribute_queue, endpoint_id=endpoint_id, attribute=cluster.Attributes.CurrentPosition, sequence=[ + switch_pressed_position, 0], timeout_sec=post_prompt_settle_delay_seconds) + + # Step 4c: TH expects at least InitialPress with NewPosition = 1 + self._placeholder_for_step("4c") + logging.info(f"Starting to wait for {post_prompt_settle_delay_seconds:.1f} seconds for InitialPress event.") + expected_events = [cluster.Events.InitialPress(newPosition=switch_pressed_position)] + self._await_sequence_of_events(event_queue=event_listener.event_queue, endpoint_id=endpoint_id, + sequence=expected_events, timeout_sec=post_prompt_settle_delay_seconds) + + # Step 4d: For MSL/AS, expect to see LongPress/LongRelease in that order + if not has_msl_feature and not has_as_feature: + logging.info("Skipping Step 4d due to missing MSL and AS features") + self._placeholder_for_skip("4d") + else: + # Steb 4d: TH expects report of LongPress, LongRelease in that order. + self._placeholder_for_step("4d") + logging.info(f"Starting to wait for {post_prompt_settle_delay_seconds:.1f} seconds for LongPress then LongRelease.") + expected_events = [] + expected_events.append(cluster.Events.LongPress(newPosition=switch_pressed_position)) + expected_events.append(cluster.Events.LongRelease(previousPosition=switch_pressed_position)) + self._await_sequence_of_events(event_queue=event_listener.event_queue, endpoint_id=endpoint_id, + sequence=expected_events, timeout_sec=post_prompt_settle_delay_seconds) + + # Step 4e: For MS & (!MSL & !AS & !MSR), expect no further events for 10 seconds. + if not has_msl_feature and not has_as_feature and not has_msr_feature: + self._placeholder_for_step("4e") + self._expect_no_events_for_cluster(event_queue=event_listener.event_queue, + endpoint_id=endpoint_id, expected_cluster=cluster, timeout_sec=10.0) + + # Step 4f: For MSR & not MSL, expect to see ShortRelease. + if not has_msl_feature and has_msr_feature: + self._placeholder_for_step("4f") + expected_events = [cluster.Events.ShortRelease(previousPosition=switch_pressed_position)] + self._await_sequence_of_events(event_queue=event_listener.event_queue, endpoint_id=endpoint_id, + sequence=expected_events, timeout_sec=post_prompt_settle_delay_seconds) + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index 90ffe5e018ca43..e3c8683acd52ae 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -34,7 +34,7 @@ from dataclasses import dataclass, field from datetime import datetime, timedelta, timezone from enum import Enum -from typing import List, Optional, Tuple +from typing import Any, List, Optional, Tuple from chip.tlv import float32, uint @@ -231,19 +231,22 @@ def name(self) -> str: class EventChangeCallback: - def __init__(self, expected_cluster: ClusterObjects): + def __init__(self, expected_cluster: ClusterObjects.Cluster): """This class creates a queue to store received event callbacks, that can be checked by the test script expected_cluster: is the cluster from which the events are expected """ self._q = queue.Queue() self._expected_cluster = expected_cluster - async def start(self, dev_ctrl, node_id: int, endpoint: int): + async def start(self, dev_ctrl, node_id: int, endpoint: int, fabric_filtered: bool = False, min_interval_sec: int = 0, max_interval_sec: int = 30) -> Any: """This starts a subscription for events on the specified node_id and endpoint. The cluster is specified when the class instance is created.""" + urgent = True self._subscription = await dev_ctrl.ReadEvent(node_id, - events=[(endpoint, self._expected_cluster, True)], reportInterval=(1, 5), - fabricFiltered=False, keepSubscriptions=True, autoResubscribe=False) + events=[(endpoint, self._expected_cluster, urgent)], reportInterval=( + min_interval_sec, max_interval_sec), + fabricFiltered=fabric_filtered, keepSubscriptions=True, autoResubscribe=False) self._subscription.SetEventUpdateCallback(self.__call__) + return self._subscription def __call__(self, res: EventReadResult, transaction: SubscriptionTransaction): """This is the subscription callback when an event is received. @@ -265,6 +268,10 @@ def wait_for_event_report(self, expected_event: ClusterObjects.ClusterEvent, tim asserts.assert_equal(res.Header.EventId, expected_event.event_id, "Expected event ID not found in event report") return res.Data + @property + def event_queue(self) -> queue.Queue: + return self._q + class AttributeChangeCallback: def __init__(self, expected_attribute: ClusterObjects.ClusterAttributeDescriptor): @@ -299,6 +306,45 @@ def wait_for_report(self): asserts.fail("[AttributeChangeCallback] Attribute {expected_attribute} not found in returned report") +@dataclass +class AttributeValue: + endpoint_id: int + attribute: ClusterObjects.ClusterAttributeDescriptor + value: Any + + +class ClusterAttributeChangeAccumulator: + def __init__(self, expected_cluster: ClusterObjects.Cluster): + self._q = queue.Queue() + self._expected_cluster = expected_cluster + self._subscription = None + + async def start(self, dev_ctrl, node_id: int, endpoint: int, fabric_filtered: bool = False, min_interval_sec: int = 0, max_interval_sec: int = 30) -> Any: + """This starts a subscription for attributes on the specified node_id and endpoint. The cluster is specified when the class instance is created.""" + self._subscription = await dev_ctrl.ReadAttribute( + nodeid=node_id, + attributes=[(endpoint, self._expected_cluster)], + reportInterval=(min_interval_sec, max_interval_sec), + fabricFiltered=fabric_filtered, + keepSubscriptions=True + ) + self._subscription.SetAttributeUpdateCallback(self.__call__) + return self._subscription + + def __call__(self, path: TypedAttributePath, transaction: SubscriptionTransaction): + """This is the subscription callback when an attribute report is received. + It checks the report is from the expected_cluster and then posts it into the queue for later processing.""" + if path.ClusterType == self._expected_cluster: + data = transaction.GetAttribute(path) + value = AttributeValue(endpoint_id=path.Path.EndpointId, attribute=path.AttributeType, value=data) + logging.info(f"Got subscription report for {path.AttributeType}: {data}") + self._q.put(value) + + @property + def attribute_queue(self) -> queue.Queue: + return self._q + + class InternalTestRunnerHooks(TestRunnerHooks): def start(self, count: int): @@ -680,7 +726,7 @@ def get_test_steps(self, test: str) -> list[TestStep]: return [TestStep(1, "Run entire test")] if steps is None else steps def get_defined_test_steps(self, test: str) -> list[TestStep]: - steps_name = 'steps_' + test[5:] + steps_name = f'steps_{test.removeprefix("test_")}' try: fn = getattr(self, steps_name) return fn() @@ -700,7 +746,7 @@ def get_test_pics(self, test: str) -> list[str]: return [] if pics is None else pics def _get_defined_pics(self, test: str) -> list[TestStep]: - steps_name = 'pics_' + test[5:] + steps_name = f'pics_{test.removeprefix("test_")}' try: fn = getattr(self, steps_name) return fn() @@ -720,7 +766,7 @@ def get_test_desc(self, test: str) -> str: ex: 133.1.1. [TC-ACL-1.1] Global attributes ''' - desc_name = 'desc_' + test[5:] + desc_name = f'desc_{test.removeprefix("test_")}' try: fn = getattr(self, desc_name) return fn() @@ -1110,7 +1156,7 @@ def get_setup_payload_info(self) -> List[SetupPayloadInfo]: def wait_for_user_input(self, prompt_msg: str, prompt_msg_placeholder: str = "Submit anything to continue", - default_value: str = "y") -> str: + default_value: str = "y") -> Optional[str]: """Ask for user input and wait for it. Args: @@ -1119,13 +1165,19 @@ def wait_for_user_input(self, default_value (str, optional): TH UI prompt default value. Defaults to "y". Returns: - str: User input + str: User input or none if input is closed. """ if self.runner_hook: self.runner_hook.show_prompt(msg=prompt_msg, placeholder=prompt_msg_placeholder, default_value=default_value) - return input(f'{prompt_msg.removesuffix(chr(10))}\n') + logging.info("========= USER PROMPT =========") + logging.info(f">>> {prompt_msg.rstrip()} (press enter to confirm)") + try: + return input() + except EOFError: + logging.info("========= EOF on STDIN =========") + return None def generate_mobly_test_config(matter_test_config: MatterTestConfig): From 571fcd114970726aea28d67b39088a0fae24dd3e Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Thu, 25 Jul 2024 13:36:41 -0400 Subject: [PATCH 10/49] Declare some global items for future testing (#34509) Co-authored-by: Andrei Litvin --- .../zcl/data-model/chip/global-structs.xml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/app/zap-templates/zcl/data-model/chip/global-structs.xml b/src/app/zap-templates/zcl/data-model/chip/global-structs.xml index 54b4f69bc93bb8..c68c4aa1d000d7 100644 --- a/src/app/zap-templates/zcl/data-model/chip/global-structs.xml +++ b/src/app/zap-templates/zcl/data-model/chip/global-structs.xml @@ -31,4 +31,25 @@ TODO: Make these structures global rather than defining them for each cluster. + + + + + + + + + + + + + + + + + + From 4423b3a139a7b5862480894b87b88c5f3cf75bd9 Mon Sep 17 00:00:00 2001 From: Junior Martinez <67972863+jmartinez-silabs@users.noreply.github.com> Date: Thu, 25 Jul 2024 14:01:27 -0400 Subject: [PATCH 11/49] =?UTF-8?q?[LevelControl]=20Implemented=20the=20Q=20?= =?UTF-8?q?quality=20logic=20for=20the=20CurrentLevel=20a=E2=80=A6=20(#344?= =?UTF-8?q?88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Implemented the Quiete reporting quality logic for the CurrentLevel and RemainingTime attributes * Restyled by clang-format * use c++ struct rather than c struct format * add cluster-building-blocks to the data model public dep --------- Co-authored-by: Restyled.io --- src/app/chip_data_model.gni | 1 + .../clusters/level-control/level-control.cpp | 198 ++++++++++++------ 2 files changed, 130 insertions(+), 69 deletions(-) diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index 90b5bd1cfec3f3..b436f46cb7cb1f 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -414,6 +414,7 @@ template("chip_data_model") { ":${_data_model_name}_zapgen", "${chip_root}/src/access", "${chip_root}/src/app", + "${chip_root}/src/app/cluster-building-blocks", "${chip_root}/src/app/common:attribute-type", "${chip_root}/src/app/common:cluster-objects", "${chip_root}/src/app/common:enums", diff --git a/src/app/clusters/level-control/level-control.cpp b/src/app/clusters/level-control/level-control.cpp index 3f15620181dfd0..814436c39e179a 100644 --- a/src/app/clusters/level-control/level-control.cpp +++ b/src/app/clusters/level-control/level-control.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,7 @@ #include using namespace chip; +using namespace chip::app; using namespace chip::app::Clusters; using namespace chip::app::Clusters::LevelControl; using chip::Protocols::InteractionModel::Status; @@ -85,7 +87,7 @@ struct CallbackScheduleState // when called consecutively }; -typedef struct +struct EmberAfLevelControlState { CommandId commandId; uint8_t moveToLevel; @@ -98,23 +100,24 @@ typedef struct uint32_t transitionTimeMs; uint32_t elapsedTimeMs; CallbackScheduleState callbackSchedule; -} EmberAfLevelControlState; + QuieterReportingAttribute quietCurrentLevel{ DataModel::NullNullable }; + QuieterReportingAttribute quietRemainingTime{ DataModel::MakeNullable(0) }; +}; static EmberAfLevelControlState stateTable[kLevelControlStateTableSize]; static EmberAfLevelControlState * getState(EndpointId endpoint); static Status moveToLevelHandler(EndpointId endpoint, CommandId commandId, uint8_t level, - app::DataModel::Nullable transitionTimeDs, - chip::Optional> optionsMask, + DataModel::Nullable transitionTimeDs, chip::Optional> optionsMask, chip::Optional> optionsOverride, uint16_t storedLevel); -static void moveHandler(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, MoveModeEnum moveMode, - app::DataModel::Nullable rate, chip::Optional> optionsMask, +static void moveHandler(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, MoveModeEnum moveMode, + DataModel::Nullable rate, chip::Optional> optionsMask, chip::Optional> optionsOverride); -static void stepHandler(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, StepModeEnum stepMode, - uint8_t stepSize, app::DataModel::Nullable transitionTimeDs, +static void stepHandler(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, StepModeEnum stepMode, + uint8_t stepSize, DataModel::Nullable transitionTimeDs, chip::Optional> optionsMask, chip::Optional> optionsOverride); -static void stopHandler(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +static void stopHandler(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, chip::Optional> optionsMask, chip::Optional> optionsOverride); static void setOnOffValue(EndpointId endpoint, bool onOff); @@ -122,6 +125,9 @@ static void writeRemainingTime(EndpointId endpoint, uint16_t remainingTimeMs); static bool shouldExecuteIfOff(EndpointId endpoint, CommandId commandId, chip::Optional> optionsMask, chip::Optional> optionsOverride); +static Status SetCurrentLevelQuietReport(EndpointId endpoint, EmberAfLevelControlState * state, + DataModel::Nullable newValue, bool isStartOrEndOfTransition); + #if defined(MATTER_DM_PLUGIN_SCENES_MANAGEMENT) && CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS class DefaultLevelControlSceneHandler : public scenes::DefaultSceneHandlerImpl { @@ -162,7 +168,7 @@ class DefaultLevelControlSceneHandler : public scenes::DefaultSceneHandlerImpl { using AttributeValuePair = ScenesManagement::Structs::AttributeValuePairStruct::Type; - app::DataModel::Nullable level; + DataModel::Nullable level; VerifyOrReturnError(Status::Success == Attributes::CurrentLevel::Get(endpoint, level), CHIP_ERROR_READ_FAILED); AttributeValuePair pairs[kLevelMaxScenableAttributes]; @@ -177,7 +183,7 @@ class DefaultLevelControlSceneHandler : public scenes::DefaultSceneHandlerImpl } else { - pairs[0].valueUnsigned8.SetValue(app::NumericAttributeTraits::kNullValue); + pairs[0].valueUnsigned8.SetValue(NumericAttributeTraits::kNullValue); } size_t attributeCount = 1; if (LevelControlHasFeature(endpoint, LevelControl::Feature::kFrequency)) @@ -189,7 +195,7 @@ class DefaultLevelControlSceneHandler : public scenes::DefaultSceneHandlerImpl attributeCount++; } - app::DataModel::List attributeValueList(pairs, attributeCount); + DataModel::List attributeValueList(pairs, attributeCount); return EncodeAttributeValueList(attributeValueList, serializedBytes); } @@ -203,7 +209,7 @@ class DefaultLevelControlSceneHandler : public scenes::DefaultSceneHandlerImpl CHIP_ERROR ApplyScene(EndpointId endpoint, ClusterId cluster, const ByteSpan & serializedBytes, scenes::TransitionTimeMs timeMs) override { - app::DataModel::DecodableList attributeValueList; + DataModel::DecodableList attributeValueList; ReturnErrorOnFailure(DecodeAttributeValueList(serializedBytes, attributeValueList)); @@ -245,15 +251,15 @@ class DefaultLevelControlSceneHandler : public scenes::DefaultSceneHandlerImpl EmberAfLevelControlState * state = getState(endpoint); if (level < state->minLevel || level > state->maxLevel) { - chip::app::NumericAttributeTraits::SetNull(level); + NumericAttributeTraits::SetNull(level); } - if (!app::NumericAttributeTraits::IsNullValue(level)) + if (!NumericAttributeTraits::IsNullValue(level)) { CommandId command = LevelControlHasFeature(endpoint, LevelControl::Feature::kOnOff) ? Commands::MoveToLevelWithOnOff::Id : Commands::MoveToLevel::Id; - moveToLevelHandler(endpoint, command, level, app::DataModel::MakeNullable(static_cast(timeMs / 100)), + moveToLevelHandler(endpoint, command, level, DataModel::MakeNullable(static_cast(timeMs / 100)), chip::Optional>(), chip::Optional>(), INVALID_STORED_LEVEL); } @@ -363,18 +369,68 @@ static void reallyUpdateCoupledColorTemp(EndpointId endpoint) } #endif // IGNORE_LEVEL_CONTROL_CLUSTER_OPTIONS && MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_TEMP +/* + * @brief + * This function is used to update the current level attribute + * while respecting it's defined quiet reporting quality: + * The attribute will be reported: + * - At most once per second, or + * - At the start of the movement/transition, or + * - At the end of the movement/transition, or + * - When it changes from null to any other value and vice versa. + * + * @param endpoint: endpoint on which the currentLevel attribute must be updated. + * @param state: LevelControlState struct of this given endpoint. + * @param newValue: Value to update the attribute with + * @param isStartOrEndOfTransition: Boolean that indicate whether the update is occuring at the start or end of a level transition + * @return Success in setting the attribute value or the IM error code for the failure. + */ +static Status SetCurrentLevelQuietReport(EndpointId endpoint, EmberAfLevelControlState * state, + DataModel::Nullable newValue, bool isStartOrEndOfTransition) +{ + AttributeDirtyState dirtyState; + MarkAttributeDirty markDirty = MarkAttributeDirty::kNo; + auto now = System::SystemClock().GetMonotonicTimestamp(); + + if (isStartOrEndOfTransition) + { + // At the start or end of the movement/transition we must report + auto predicate = [](const decltype(state->quietCurrentLevel)::SufficientChangePredicateCandidate &) -> bool { + return true; + }; + dirtyState = state->quietCurrentLevel.SetValue(newValue, now, predicate); + } + else + { + // During transtions, reports should be at most once per second + System::Clock::Milliseconds64 reportInterval = + std::max(System::Clock::Milliseconds64(1000), System::Clock::Milliseconds64(state->transitionTimeMs / 4)); + auto predicate = state->quietCurrentLevel.GetPredicateForSufficientTimeSinceLastDirty(reportInterval); + dirtyState = state->quietCurrentLevel.SetValue(newValue, now, predicate); + } + + if (dirtyState == AttributeDirtyState::kMustReport) + { + markDirty = MarkAttributeDirty::kIfChanged; + } + return Attributes::CurrentLevel::Set(endpoint, state->quietCurrentLevel.value(), markDirty); +} + void emberAfLevelControlClusterServerTickCallback(EndpointId endpoint) { EmberAfLevelControlState * state = getState(endpoint); Status status; - app::DataModel::Nullable currentLevel; + DataModel::Nullable currentLevel; const auto callbackStartTimestamp = System::SystemClock().GetMonotonicTimestamp(); + bool isTransitionStart = false; + bool isTransitionEnd = false; if (state == nullptr) { return; } + isTransitionStart = (state->elapsedTimeMs == 0); state->elapsedTimeMs += state->eventDurationMs; // Read the attribute; print error message and return if it can't be read @@ -412,7 +468,9 @@ void emberAfLevelControlClusterServerTickCallback(EndpointId endpoint) ChipLogDetail(Zcl, " to %d ", currentLevel.Value()); ChipLogDetail(Zcl, "(diff %c1)", state->increasing ? '+' : '-'); - status = Attributes::CurrentLevel::Set(endpoint, currentLevel); + // Are we at the requested level? + isTransitionEnd = (currentLevel.Value() == state->moveToLevel); + status = SetCurrentLevelQuietReport(endpoint, state, currentLevel, (isTransitionStart || isTransitionEnd)); if (status != Status::Success) { ChipLogProgress(Zcl, "ERR: writing current level %x", to_underlying(status)); @@ -423,8 +481,7 @@ void emberAfLevelControlClusterServerTickCallback(EndpointId endpoint) updateCoupledColorTemp(endpoint); - // Are we at the requested level? - if (currentLevel.Value() == state->moveToLevel) + if (isTransitionEnd) { if (state->commandId == Commands::MoveToLevelWithOnOff::Id || state->commandId == Commands::MoveWithOnOff::Id || state->commandId == Commands::StepWithOnOff::Id) @@ -478,11 +535,20 @@ static void writeRemainingTime(EndpointId endpoint, uint16_t remainingTimeMs) // This is done to ensure that the attribute, in tenths of a second, only // goes to zero when the remaining time in milliseconds is actually zero. uint16_t remainingTimeDs = static_cast((remainingTimeMs + 99) / 100); - Status status = LevelControl::Attributes::RemainingTime::Set(endpoint, remainingTimeDs); - if (status != Status::Success) + auto markDirty = MarkAttributeDirty::kNo; + auto state = getState(endpoint); + auto now = System::SystemClock().GetMonotonicTimestamp(); + + // Establish the quiet report condition for the RemainingTime Attribute + // The quiet report is determined by the previously set policies: + // - kMarkDirtyOnChangeToFromZero : When the value changes from 0 to any other value and vice versa, or + // - kMarkDirtyOnIncrement : When the value increases. + if (state->quietRemainingTime.SetValue(remainingTimeDs, now) == AttributeDirtyState::kMustReport) { - ChipLogProgress(Zcl, "ERR: writing remaining time %x", to_underlying(status)); + markDirty = MarkAttributeDirty::kIfChanged; } + + Attributes::RemainingTime::Set(endpoint, state->quietRemainingTime.value().ValueOr(0), markDirty); } #endif // IGNORE_LEVEL_CONTROL_CLUSTER_LEVEL_CONTROL_REMAINING_TIME } @@ -583,7 +649,7 @@ static bool shouldExecuteIfOff(EndpointId endpoint, CommandId commandId, chip::O return true; } -bool emberAfLevelControlClusterMoveToLevelCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +bool emberAfLevelControlClusterMoveToLevelCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, const Commands::MoveToLevel::DecodableType & commandData) { MATTER_TRACE_SCOPE("MoveToLevel", "LevelControl"); @@ -629,8 +695,7 @@ chip::scenes::SceneHandler * GetSceneHandler() } // namespace LevelControlServer -bool emberAfLevelControlClusterMoveToLevelWithOnOffCallback(app::CommandHandler * commandObj, - const app::ConcreteCommandPath & commandPath, +bool emberAfLevelControlClusterMoveToLevelWithOnOffCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, const Commands::MoveToLevelWithOnOff::DecodableType & commandData) { MATTER_TRACE_SCOPE("MoveToLevelWithOnOff", "LevelControl"); @@ -660,7 +725,7 @@ bool emberAfLevelControlClusterMoveToLevelWithOnOffCallback(app::CommandHandler return true; } -bool emberAfLevelControlClusterMoveCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +bool emberAfLevelControlClusterMoveCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, const Commands::Move::DecodableType & commandData) { MATTER_TRACE_SCOPE("Move", "LevelControl"); @@ -685,7 +750,7 @@ bool emberAfLevelControlClusterMoveCallback(app::CommandHandler * commandObj, co return true; } -bool emberAfLevelControlClusterMoveWithOnOffCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +bool emberAfLevelControlClusterMoveWithOnOffCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, const Commands::MoveWithOnOff::DecodableType & commandData) { MATTER_TRACE_SCOPE("MoveWithOnOff", "LevelControl"); @@ -710,7 +775,7 @@ bool emberAfLevelControlClusterMoveWithOnOffCallback(app::CommandHandler * comma return true; } -bool emberAfLevelControlClusterStepCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +bool emberAfLevelControlClusterStepCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, const Commands::Step::DecodableType & commandData) { MATTER_TRACE_SCOPE("Step", "LevelControl"); @@ -736,7 +801,7 @@ bool emberAfLevelControlClusterStepCallback(app::CommandHandler * commandObj, co return true; } -bool emberAfLevelControlClusterStepWithOnOffCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +bool emberAfLevelControlClusterStepWithOnOffCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, const Commands::StepWithOnOff::DecodableType & commandData) { MATTER_TRACE_SCOPE("StepWithOnOff", "LevelControl"); @@ -762,7 +827,7 @@ bool emberAfLevelControlClusterStepWithOnOffCallback(app::CommandHandler * comma return true; } -bool emberAfLevelControlClusterStopCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +bool emberAfLevelControlClusterStopCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, const Commands::Stop::DecodableType & commandData) { MATTER_TRACE_SCOPE("Stop", "LevelControl"); @@ -775,7 +840,7 @@ bool emberAfLevelControlClusterStopCallback(app::CommandHandler * commandObj, co return true; } -bool emberAfLevelControlClusterStopWithOnOffCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +bool emberAfLevelControlClusterStopWithOnOffCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, const Commands::StopWithOnOff::DecodableType & commandData) { MATTER_TRACE_SCOPE("StopWithOnOff", "LevelControl"); @@ -788,12 +853,11 @@ bool emberAfLevelControlClusterStopWithOnOffCallback(app::CommandHandler * comma } static Status moveToLevelHandler(EndpointId endpoint, CommandId commandId, uint8_t level, - app::DataModel::Nullable transitionTimeDs, - chip::Optional> optionsMask, + DataModel::Nullable transitionTimeDs, chip::Optional> optionsMask, chip::Optional> optionsOverride, uint16_t storedLevel) { EmberAfLevelControlState * state = getState(endpoint); - app::DataModel::Nullable currentLevel; + DataModel::Nullable currentLevel; uint8_t actualStepSize; if (state == nullptr) @@ -916,11 +980,9 @@ static Status moveToLevelHandler(EndpointId endpoint, CommandId commandId, uint8 // The duration between events will be the transition time divided by the // distance we must move. - state->eventDurationMs = state->transitionTimeMs / std::max(static_cast(1u), actualStepSize); - state->elapsedTimeMs = 0; - - state->storedLevel = storedLevel; - + state->eventDurationMs = state->transitionTimeMs / std::max(static_cast(1u), actualStepSize); + state->elapsedTimeMs = 0; + state->storedLevel = storedLevel; state->callbackSchedule.runTime = System::Clock::Milliseconds32(0); #ifdef MATTER_DM_PLUGIN_SCENES_MANAGEMENT @@ -946,14 +1008,14 @@ static Status moveToLevelHandler(EndpointId endpoint, CommandId commandId, uint8 return Status::Success; } -static void moveHandler(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, MoveModeEnum moveMode, - app::DataModel::Nullable rate, chip::Optional> optionsMask, +static void moveHandler(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, MoveModeEnum moveMode, + DataModel::Nullable rate, chip::Optional> optionsMask, chip::Optional> optionsOverride) { Status status; uint8_t difference; EmberAfLevelControlState * state; - app::DataModel::Nullable currentLevel; + DataModel::Nullable currentLevel; EndpointId endpoint = commandPath.mEndpointId; CommandId commandId = commandPath.mCommandId; @@ -983,7 +1045,7 @@ static void moveHandler(app::CommandHandler * commandObj, const app::ConcreteCom // Otherwise, move as fast as possible if (rate.IsNull()) { - app::DataModel::Nullable defaultMoveRate; + DataModel::Nullable defaultMoveRate; status = Attributes::DefaultMoveRate::Get(endpoint, defaultMoveRate); if (status != Status::Success || defaultMoveRate.IsNull()) { @@ -1078,8 +1140,7 @@ static void moveHandler(app::CommandHandler * commandObj, const app::ConcreteCom state->elapsedTimeMs = 0; // storedLevel is not used for Move commands. - state->storedLevel = INVALID_STORED_LEVEL; - + state->storedLevel = INVALID_STORED_LEVEL; state->callbackSchedule.runTime = System::Clock::Milliseconds32(0); // The setup was successful, so mark the new state as active and return. @@ -1090,13 +1151,13 @@ static void moveHandler(app::CommandHandler * commandObj, const app::ConcreteCom commandObj->AddStatus(commandPath, status); } -static void stepHandler(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, StepModeEnum stepMode, - uint8_t stepSize, app::DataModel::Nullable transitionTimeDs, +static void stepHandler(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, StepModeEnum stepMode, + uint8_t stepSize, DataModel::Nullable transitionTimeDs, chip::Optional> optionsMask, chip::Optional> optionsOverride) { Status status; EmberAfLevelControlState * state; - app::DataModel::Nullable currentLevel; + DataModel::Nullable currentLevel; EndpointId endpoint = commandPath.mEndpointId; CommandId commandId = commandPath.mCommandId; @@ -1226,8 +1287,7 @@ static void stepHandler(app::CommandHandler * commandObj, const app::ConcreteCom state->elapsedTimeMs = 0; // storedLevel is not used for Step commands - state->storedLevel = INVALID_STORED_LEVEL; - + state->storedLevel = INVALID_STORED_LEVEL; state->callbackSchedule.runTime = System::Clock::Milliseconds32(0); // The setup was successful, so mark the new state as active and return. @@ -1238,14 +1298,13 @@ static void stepHandler(app::CommandHandler * commandObj, const app::ConcreteCom commandObj->AddStatus(commandPath, status); } -static void stopHandler(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +static void stopHandler(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, chip::Optional> optionsMask, chip::Optional> optionsOverride) { - EndpointId endpoint = commandPath.mEndpointId; - CommandId commandId = commandPath.mCommandId; - + EndpointId endpoint = commandPath.mEndpointId; + CommandId commandId = commandPath.mCommandId; EmberAfLevelControlState * state = getState(endpoint); - Status status; + Status status = Status::Success; if (state == nullptr) { @@ -1255,14 +1314,13 @@ static void stopHandler(app::CommandHandler * commandObj, const app::ConcreteCom if (!shouldExecuteIfOff(endpoint, commandId, optionsMask, optionsOverride)) { - status = Status::Success; goto send_default_response; } // Cancel any currently active command. cancelEndpointTimerCallback(endpoint); + SetCurrentLevelQuietReport(endpoint, state, state->quietCurrentLevel.value(), true /*isStartOrEndOfTransition*/); writeRemainingTime(endpoint, 0); - status = Status::Success; send_default_response: commandObj->AddStatus(commandPath, status); @@ -1272,9 +1330,9 @@ static void stopHandler(app::CommandHandler * commandObj, const app::ConcreteCom // Quotes are from table 3.46. void emberAfOnOffClusterLevelControlEffectCallback(EndpointId endpoint, bool newValue) { - app::DataModel::Nullable resolvedLevel; - app::DataModel::Nullable temporaryCurrentLevelCache; - app::DataModel::Nullable transitionTime; + DataModel::Nullable resolvedLevel; + DataModel::Nullable temporaryCurrentLevelCache; + DataModel::Nullable transitionTime; uint16_t currentOnOffTransitionTime; Status status; @@ -1355,7 +1413,7 @@ void emberAfOnOffClusterLevelControlEffectCallback(EndpointId endpoint, bool new { // If newValue is OnOff::Commands::On::Id... // "Set CurrentLevel to minimum level allowed for the device." - status = Attributes::CurrentLevel::Set(endpoint, minimumLevelAllowedForTheDevice); + status = SetCurrentLevelQuietReport(endpoint, state, minimumLevelAllowedForTheDevice, true /*isStartOrEndOfTransition*/); if (status != Status::Success) { ChipLogProgress(Zcl, "ERR: reading current level %x", to_underlying(status)); @@ -1398,6 +1456,9 @@ void emberAfLevelControlClusterServerInitCallback(EndpointId endpoint) return; } + state->quietRemainingTime.policy() + .Set(QuieterReportingPolicyEnum::kMarkDirtyOnIncrement) + .Set(QuieterReportingPolicyEnum::kMarkDirtyOnChangeToFromZero); state->minLevel = MATTER_DM_PLUGIN_LEVEL_CONTROL_MINIMUM_LEVEL; state->maxLevel = MATTER_DM_PLUGIN_LEVEL_CONTROL_MAXIMUM_LEVEL; @@ -1419,7 +1480,7 @@ void emberAfLevelControlClusterServerInitCallback(EndpointId endpoint) } } - app::DataModel::Nullable currentLevel; + DataModel::Nullable currentLevel; Status status = Attributes::CurrentLevel::Get(endpoint, currentLevel); if (status == Status::Success) { @@ -1440,7 +1501,7 @@ void emberAfLevelControlClusterServerInitCallback(EndpointId endpoint) // 0xFF Work Around ZAP Can't set default value to NULL // https://github.com/project-chip/zap/issues/354 - app::DataModel::Nullable startUpCurrentLevel; + DataModel::Nullable startUpCurrentLevel; status = Attributes::StartUpCurrentLevel::Get(endpoint, startUpCurrentLevel); if (status == Status::Success) { @@ -1470,25 +1531,24 @@ void emberAfLevelControlClusterServerInitCallback(EndpointId endpoint) } } // Otherwise Set the CurrentLevel attribute to its previous value which was already fetch above - - Attributes::CurrentLevel::Set(endpoint, currentLevel); + SetCurrentLevelQuietReport(endpoint, state, currentLevel, true /*isStartOrEndOfTransition*/); } } #endif // IGNORE_LEVEL_CONTROL_CLUSTER_START_UP_CURRENT_LEVEL // In any case, we make sure that the respects min/max if (currentLevel.IsNull() || currentLevel.Value() < state->minLevel) { - Attributes::CurrentLevel::Set(endpoint, state->minLevel); + SetCurrentLevelQuietReport(endpoint, state, state->minLevel, true /*isStartOrEndOfTransition*/); } else if (currentLevel.Value() > state->maxLevel) { - Attributes::CurrentLevel::Set(endpoint, state->maxLevel); + SetCurrentLevelQuietReport(endpoint, state, state->maxLevel, true /*isStartOrEndOfTransition*/); } } #if defined(MATTER_DM_PLUGIN_SCENES_MANAGEMENT) && CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS // Registers Scene handlers for the level control cluster on the server - app::Clusters::ScenesManagement::ScenesServer::Instance().RegisterSceneHandler(endpoint, LevelControlServer::GetSceneHandler()); + Clusters::ScenesManagement::ScenesServer::Instance().RegisterSceneHandler(endpoint, LevelControlServer::GetSceneHandler()); #endif // defined(MATTER_DM_PLUGIN_SCENES_MANAGEMENT) && CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS emberAfPluginLevelControlClusterServerPostInitCallback(endpoint); From 2a607350ccea2e6666a250b7591cc2545ed1b450 Mon Sep 17 00:00:00 2001 From: C Freeman Date: Thu, 25 Jul 2024 15:15:56 -0400 Subject: [PATCH 12/49] Revert thermostat stuff breaking tot (#34518) * Revert "update tests and thermostat server cluster for new constraints for LocalTemperatureCalibration and MinSetpointDeadBand (#34474)" This reverts commit 335ac96cdde78fd81d69d6fb451427e769b7f413. * Revert "update constraints for LocalTemperatureCalibration and MinSetpointDeadBand attributes (#34473)" This reverts commit 21a5bd6a402b699c0d64283918e266092a771bd1. --- .../all-clusters-app/app-templates/endpoint_config.h | 2 +- src/app/clusters/thermostat-server/thermostat-server.cpp | 2 +- src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml | 6 +++--- .../zcl/data-model/chip/thermostat-cluster.xml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h index a77bd74f11f267..f2c082b1d0d4a1 100644 --- a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h +++ b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h @@ -307,7 +307,7 @@ { (uint16_t) 0xBB8, (uint16_t) -0x6AB3, (uint16_t) 0x7FFF }, /* MaxHeatSetpointLimit */ \ { (uint16_t) 0x640, (uint16_t) -0x6AB3, (uint16_t) 0x7FFF }, /* MinCoolSetpointLimit */ \ { (uint16_t) 0xC80, (uint16_t) -0x6AB3, (uint16_t) 0x7FFF }, /* MaxCoolSetpointLimit */ \ - { (uint16_t) 0x19, (uint16_t) 0x0, (uint16_t) 0x7F }, /* MinSetpointDeadBand */ \ + { (uint16_t) 0x19, (uint16_t) 0x0, (uint16_t) 0x19 }, /* MinSetpointDeadBand */ \ { (uint16_t) 0x4, (uint16_t) 0x0, (uint16_t) 0x5 }, /* ControlSequenceOfOperation */ \ { (uint16_t) 0x1, (uint16_t) 0x0, (uint16_t) 0x9 }, /* SystemMode */ \ \ diff --git a/src/app/clusters/thermostat-server/thermostat-server.cpp b/src/app/clusters/thermostat-server/thermostat-server.cpp index e802a9201f33dc..c650a30c871989 100644 --- a/src/app/clusters/thermostat-server/thermostat-server.cpp +++ b/src/app/clusters/thermostat-server/thermostat-server.cpp @@ -409,7 +409,7 @@ MatterThermostatClusterServerPreAttributeChangedCallback(const app::ConcreteAttr requested = *value; if (!AutoSupported) return imcode::UnsupportedAttribute; - if (requested < 0 || requested > 127) + if (requested < 0 || requested > 25) return imcode::InvalidValue; return imcode::Success; } diff --git a/src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml b/src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml index 4558b08745a949..faf34fdea43e92 100644 --- a/src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml +++ b/src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml @@ -243,8 +243,8 @@ tests: response: constraints: type: int8s - minValue: -127 - maxValue: 127 + minValue: -25 + maxValue: 25 - label: "Step 13a: TH reads attribute OccupiedCoolingSetpoint from the DUT" PICS: TSTAT.S.F01 && TSTAT.S.A0017 && TSTAT.S.A0018 @@ -426,7 +426,7 @@ tests: constraints: type: int8s minValue: 0 - maxValue: 127 + maxValue: 25 - label: "Step 22: TH reads the RemoteSensing attribute from the DUT" PICS: TSTAT.S.A001a diff --git a/src/app/zap-templates/zcl/data-model/chip/thermostat-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/thermostat-cluster.xml index c2979487e45ae3..91a89497d7c8cc 100644 --- a/src/app/zap-templates/zcl/data-model/chip/thermostat-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/thermostat-cluster.xml @@ -337,7 +337,7 @@ limitations under the License. - + LocalTemperatureCalibration @@ -361,7 +361,7 @@ limitations under the License. MaxCoolSetpointLimit - + MinSetpointDeadBand From 5ea06ea871002f256f3dc5f41c9de566592d4b50 Mon Sep 17 00:00:00 2001 From: Alex Tsitsiura Date: Thu, 25 Jul 2024 22:25:54 +0300 Subject: [PATCH 13/49] [Telink] Update Docker image (Zephyr update) (#34503) --- integrations/docker/images/base/chip-build/version | 2 +- integrations/docker/images/stage-2/chip-build-telink/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/integrations/docker/images/base/chip-build/version b/integrations/docker/images/base/chip-build/version index 8a1624d292e305..ba77442b28dc23 100644 --- a/integrations/docker/images/base/chip-build/version +++ b/integrations/docker/images/base/chip-build/version @@ -1 +1 @@ -65 : [nrfconnect] Update nRF Connect SDK version. +66 : [Telink] Update Docker image (Zephyr update) diff --git a/integrations/docker/images/stage-2/chip-build-telink/Dockerfile b/integrations/docker/images/stage-2/chip-build-telink/Dockerfile index 66d5eccdc0d3a1..bea580245e8639 100644 --- a/integrations/docker/images/stage-2/chip-build-telink/Dockerfile +++ b/integrations/docker/images/stage-2/chip-build-telink/Dockerfile @@ -18,7 +18,7 @@ RUN set -x \ && : # last line # Setup Zephyr -ARG ZEPHYR_REVISION=ab81a585fca6a83b30e1f4e58a021113d6a3acb8 +ARG ZEPHYR_REVISION=ef7bfc2214602ecf237332b71e6a2bdd69205fbd WORKDIR /opt/telink/zephyrproject RUN set -x \ && python3 -m pip install --break-system-packages -U --no-cache-dir west \ From b74057ee9cbc6f510996ffa5ec09fa25023bee91 Mon Sep 17 00:00:00 2001 From: Pradip De Date: Thu, 25 Jul 2024 12:46:52 -0700 Subject: [PATCH 14/49] Add TransportPayloadCapability flag for GetConnectedDevices and bubble (#34450) up the flag to the wrapper IM Python APIs. Add python script binding methods for LargePayload tests --to check if session allows large payload. --to close the underlying TCP connection. --to check if the session is active. --- .../ChipDeviceController-ScriptBinding.cpp | 61 +++++++++++- src/controller/python/chip/ChipDeviceCtrl.py | 99 +++++++++++++++---- src/python_testing/matter_testing_support.py | 6 +- 3 files changed, 144 insertions(+), 22 deletions(-) diff --git a/src/controller/python/ChipDeviceController-ScriptBinding.cpp b/src/controller/python/ChipDeviceController-ScriptBinding.cpp index d4603efe05e469..d9b557bb62be67 100644 --- a/src/controller/python/ChipDeviceController-ScriptBinding.cpp +++ b/src/controller/python/ChipDeviceController-ScriptBinding.cpp @@ -213,7 +213,8 @@ PyChipError pychip_DeviceCommissioner_CloseBleConnection(chip::Controller::Devic const char * pychip_Stack_StatusReportToString(uint32_t profileId, uint16_t statusCode); PyChipError pychip_GetConnectedDeviceByNodeId(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId, - chip::Controller::Python::PyObject * context, DeviceAvailableFunc callback); + chip::Controller::Python::PyObject * context, DeviceAvailableFunc callback, + int transportPayloadCapability); PyChipError pychip_FreeOperationalDeviceProxy(chip::OperationalDeviceProxy * deviceProxy); PyChipError pychip_GetLocalSessionId(chip::OperationalDeviceProxy * deviceProxy, uint16_t * localSessionId); PyChipError pychip_GetNumSessionsToPeer(chip::OperationalDeviceProxy * deviceProxy, uint32_t * numSessions); @@ -239,6 +240,13 @@ void pychip_Storage_ShutdownAdapter(chip::Controller::Python::StorageAdapter * s // ICD // void pychip_CheckInDelegate_SetOnCheckInCompleteCallback(PyChipCheckInDelegate::OnCheckInCompleteCallback * callback); + +// +// LargePayload and TCP +PyChipError pychip_SessionAllowsLargePayload(chip::OperationalDeviceProxy * deviceProxy, bool * allowsLargePayload); +PyChipError pychip_IsSessionOverTCPConnection(chip::OperationalDeviceProxy * deviceProxy, bool * isSessionOverTCP); +PyChipError pychip_IsActiveSession(chip::OperationalDeviceProxy * deviceProxy, bool * isActiveSession); +PyChipError pychip_CloseTCPConnectionWithPeer(chip::OperationalDeviceProxy * deviceProxy); } void * pychip_Storage_InitializeStorageAdapter(chip::Controller::Python::PyObject * context, @@ -807,11 +815,58 @@ struct GetDeviceCallbacks } // anonymous namespace PyChipError pychip_GetConnectedDeviceByNodeId(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId, - chip::Controller::Python::PyObject * context, DeviceAvailableFunc callback) + chip::Controller::Python::PyObject * context, DeviceAvailableFunc callback, + int transportPayloadCapability) { VerifyOrReturnError(devCtrl != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); auto * callbacks = new GetDeviceCallbacks(context, callback); - return ToPyChipError(devCtrl->GetConnectedDevice(nodeId, &callbacks->mOnSuccess, &callbacks->mOnFailure)); + return ToPyChipError(devCtrl->GetConnectedDevice(nodeId, &callbacks->mOnSuccess, &callbacks->mOnFailure, + static_cast(transportPayloadCapability))); +} + +PyChipError pychip_SessionAllowsLargePayload(chip::OperationalDeviceProxy * deviceProxy, bool * allowsLargePayload) +{ + VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION)); + VerifyOrReturnError(allowsLargePayload != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); + + *allowsLargePayload = deviceProxy->GetSecureSession().Value()->AsSecureSession()->AllowsLargePayload(); + + return ToPyChipError(CHIP_NO_ERROR); +} + +PyChipError pychip_IsSessionOverTCPConnection(chip::OperationalDeviceProxy * deviceProxy, bool * isSessionOverTCP) +{ + VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION)); + VerifyOrReturnError(isSessionOverTCP != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); + + *isSessionOverTCP = deviceProxy->GetSecureSession().Value()->AsSecureSession()->GetTCPConnection() != nullptr; + + return ToPyChipError(CHIP_NO_ERROR); +} + +PyChipError pychip_IsActiveSession(chip::OperationalDeviceProxy * deviceProxy, bool * isActiveSession) +{ + VerifyOrReturnError(isActiveSession != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); + + *isActiveSession = false; + if (deviceProxy->GetSecureSession().HasValue()) + { + *isActiveSession = deviceProxy->GetSecureSession().Value()->AsSecureSession()->IsActiveSession(); + } + + return ToPyChipError(CHIP_NO_ERROR); +} + +PyChipError pychip_CloseTCPConnectionWithPeer(chip::OperationalDeviceProxy * deviceProxy) +{ + VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION)); + VerifyOrReturnError(deviceProxy->GetSecureSession().Value()->AsSecureSession()->AllowsLargePayload(), + ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); + + deviceProxy->GetExchangeManager()->GetSessionManager()->TCPDisconnect( + deviceProxy->GetSecureSession().Value()->AsSecureSession()->GetTCPConnection(), /* shouldAbort = */ false); + + return ToPyChipError(CHIP_NO_ERROR); } PyChipError pychip_FreeOperationalDeviceProxy(chip::OperationalDeviceProxy * deviceProxy) diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py index 0e568803b1e523..91c83e8954fa37 100644 --- a/src/controller/python/chip/ChipDeviceCtrl.py +++ b/src/controller/python/chip/ChipDeviceCtrl.py @@ -84,6 +84,16 @@ _ChipDeviceController_IterateDiscoveredCommissionableNodesFunct = CFUNCTYPE(None, c_char_p, c_size_t) +# Defines for the transport payload types to use to select the suitable +# underlying transport of the session. +# class TransportPayloadCapability(ctypes.c_int): + + +class TransportPayloadCapability(ctypes.c_int): + MRP_PAYLOAD = 0 + LARGE_PAYLOAD = 1 + MRP_OR_TCP_PAYLOAD = 2 + @dataclass class CommissioningParameters: @@ -371,6 +381,53 @@ def attestationChallenge(self) -> bytes: return bytes(buf) + @property + def sessionAllowsLargePayload(self) -> bool: + self._dmLib.pychip_SessionAllowsLargePayload.argtypes = [ctypes.c_void_p, POINTER(ctypes.c_bool)] + self._dmLib.pychip_SessionAllowsLargePayload.restype = PyChipError + + supportsLargePayload = ctypes.c_bool(False) + + builtins.chipStack.Call( + lambda: self._dmLib.pychip_SessionAllowsLargePayload(self._deviceProxy, pointer(supportsLargePayload)) + ).raise_on_error() + + return supportsLargePayload.value + + @property + def isSessionOverTCPConnection(self) -> bool: + self._dmLib.pychip_IsSessionOverTCPConnection.argtypes = [ctypes.c_void_p, POINTER(ctypes.c_bool)] + self._dmLib.pychip_IsSessionOverTCPConnection.restype = PyChipError + + isSessionOverTCP = ctypes.c_bool(False) + + builtins.chipStack.Call( + lambda: self._dmLib.pychip_IsSessionOverTCPConnection(self._deviceProxy, pointer(isSessionOverTCP)) + ).raise_on_error() + + return isSessionOverTCP.value + + @property + def isActiveSession(self) -> bool: + self._dmLib.pychip_IsActiveSession.argtypes = [ctypes.c_void_p, POINTER(ctypes.c_bool)] + self._dmLib.pychip_IsActiveSession.restype = PyChipError + + isActiveSession = ctypes.c_bool(False) + + builtins.chipStack.Call( + lambda: self._dmLib.pychip_IsActiveSession(self._deviceProxy, pointer(isActiveSession)) + ).raise_on_error() + + return isActiveSession.value + + def closeTCPConnectionWithPeer(self): + self._dmLib.pychip_CloseTCPConnectionWithPeer.argtypes = [ctypes.c_void_p] + self._dmLib.pychip_CloseTCPConnectionWithPeer.restype = PyChipError + + builtins.chipStack.Call( + lambda: self._dmLib.pychip_CloseTCPConnectionWithPeer(self._deviceProxy) + ).raise_on_error() + DiscoveryFilterType = discovery.FilterType DiscoveryType = discovery.DiscoveryType @@ -906,7 +963,7 @@ async def FindOrEstablishPASESession(self, setupCode: str, nodeid: int, timeoutM if res.is_success: return DeviceProxyWrapper(returnDevice, DeviceProxyWrapper.DeviceProxyType.COMMISSIONEE, self._dmLib) - def GetConnectedDeviceSync(self, nodeid, allowPASE=True, timeoutMs: int = None): + def GetConnectedDeviceSync(self, nodeid, allowPASE=True, timeoutMs: int = None, payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD): ''' Gets an OperationalDeviceProxy or CommissioneeDeviceProxy for the specified Node. nodeId: Target's Node ID @@ -943,7 +1000,7 @@ def deviceAvailable(self, device, err): closure = DeviceAvailableClosure() ctypes.pythonapi.Py_IncRef(ctypes.py_object(closure)) self._ChipStack.Call(lambda: self._dmLib.pychip_GetConnectedDeviceByNodeId( - self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback), + self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback, payloadCapability), timeoutMs).raise_on_error() # The callback might have been received synchronously (during self._ChipStack.Call()). @@ -975,7 +1032,8 @@ async def WaitForActive(self, nodeid, *, timeoutSeconds=30.0, stayActiveDuration await WaitForCheckIn(ScopedNodeId(nodeid, self._fabricIndex), timeoutSeconds=timeoutSeconds) return await self.SendCommand(nodeid, 0, Clusters.IcdManagement.Commands.StayActiveRequest(stayActiveDuration=stayActiveDurationMs)) - async def GetConnectedDevice(self, nodeid, allowPASE: bool = True, timeoutMs: int = None): + async def GetConnectedDevice(self, nodeid, allowPASE: bool = True, timeoutMs: int = None, + payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD): ''' Gets an OperationalDeviceProxy or CommissioneeDeviceProxy for the specified Node. nodeId: Target's Node ID @@ -1020,7 +1078,7 @@ def deviceAvailable(self, device, err): closure = DeviceAvailableClosure(eventLoop, future) ctypes.pythonapi.Py_IncRef(ctypes.py_object(closure)) await self._ChipStack.CallAsync(lambda: self._dmLib.pychip_GetConnectedDeviceByNodeId( - self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback), + self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback, payloadCapability), timeoutMs) # The callback might have been received synchronously (during self._ChipStack.CallAsync()). @@ -1124,7 +1182,8 @@ async def TestOnlySendCommandTimedRequestFlagWithNoTimedInvoke(self, nodeid: int async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects.ClusterCommand, responseType=None, timedRequestTimeoutMs: typing.Union[None, int] = None, interactionTimeoutMs: typing.Union[None, int] = None, busyWaitMs: typing.Union[None, int] = None, - suppressResponse: typing.Union[None, bool] = None): + suppressResponse: typing.Union[None, bool] = None, + payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD): ''' Send a cluster-object encapsulated command to a node and get returned a future that can be awaited upon to receive the response. If a valid responseType is passed in, that will be used to de-serialize the object. If not, @@ -1144,7 +1203,7 @@ async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects. eventLoop = asyncio.get_running_loop() future = eventLoop.create_future() - device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs) + device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs, payloadCapability=payloadCapability) res = await ClusterCommand.SendCommand( future, eventLoop, responseType, device.deviceProxy, ClusterCommand.CommandPath( EndpointId=endpoint, @@ -1158,7 +1217,8 @@ async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects. async def SendBatchCommands(self, nodeid: int, commands: typing.List[ClusterCommand.InvokeRequestInfo], timedRequestTimeoutMs: typing.Optional[int] = None, interactionTimeoutMs: typing.Optional[int] = None, busyWaitMs: typing.Optional[int] = None, - suppressResponse: typing.Optional[bool] = None): + suppressResponse: typing.Optional[bool] = None, + payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD): ''' Send a batch of cluster-object encapsulated commands to a node and get returned a future that can be awaited upon to receive the responses. If a valid responseType is passed in, that will be used to de-serialize the object. If not, @@ -1186,7 +1246,7 @@ async def SendBatchCommands(self, nodeid: int, commands: typing.List[ClusterComm eventLoop = asyncio.get_running_loop() future = eventLoop.create_future() - device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs) + device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs, payloadCapability=payloadCapability) res = await ClusterCommand.SendBatchCommands( future, eventLoop, device.deviceProxy, commands, @@ -1215,7 +1275,8 @@ def SendGroupCommand(self, groupid: int, payload: ClusterObjects.ClusterCommand, async def WriteAttribute(self, nodeid: int, attributes: typing.List[typing.Tuple[int, ClusterObjects.ClusterAttributeDescriptor]], timedRequestTimeoutMs: typing.Union[None, int] = None, - interactionTimeoutMs: typing.Union[None, int] = None, busyWaitMs: typing.Union[None, int] = None): + interactionTimeoutMs: typing.Union[None, int] = None, busyWaitMs: typing.Union[None, int] = None, + payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD): ''' Write a list of attributes on a target node. @@ -1237,7 +1298,7 @@ async def WriteAttribute(self, nodeid: int, eventLoop = asyncio.get_running_loop() future = eventLoop.create_future() - device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs) + device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs, payloadCapability=payloadCapability) attrs = [] for v in attributes: @@ -1396,7 +1457,8 @@ async def Read(self, nodeid: int, attributes: typing.List[typing.Union[ ]] = None, eventNumberFilter: typing.Optional[int] = None, returnClusterObject: bool = False, reportInterval: typing.Tuple[int, int] = None, - fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True): + fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True, + payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD): ''' Read a list of attributes and/or events from a target node @@ -1456,7 +1518,7 @@ async def Read(self, nodeid: int, attributes: typing.List[typing.Union[ eventLoop = asyncio.get_running_loop() future = eventLoop.create_future() - device = await self.GetConnectedDevice(nodeid) + device = await self.GetConnectedDevice(nodeid, payloadCapability=payloadCapability) attributePaths = [self._parseAttributePathTuple( v) for v in attributes] if attributes else None clusterDataVersionFilters = [self._parseDataVersionFilterTuple( @@ -1487,7 +1549,8 @@ async def ReadAttribute(self, nodeid: int, attributes: typing.List[typing.Union[ ]], dataVersionFilters: typing.List[typing.Tuple[int, typing.Type[ClusterObjects.Cluster], int]] = None, returnClusterObject: bool = False, reportInterval: typing.Tuple[int, int] = None, - fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True): + fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True, + payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD): ''' Read a list of attributes from a target node, this is a wrapper of DeviceController.Read() @@ -1547,7 +1610,8 @@ async def ReadAttribute(self, nodeid: int, attributes: typing.List[typing.Union[ reportInterval=reportInterval, fabricFiltered=fabricFiltered, keepSubscriptions=keepSubscriptions, - autoResubscribe=autoResubscribe) + autoResubscribe=autoResubscribe, + payloadCapability=payloadCapability) if isinstance(res, ClusterAttribute.SubscriptionTransaction): return res else: @@ -1569,7 +1633,8 @@ async def ReadEvent(self, nodeid: int, events: typing.List[typing.Union[ fabricFiltered: bool = True, reportInterval: typing.Tuple[int, int] = None, keepSubscriptions: bool = False, - autoResubscribe: bool = True): + autoResubscribe: bool = True, + payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD): ''' Read a list of events from a target node, this is a wrapper of DeviceController.Read() @@ -1616,7 +1681,7 @@ async def ReadEvent(self, nodeid: int, events: typing.List[typing.Union[ ''' res = await self.Read(nodeid=nodeid, events=events, eventNumberFilter=eventNumberFilter, fabricFiltered=fabricFiltered, reportInterval=reportInterval, keepSubscriptions=keepSubscriptions, - autoResubscribe=autoResubscribe) + autoResubscribe=autoResubscribe, payloadCapability=payloadCapability) if isinstance(res, ClusterAttribute.SubscriptionTransaction): return res else: @@ -1764,7 +1829,7 @@ def _InitLib(self): self._dmLib.pychip_ScriptDevicePairingDelegate_SetExpectingPairingComplete.restype = PyChipError self._dmLib.pychip_GetConnectedDeviceByNodeId.argtypes = [ - c_void_p, c_uint64, py_object, _DeviceAvailableCallbackFunct] + c_void_p, c_uint64, py_object, _DeviceAvailableCallbackFunct, c_int] self._dmLib.pychip_GetConnectedDeviceByNodeId.restype = PyChipError self._dmLib.pychip_FreeOperationalDeviceProxy.argtypes = [ diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index e3c8683acd52ae..11b1dfb01c0760 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -928,7 +928,8 @@ async def read_single_attribute_expect_error( async def send_single_cmd( self, cmd: Clusters.ClusterObjects.ClusterCommand, dev_ctrl: ChipDeviceCtrl = None, node_id: int = None, endpoint: int = None, - timedRequestTimeoutMs: typing.Union[None, int] = None) -> object: + timedRequestTimeoutMs: typing.Union[None, int] = None, + payloadCapability: int = ChipDeviceCtrl.TransportPayloadCapability.MRP_PAYLOAD) -> object: if dev_ctrl is None: dev_ctrl = self.default_controller if node_id is None: @@ -936,7 +937,8 @@ async def send_single_cmd( if endpoint is None: endpoint = self.matter_test_config.endpoint - result = await dev_ctrl.SendCommand(nodeid=node_id, endpoint=endpoint, payload=cmd, timedRequestTimeoutMs=timedRequestTimeoutMs) + result = await dev_ctrl.SendCommand(nodeid=node_id, endpoint=endpoint, payload=cmd, timedRequestTimeoutMs=timedRequestTimeoutMs, + payloadCapability=payloadCapability) return result async def send_test_event_triggers(self, eventTrigger: int, enableKey: bytes = None): From ba949bff52cd3cf382e56916d14dcae57bc764bc Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 25 Jul 2024 16:03:15 -0400 Subject: [PATCH 15/49] Remove no-longer-used MTRDevice logic for truncating data version lists (#34183) * Remove no-longer-used MTRDevice logic for truncating data version lists After https://github.com/project-chip/connectedhomeip/pull/34111, ReadClient handles this logic itself, so the attempted truncation in MTRDevice was now dead code. * Address review comment. * Fix compile issues. * Address another review comment. * Address review comment. --- src/app/ReadClient.cpp | 22 ++++- src/darwin/Framework/CHIP/MTRDevice.mm | 132 ++++++++++--------------- 2 files changed, 74 insertions(+), 80 deletions(-) diff --git a/src/app/ReadClient.cpp b/src/app/ReadClient.cpp index 470c875fbaac30..51adf6e8ddc143 100644 --- a/src/app/ReadClient.cpp +++ b/src/app/ReadClient.cpp @@ -403,6 +403,11 @@ CHIP_ERROR ReadClient::BuildDataVersionFilterList(DataVersionFilterIBs::Builder const Span & aDataVersionFilters, bool & aEncodedDataVersionList) { +#if CHIP_PROGRESS_LOGGING + size_t encodedFilterCount = 0; + size_t irrelevantFilterCount = 0; + size_t skippedFilterCount = 0; +#endif for (auto & filter : aDataVersionFilters) { VerifyOrReturnError(filter.IsValidDataVersionFilter(), CHIP_ERROR_INVALID_ARGUMENT); @@ -420,6 +425,9 @@ CHIP_ERROR ReadClient::BuildDataVersionFilterList(DataVersionFilterIBs::Builder if (!intersected) { +#if CHIP_PROGRESS_LOGGING + ++irrelevantFilterCount; +#endif continue; } @@ -428,19 +436,31 @@ CHIP_ERROR ReadClient::BuildDataVersionFilterList(DataVersionFilterIBs::Builder CHIP_ERROR err = EncodeDataVersionFilter(aDataVersionFilterIBsBuilder, filter); if (err == CHIP_NO_ERROR) { +#if CHIP_PROGRESS_LOGGING + ++encodedFilterCount; +#endif aEncodedDataVersionList = true; } else if (err == CHIP_ERROR_NO_MEMORY || err == CHIP_ERROR_BUFFER_TOO_SMALL) { // Packet is full, ignore the rest of the list aDataVersionFilterIBsBuilder.Rollback(backup); - return CHIP_NO_ERROR; +#if CHIP_PROGRESS_LOGGING + ssize_t nonSkippedFilterCount = &filter - aDataVersionFilters.data(); + skippedFilterCount = aDataVersionFilters.size() - static_cast(nonSkippedFilterCount); +#endif // CHIP_PROGRESS_LOGGING + break; } else { return err; } } + + ChipLogProgress(DataManagement, + "%lu data version filters provided, %lu not relevant, %lu encoded, %lu skipped due to lack of space", + static_cast(aDataVersionFilters.size()), static_cast(irrelevantFilterCount), + static_cast(encodedFilterCount), static_cast(skippedFilterCount)); return CHIP_NO_ERROR; } diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm index 1c68d788943ce2..b8ef8694825e1f 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.mm +++ b/src/darwin/Framework/CHIP/MTRDevice.mm @@ -2277,32 +2277,26 @@ - (void)_removeCachedAttribute:(NSNumber *)attributeID fromCluster:(MTRClusterPa [clusterData removeValueForAttribute:attributeID]; } -- (void)_createDataVersionFilterListFromDictionary:(NSDictionary *)dataVersions dataVersionFilterList:(DataVersionFilter **)dataVersionFilterList count:(size_t *)count sizeReduction:(size_t)sizeReduction +- (void)_createDataVersionFilterListFromDictionary:(NSDictionary *)dataVersions dataVersionFilterList:(DataVersionFilter **)dataVersionFilterList count:(size_t *)count { - size_t maxDataVersionFilterSize = dataVersions.count; + size_t dataVersionFilterSize = dataVersions.count; // Check if any filter list should be generated - if (!dataVersions.count || (maxDataVersionFilterSize <= sizeReduction)) { + if (dataVersionFilterSize == 0) { *count = 0; *dataVersionFilterList = nullptr; return; } - maxDataVersionFilterSize -= sizeReduction; - DataVersionFilter * dataVersionFilterArray = new DataVersionFilter[maxDataVersionFilterSize]; + DataVersionFilter * dataVersionFilterArray = new DataVersionFilter[dataVersionFilterSize]; size_t i = 0; for (MTRClusterPath * path in dataVersions) { NSNumber * dataVersionNumber = dataVersions[path]; - if (dataVersionNumber) { - dataVersionFilterArray[i++] = DataVersionFilter(static_cast(path.endpoint.unsignedShortValue), static_cast(path.cluster.unsignedLongValue), static_cast(dataVersionNumber.unsignedLongValue)); - } - if (i == maxDataVersionFilterSize) { - break; - } + dataVersionFilterArray[i++] = DataVersionFilter(static_cast(path.endpoint.unsignedShortValue), static_cast(path.cluster.unsignedLongValue), static_cast(dataVersionNumber.unsignedLongValue)); } *dataVersionFilterList = dataVersionFilterArray; - *count = maxDataVersionFilterSize; + *count = dataVersionFilterSize; } - (void)_setupConnectivityMonitoring @@ -2530,75 +2524,55 @@ - (void)_setupSubscriptionWithReason:(NSString *)reason auto readClient = std::make_unique(InteractionModelEngine::GetInstance(), exchangeManager, clusterStateCache->GetBufferedCallback(), ReadClient::InteractionType::Subscribe); - // Subscribe with data version filter list and retry with smaller list if out of packet space - CHIP_ERROR err; - NSDictionary * dataVersions = [self _getCachedDataVersions]; - size_t dataVersionFilterListSizeReduction = 0; - for (;;) { - // Wildcard endpoint, cluster, attribute, event. - auto attributePath = std::make_unique(); - auto eventPath = std::make_unique(); - // We want to get event reports at the minInterval, not the maxInterval. - eventPath->mIsUrgentEvent = true; - ReadPrepareParams readParams(session.Value()); - - readParams.mMinIntervalFloorSeconds = 0; - // Select a max interval based on the device's claimed idle sleep interval. - auto idleSleepInterval = std::chrono::duration_cast( - session.Value()->GetRemoteMRPConfig().mIdleRetransTimeout); - - auto maxIntervalCeilingMin = System::Clock::Seconds32(MTR_DEVICE_SUBSCRIPTION_MAX_INTERVAL_MIN); - if (idleSleepInterval < maxIntervalCeilingMin) { - idleSleepInterval = maxIntervalCeilingMin; - } - - auto maxIntervalCeilingMax = System::Clock::Seconds32(MTR_DEVICE_SUBSCRIPTION_MAX_INTERVAL_MAX); - if (idleSleepInterval > maxIntervalCeilingMax) { - idleSleepInterval = maxIntervalCeilingMax; - } + // Wildcard endpoint, cluster, attribute, event. + auto attributePath = std::make_unique(); + auto eventPath = std::make_unique(); + // We want to get event reports at the minInterval, not the maxInterval. + eventPath->mIsUrgentEvent = true; + ReadPrepareParams readParams(session.Value()); + + readParams.mMinIntervalFloorSeconds = 0; + // Select a max interval based on the device's claimed idle sleep interval. + auto idleSleepInterval = std::chrono::duration_cast( + session.Value()->GetRemoteMRPConfig().mIdleRetransTimeout); + + auto maxIntervalCeilingMin = System::Clock::Seconds32(MTR_DEVICE_SUBSCRIPTION_MAX_INTERVAL_MIN); + if (idleSleepInterval < maxIntervalCeilingMin) { + idleSleepInterval = maxIntervalCeilingMin; + } + + auto maxIntervalCeilingMax = System::Clock::Seconds32(MTR_DEVICE_SUBSCRIPTION_MAX_INTERVAL_MAX); + if (idleSleepInterval > maxIntervalCeilingMax) { + idleSleepInterval = maxIntervalCeilingMax; + } #ifdef DEBUG - if (maxIntervalOverride.HasValue()) { - idleSleepInterval = maxIntervalOverride.Value(); - } -#endif - readParams.mMaxIntervalCeilingSeconds = static_cast(idleSleepInterval.count()); - - readParams.mpAttributePathParamsList = attributePath.get(); - readParams.mAttributePathParamsListSize = 1; - readParams.mpEventPathParamsList = eventPath.get(); - readParams.mEventPathParamsListSize = 1; - readParams.mKeepSubscriptions = true; - readParams.mIsFabricFiltered = false; - size_t dataVersionFilterListSize = 0; - DataVersionFilter * dataVersionFilterList; - [self _createDataVersionFilterListFromDictionary:dataVersions dataVersionFilterList:&dataVersionFilterList count:&dataVersionFilterListSize sizeReduction:dataVersionFilterListSizeReduction]; - readParams.mDataVersionFilterListSize = dataVersionFilterListSize; - readParams.mpDataVersionFilterList = dataVersionFilterList; - attributePath.release(); - eventPath.release(); - - // TODO: Change from local filter list generation to rehydrating ClusterStateCache ot take advantage of existing filter list sorting algorithm - - // SendAutoResubscribeRequest cleans up the params, even on failure. - err = readClient->SendAutoResubscribeRequest(std::move(readParams)); - if (err == CHIP_NO_ERROR) { - break; - } - - // If error is not a "no memory" issue, then break and go through regular resubscribe logic - if (err != CHIP_ERROR_NO_MEMORY) { - break; - } - - // If "no memory" error is not caused by data version filter list, break as well - if (!dataVersionFilterListSize) { - break; - } - - // Now "no memory" could mean subscribe request packet space ran out. Reduce size and try again immediately - dataVersionFilterListSizeReduction++; + if (maxIntervalOverride.HasValue()) { + idleSleepInterval = maxIntervalOverride.Value(); } +#endif + readParams.mMaxIntervalCeilingSeconds = static_cast(idleSleepInterval.count()); + + readParams.mpAttributePathParamsList = attributePath.get(); + readParams.mAttributePathParamsListSize = 1; + readParams.mpEventPathParamsList = eventPath.get(); + readParams.mEventPathParamsListSize = 1; + readParams.mKeepSubscriptions = true; + readParams.mIsFabricFiltered = false; + + // Subscribe with data version filter list from our cache. + size_t dataVersionFilterListSize = 0; + DataVersionFilter * dataVersionFilterList; + [self _createDataVersionFilterListFromDictionary:[self _getCachedDataVersions] dataVersionFilterList:&dataVersionFilterList count:&dataVersionFilterListSize]; + + readParams.mDataVersionFilterListSize = dataVersionFilterListSize; + readParams.mpDataVersionFilterList = dataVersionFilterList; + attributePath.release(); + eventPath.release(); + + // TODO: Change from local filter list generation to rehydrating ClusterStateCache to take advantage of existing filter list sorting algorithm + // SendAutoResubscribeRequest cleans up the params, even on failure. + CHIP_ERROR err = readClient->SendAutoResubscribeRequest(std::move(readParams)); if (err != CHIP_NO_ERROR) { NSError * error = [MTRError errorForCHIPErrorCode:err logContext:self]; MTR_LOG_ERROR("%@ SendAutoResubscribeRequest error %@", self, error); @@ -2610,7 +2584,7 @@ - (void)_setupSubscriptionWithReason:(NSString *)reason return; } - MTR_LOG("%@ Subscribe with data version list size %lu, reduced by %lu", self, static_cast(dataVersions.count), static_cast(dataVersionFilterListSizeReduction)); + MTR_LOG("%@ Subscribe with data version list size %lu", self, static_cast(dataVersionFilterListSize)); // Callback and ClusterStateCache and ReadClient will be deleted // when OnDone is called. From 434b3f855473853d6daeb3f00cb0f23c02c3a8b4 Mon Sep 17 00:00:00 2001 From: Terence Hampson Date: Thu, 25 Jul 2024 17:51:32 -0400 Subject: [PATCH 16/49] Add fabric sync related changed into bridge device info cluster (#34336) --- .../placeholder/linux/apps/app1/config.matter | 11 +++ .../placeholder/linux/apps/app2/config.matter | 11 +++ .../chip/bridged-device-basic-information.xml | 14 ++++ .../data_model/controller-clusters.matter | 11 +++ .../chip/devicecontroller/ChipClusters.java | 16 ++++ .../devicecontroller/ChipEventStructs.java | 46 ++++++++++++ .../devicecontroller/ClusterIDMapping.java | 6 +- .../devicecontroller/ClusterInfoMapping.java | 12 +++ ...sicInformationClusterActiveChangedEvent.kt | 56 ++++++++++++++ .../chip/devicecontroller/cluster/files.gni | 1 + .../BridgedDeviceBasicInformationCluster.kt | 21 ++++++ ...sicInformationClusterActiveChangedEvent.kt | 56 ++++++++++++++ .../java/matter/controller/cluster/files.gni | 1 + .../CHIPEventTLVValueDecoder.cpp | 39 ++++++++++ .../python/chip/clusters/CHIPClusters.py | 6 ++ .../python/chip/clusters/Objects.py | 37 ++++++++++ .../CHIP/zap-generated/MTRBaseClusters.h | 13 ++++ .../CHIP/zap-generated/MTRBaseClusters.mm | 29 ++++++++ .../CHIP/zap-generated/MTRClusterConstants.h | 6 ++ .../CHIP/zap-generated/MTRClusters.h | 8 +- .../CHIP/zap-generated/MTRClusters.mm | 31 ++++++++ .../zap-generated/MTRCommandPayloadsObjc.h | 28 +++++++ .../zap-generated/MTRCommandPayloadsObjc.mm | 73 ++++++++++++++++++ .../MTRCommandPayloads_Internal.h | 6 ++ .../zap-generated/MTREventTLVValueDecoder.mm | 17 +++++ .../CHIP/zap-generated/MTRStructsObjc.h | 5 ++ .../CHIP/zap-generated/MTRStructsObjc.mm | 27 +++++++ .../app-common/zap-generated/callback.h | 6 ++ .../app-common/zap-generated/cluster-enums.h | 6 ++ .../zap-generated/cluster-objects.cpp | 65 +++++++++++++++- .../zap-generated/cluster-objects.h | 74 +++++++++++++++++++ .../app-common/zap-generated/ids/Commands.h | 10 +++ .../app-common/zap-generated/ids/Events.h | 4 + .../zap-generated/cluster/Commands.h | 44 ++++++++++- .../cluster/logging/DataModelLogger.cpp | 21 ++++++ .../cluster/logging/DataModelLogger.h | 2 + .../zap-generated/cluster/Commands.h | 52 +++++++++++++ 37 files changed, 865 insertions(+), 6 deletions(-) create mode 100644 src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/BridgedDeviceBasicInformationClusterActiveChangedEvent.kt create mode 100644 src/controller/java/generated/java/matter/controller/cluster/eventstructs/BridgedDeviceBasicInformationClusterActiveChangedEvent.kt diff --git a/examples/placeholder/linux/apps/app1/config.matter b/examples/placeholder/linux/apps/app1/config.matter index 2f250369cd3c05..1955b363c01a4d 100644 --- a/examples/placeholder/linux/apps/app1/config.matter +++ b/examples/placeholder/linux/apps/app1/config.matter @@ -2320,6 +2320,10 @@ cluster BridgedDeviceBasicInformation = 57 { kFabric = 5; } + bitmap Feature : bitmap32 { + kBridgedICDSupport = 0x100000; + } + struct ProductAppearanceStruct { ProductFinishEnum finish = 0; nullable ColorEnum primaryColor = 1; @@ -2339,6 +2343,10 @@ cluster BridgedDeviceBasicInformation = 57 { boolean reachableNewValue = 0; } + info event ActiveChanged = 128 { + int32u promisedActiveDuration = 0; + } + readonly attribute optional char_string<32> vendorName = 1; readonly attribute optional vendor_id vendorID = 2; readonly attribute optional char_string<32> productName = 3; @@ -2361,6 +2369,9 @@ cluster BridgedDeviceBasicInformation = 57 { readonly attribute attrib_id attributeList[] = 65531; readonly attribute bitmap32 featureMap = 65532; readonly attribute int16u clusterRevision = 65533; + + /** The server SHALL attempt to keep the devices specified active for StayActiveDuration milliseconds when they are next active. */ + command KeepActive(): DefaultSuccess = 128; } /** This cluster exposes interactions with a switch device, for the purpose of using those interactions by other devices. diff --git a/examples/placeholder/linux/apps/app2/config.matter b/examples/placeholder/linux/apps/app2/config.matter index a68dc9ce2b63f2..8debe20800dc40 100644 --- a/examples/placeholder/linux/apps/app2/config.matter +++ b/examples/placeholder/linux/apps/app2/config.matter @@ -2277,6 +2277,10 @@ cluster BridgedDeviceBasicInformation = 57 { kFabric = 5; } + bitmap Feature : bitmap32 { + kBridgedICDSupport = 0x100000; + } + struct ProductAppearanceStruct { ProductFinishEnum finish = 0; nullable ColorEnum primaryColor = 1; @@ -2296,6 +2300,10 @@ cluster BridgedDeviceBasicInformation = 57 { boolean reachableNewValue = 0; } + info event ActiveChanged = 128 { + int32u promisedActiveDuration = 0; + } + readonly attribute optional char_string<32> vendorName = 1; readonly attribute optional vendor_id vendorID = 2; readonly attribute optional char_string<32> productName = 3; @@ -2318,6 +2326,9 @@ cluster BridgedDeviceBasicInformation = 57 { readonly attribute attrib_id attributeList[] = 65531; readonly attribute bitmap32 featureMap = 65532; readonly attribute int16u clusterRevision = 65533; + + /** The server SHALL attempt to keep the devices specified active for StayActiveDuration milliseconds when they are next active. */ + command KeepActive(): DefaultSuccess = 128; } /** This cluster exposes interactions with a switch device, for the purpose of using those interactions by other devices. diff --git a/src/app/zap-templates/zcl/data-model/chip/bridged-device-basic-information.xml b/src/app/zap-templates/zcl/data-model/chip/bridged-device-basic-information.xml index 702ea8754795b1..ff118a8a80c162 100644 --- a/src/app/zap-templates/zcl/data-model/chip/bridged-device-basic-information.xml +++ b/src/app/zap-templates/zcl/data-model/chip/bridged-device-basic-information.xml @@ -71,6 +71,12 @@ limitations under the License. true + + + + + + VendorName VendorID ProductName @@ -88,6 +94,10 @@ limitations under the License. UniqueID ProductAppearance + + The server SHALL attempt to keep the devices specified active for StayActiveDuration milliseconds when they are next active. + + The StartUp event SHALL be emitted by a Node as soon as reasonable after completing a boot or reboot process. @@ -102,6 +112,10 @@ limitations under the License. This event SHALL be generated when there is a change in the Reachable attribute. + + This event (when supported) SHALL be generated the next time a bridged device becomes active after a KeepActive command is received. + + diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter index 6acf9fd8c4fb2f..39a4d56bfd69b8 100644 --- a/src/controller/data_model/controller-clusters.matter +++ b/src/controller/data_model/controller-clusters.matter @@ -2225,6 +2225,10 @@ cluster BridgedDeviceBasicInformation = 57 { kFabric = 5; } + bitmap Feature : bitmap32 { + kBridgedICDSupport = 0x100000; + } + struct ProductAppearanceStruct { ProductFinishEnum finish = 0; nullable ColorEnum primaryColor = 1; @@ -2244,6 +2248,10 @@ cluster BridgedDeviceBasicInformation = 57 { boolean reachableNewValue = 0; } + info event ActiveChanged = 128 { + int32u promisedActiveDuration = 0; + } + readonly attribute optional char_string<32> vendorName = 1; readonly attribute optional vendor_id vendorID = 2; readonly attribute optional char_string<32> productName = 3; @@ -2266,6 +2274,9 @@ cluster BridgedDeviceBasicInformation = 57 { readonly attribute attrib_id attributeList[] = 65531; readonly attribute bitmap32 featureMap = 65532; readonly attribute int16u clusterRevision = 65533; + + /** The server SHALL attempt to keep the devices specified active for StayActiveDuration milliseconds when they are next active. */ + command KeepActive(): DefaultSuccess = 128; } /** This cluster exposes interactions with a switch device, for the purpose of using those interactions by other devices. diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java index 66338f93c0b225..bbdc1dea185456 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java @@ -14963,6 +14963,22 @@ public long initWithDevice(long devicePtr, int endpointId) { return 0L; } + public void keepActive(DefaultClusterCallback callback) { + keepActive(callback, 0); + } + + public void keepActive(DefaultClusterCallback callback, int timedInvokeTimeoutMs) { + final long commandId = 128L; + + ArrayList elements = new ArrayList<>(); + StructType commandArgs = new StructType(elements); + invoke(new InvokeCallbackImpl(callback) { + @Override + public void onResponse(StructType invokeStructValue) { + callback.onSuccess(); + }}, commandId, commandArgs, timedInvokeTimeoutMs); + } + public interface ProductAppearanceAttributeCallback extends BaseAttributeCallback { void onSuccess(ChipStructs.BridgedDeviceBasicInformationClusterProductAppearanceStruct value); } diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipEventStructs.java b/src/controller/java/generated/java/chip/devicecontroller/ChipEventStructs.java index 6ab2b64ce65000..e33980d8a6e7d9 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipEventStructs.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipEventStructs.java @@ -1899,6 +1899,52 @@ public String toString() { return output.toString(); } } +public static class BridgedDeviceBasicInformationClusterActiveChangedEvent { + public Long promisedActiveDuration; + private static final long PROMISED_ACTIVE_DURATION_ID = 0L; + + public BridgedDeviceBasicInformationClusterActiveChangedEvent( + Long promisedActiveDuration + ) { + this.promisedActiveDuration = promisedActiveDuration; + } + + public StructType encodeTlv() { + ArrayList values = new ArrayList<>(); + values.add(new StructElement(PROMISED_ACTIVE_DURATION_ID, new UIntType(promisedActiveDuration))); + + return new StructType(values); + } + + public static BridgedDeviceBasicInformationClusterActiveChangedEvent decodeTlv(BaseTLVType tlvValue) { + if (tlvValue == null || tlvValue.type() != TLVType.Struct) { + return null; + } + Long promisedActiveDuration = null; + for (StructElement element: ((StructType)tlvValue).value()) { + if (element.contextTagNum() == PROMISED_ACTIVE_DURATION_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + promisedActiveDuration = castingValue.value(Long.class); + } + } + } + return new BridgedDeviceBasicInformationClusterActiveChangedEvent( + promisedActiveDuration + ); + } + + @Override + public String toString() { + StringBuilder output = new StringBuilder(); + output.append("BridgedDeviceBasicInformationClusterActiveChangedEvent {\n"); + output.append("\tpromisedActiveDuration: "); + output.append(promisedActiveDuration); + output.append("\n"); + output.append("}\n"); + return output.toString(); + } +} public static class SwitchClusterSwitchLatchedEvent { public Integer newPosition; private static final long NEW_POSITION_ID = 0L; diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java index 6959f3a3d83caa..72972b2554c102 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java @@ -4455,7 +4455,8 @@ public enum Event { StartUp(0L), ShutDown(1L), Leave(2L), - ReachableChanged(3L),; + ReachableChanged(3L), + ActiveChanged(128L),; private final long id; Event(long id) { this.id = id; @@ -4475,7 +4476,8 @@ public static Event value(long id) throws NoSuchFieldError { } } - public enum Command {; + public enum Command { + KeepActive(128L),; private final long id; Command(long id) { this.id = id; diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java index 99bae94690439d..98a276983b8670 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java @@ -24028,6 +24028,18 @@ public Map> getCommandMap() { Map bridgedDeviceBasicInformationClusterInteractionInfoMap = new LinkedHashMap<>(); + Map bridgedDeviceBasicInformationkeepActiveCommandParams = new LinkedHashMap(); + InteractionInfo bridgedDeviceBasicInformationkeepActiveInteractionInfo = new InteractionInfo( + (cluster, callback, commandArguments) -> { + ((ChipClusters.BridgedDeviceBasicInformationCluster) cluster) + .keepActive((DefaultClusterCallback) callback + ); + }, + () -> new DelegatedDefaultClusterCallback(), + bridgedDeviceBasicInformationkeepActiveCommandParams + ); + bridgedDeviceBasicInformationClusterInteractionInfoMap.put("keepActive", bridgedDeviceBasicInformationkeepActiveInteractionInfo); + commandMap.put("bridgedDeviceBasicInformation", bridgedDeviceBasicInformationClusterInteractionInfoMap); Map switchClusterInteractionInfoMap = new LinkedHashMap<>(); diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/BridgedDeviceBasicInformationClusterActiveChangedEvent.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/BridgedDeviceBasicInformationClusterActiveChangedEvent.kt new file mode 100644 index 00000000000000..73be6ff7c1042e --- /dev/null +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/BridgedDeviceBasicInformationClusterActiveChangedEvent.kt @@ -0,0 +1,56 @@ +/* + * + * 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. + */ +package chip.devicecontroller.cluster.eventstructs + +import chip.devicecontroller.cluster.* +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class BridgedDeviceBasicInformationClusterActiveChangedEvent(val promisedActiveDuration: ULong) { + override fun toString(): String = buildString { + append("BridgedDeviceBasicInformationClusterActiveChangedEvent {\n") + append("\tpromisedActiveDuration : $promisedActiveDuration\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + put(ContextSpecificTag(TAG_PROMISED_ACTIVE_DURATION), promisedActiveDuration) + endStructure() + } + } + + companion object { + private const val TAG_PROMISED_ACTIVE_DURATION = 0 + + fun fromTlv( + tlvTag: Tag, + tlvReader: TlvReader, + ): BridgedDeviceBasicInformationClusterActiveChangedEvent { + tlvReader.enterStructure(tlvTag) + val promisedActiveDuration = + tlvReader.getULong(ContextSpecificTag(TAG_PROMISED_ACTIVE_DURATION)) + + tlvReader.exitContainer() + + return BridgedDeviceBasicInformationClusterActiveChangedEvent(promisedActiveDuration) + } + } +} diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni b/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni index f50c3e2c82b56e..90ce4a42ae206f 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni @@ -162,6 +162,7 @@ eventstructs_sources = [ "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/BooleanStateClusterStateChangeEvent.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/BooleanStateConfigurationClusterAlarmsStateChangedEvent.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/BooleanStateConfigurationClusterSensorFaultEvent.kt", + "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/BridgedDeviceBasicInformationClusterActiveChangedEvent.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/BridgedDeviceBasicInformationClusterReachableChangedEvent.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/BridgedDeviceBasicInformationClusterStartUpEvent.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/CommissionerControlClusterCommissioningRequestResultEvent.kt", diff --git a/src/controller/java/generated/java/matter/controller/cluster/clusters/BridgedDeviceBasicInformationCluster.kt b/src/controller/java/generated/java/matter/controller/cluster/clusters/BridgedDeviceBasicInformationCluster.kt index 73f0502017eee1..45adb1bb8f7820 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/clusters/BridgedDeviceBasicInformationCluster.kt +++ b/src/controller/java/generated/java/matter/controller/cluster/clusters/BridgedDeviceBasicInformationCluster.kt @@ -23,6 +23,8 @@ import java.util.logging.Logger import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.transform import matter.controller.BooleanSubscriptionState +import matter.controller.InvokeRequest +import matter.controller.InvokeResponse import matter.controller.MatterController import matter.controller.ReadData import matter.controller.ReadRequest @@ -36,6 +38,7 @@ import matter.controller.WriteRequests import matter.controller.WriteResponse import matter.controller.cluster.structs.* import matter.controller.model.AttributePath +import matter.controller.model.CommandPath import matter.tlv.AnonymousTag import matter.tlv.TlvReader import matter.tlv.TlvWriter @@ -97,6 +100,24 @@ class BridgedDeviceBasicInformationCluster( object SubscriptionEstablished : AttributeListAttributeSubscriptionState() } + suspend fun keepActive(timedInvokeTimeout: Duration? = null) { + val commandId: UInt = 128u + + val tlvWriter = TlvWriter() + tlvWriter.startStructure(AnonymousTag) + tlvWriter.endStructure() + + val request: InvokeRequest = + InvokeRequest( + CommandPath(endpointId, clusterId = CLUSTER_ID, commandId), + tlvPayload = tlvWriter.getEncoded(), + timedRequest = timedInvokeTimeout, + ) + + val response: InvokeResponse = controller.invoke(request) + logger.log(Level.FINE, "Invoke command succeeded: ${response}") + } + suspend fun readVendorNameAttribute(): String? { val ATTRIBUTE_ID: UInt = 1u diff --git a/src/controller/java/generated/java/matter/controller/cluster/eventstructs/BridgedDeviceBasicInformationClusterActiveChangedEvent.kt b/src/controller/java/generated/java/matter/controller/cluster/eventstructs/BridgedDeviceBasicInformationClusterActiveChangedEvent.kt new file mode 100644 index 00000000000000..7713cb0faf07b3 --- /dev/null +++ b/src/controller/java/generated/java/matter/controller/cluster/eventstructs/BridgedDeviceBasicInformationClusterActiveChangedEvent.kt @@ -0,0 +1,56 @@ +/* + * + * 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. + */ +package matter.controller.cluster.eventstructs + +import matter.controller.cluster.* +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class BridgedDeviceBasicInformationClusterActiveChangedEvent(val promisedActiveDuration: UInt) { + override fun toString(): String = buildString { + append("BridgedDeviceBasicInformationClusterActiveChangedEvent {\n") + append("\tpromisedActiveDuration : $promisedActiveDuration\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + put(ContextSpecificTag(TAG_PROMISED_ACTIVE_DURATION), promisedActiveDuration) + endStructure() + } + } + + companion object { + private const val TAG_PROMISED_ACTIVE_DURATION = 0 + + fun fromTlv( + tlvTag: Tag, + tlvReader: TlvReader, + ): BridgedDeviceBasicInformationClusterActiveChangedEvent { + tlvReader.enterStructure(tlvTag) + val promisedActiveDuration = + tlvReader.getUInt(ContextSpecificTag(TAG_PROMISED_ACTIVE_DURATION)) + + tlvReader.exitContainer() + + return BridgedDeviceBasicInformationClusterActiveChangedEvent(promisedActiveDuration) + } + } +} diff --git a/src/controller/java/generated/java/matter/controller/cluster/files.gni b/src/controller/java/generated/java/matter/controller/cluster/files.gni index 856e286c2a2e7d..14ff82b93897cd 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/files.gni +++ b/src/controller/java/generated/java/matter/controller/cluster/files.gni @@ -162,6 +162,7 @@ matter_eventstructs_sources = [ "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/BooleanStateClusterStateChangeEvent.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/BooleanStateConfigurationClusterAlarmsStateChangedEvent.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/BooleanStateConfigurationClusterSensorFaultEvent.kt", + "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/BridgedDeviceBasicInformationClusterActiveChangedEvent.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/BridgedDeviceBasicInformationClusterReachableChangedEvent.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/BridgedDeviceBasicInformationClusterStartUpEvent.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/CommissionerControlClusterCommissioningRequestResultEvent.kt", diff --git a/src/controller/java/zap-generated/CHIPEventTLVValueDecoder.cpp b/src/controller/java/zap-generated/CHIPEventTLVValueDecoder.cpp index 0dbed58b4b93d3..8d7826aed4d310 100644 --- a/src/controller/java/zap-generated/CHIPEventTLVValueDecoder.cpp +++ b/src/controller/java/zap-generated/CHIPEventTLVValueDecoder.cpp @@ -2159,6 +2159,45 @@ jobject DecodeEventValue(const app::ConcreteEventPath & aPath, TLV::TLVReader & return value; } + case Events::ActiveChanged::Id: { + Events::ActiveChanged::DecodableType cppValue; + *aError = app::DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) + { + return nullptr; + } + jobject value_promisedActiveDuration; + std::string value_promisedActiveDurationClassName = "java/lang/Long"; + std::string value_promisedActiveDurationCtorSignature = "(J)V"; + jlong jnivalue_promisedActiveDuration = static_cast(cppValue.promisedActiveDuration); + chip::JniReferences::GetInstance().CreateBoxedObject( + value_promisedActiveDurationClassName.c_str(), value_promisedActiveDurationCtorSignature.c_str(), + jnivalue_promisedActiveDuration, value_promisedActiveDuration); + + jclass activeChangedStructClass; + err = chip::JniReferences::GetInstance().GetLocalClassRef( + env, "chip/devicecontroller/ChipEventStructs$BridgedDeviceBasicInformationClusterActiveChangedEvent", + activeChangedStructClass); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Could not find class ChipEventStructs$BridgedDeviceBasicInformationClusterActiveChangedEvent"); + return nullptr; + } + + jmethodID activeChangedStructCtor; + err = chip::JniReferences::GetInstance().FindMethod(env, activeChangedStructClass, "", "(Ljava/lang/Long;)V", + &activeChangedStructCtor); + if (err != CHIP_NO_ERROR || activeChangedStructCtor == nullptr) + { + ChipLogError(Zcl, + "Could not find ChipEventStructs$BridgedDeviceBasicInformationClusterActiveChangedEvent constructor"); + return nullptr; + } + + jobject value = env->NewObject(activeChangedStructClass, activeChangedStructCtor, value_promisedActiveDuration); + + return value; + } default: *aError = CHIP_ERROR_IM_MALFORMED_EVENT_PATH_IB; break; diff --git a/src/controller/python/chip/clusters/CHIPClusters.py b/src/controller/python/chip/clusters/CHIPClusters.py index 1f3b1305b5f52f..c9f559f9f61915 100644 --- a/src/controller/python/chip/clusters/CHIPClusters.py +++ b/src/controller/python/chip/clusters/CHIPClusters.py @@ -3246,6 +3246,12 @@ class ChipClusters: "clusterName": "BridgedDeviceBasicInformation", "clusterId": 0x00000039, "commands": { + 0x00000080: { + "commandId": 0x00000080, + "commandName": "KeepActive", + "args": { + }, + }, }, "attributes": { 0x00000001: { diff --git a/src/controller/python/chip/clusters/Objects.py b/src/controller/python/chip/clusters/Objects.py index bf94c7ea84ea08..13c6fe3d9f4dab 100644 --- a/src/controller/python/chip/clusters/Objects.py +++ b/src/controller/python/chip/clusters/Objects.py @@ -11425,6 +11425,10 @@ class ProductFinishEnum(MatterIntEnum): # enum value. This specific should never be transmitted. kUnknownEnumValue = 6, + class Bitmaps: + class Feature(IntFlag): + kBridgedICDSupport = 0x100000 + class Structs: @dataclass class ProductAppearanceStruct(ClusterObject): @@ -11439,6 +11443,20 @@ def descriptor(cls) -> ClusterObjectDescriptor: finish: 'BridgedDeviceBasicInformation.Enums.ProductFinishEnum' = 0 primaryColor: 'typing.Union[Nullable, BridgedDeviceBasicInformation.Enums.ColorEnum]' = NullValue + class Commands: + @dataclass + class KeepActive(ClusterCommand): + cluster_id: typing.ClassVar[int] = 0x00000039 + command_id: typing.ClassVar[int] = 0x00000080 + is_client: typing.ClassVar[bool] = True + response_type: typing.ClassVar[str] = None + + @ChipUtility.classproperty + def descriptor(cls) -> ClusterObjectDescriptor: + return ClusterObjectDescriptor( + Fields=[ + ]) + class Attributes: @dataclass class VendorName(ClusterAttributeDescriptor): @@ -11863,6 +11881,25 @@ def descriptor(cls) -> ClusterObjectDescriptor: reachableNewValue: 'bool' = False + @dataclass + class ActiveChanged(ClusterEvent): + @ChipUtility.classproperty + def cluster_id(cls) -> int: + return 0x00000039 + + @ChipUtility.classproperty + def event_id(cls) -> int: + return 0x00000080 + + @ChipUtility.classproperty + def descriptor(cls) -> ClusterObjectDescriptor: + return ClusterObjectDescriptor( + Fields=[ + ClusterObjectFieldDescriptor(Label="promisedActiveDuration", Tag=0, Type=uint), + ]) + + promisedActiveDuration: 'uint' = 0 + @dataclass class Switch(Cluster): diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h index c1ad2950a339f2..7c3e36f0797323 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h @@ -3670,6 +3670,15 @@ MTR_PROVISIONALLY_AVAILABLE MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) @interface MTRBaseClusterBridgedDeviceBasicInformation : MTRGenericBaseCluster +/** + * Command KeepActive + * + * The server SHALL attempt to keep the devices specified active for StayActiveDuration milliseconds when they are next active. + */ +- (void)keepActiveWithParams:(MTRBridgedDeviceBasicInformationClusterKeepActiveParams * _Nullable)params completion:(MTRStatusCompletion)completion MTR_PROVISIONALLY_AVAILABLE; +- (void)keepActiveWithCompletion:(MTRStatusCompletion)completion + MTR_PROVISIONALLY_AVAILABLE; + - (void)readAttributeVendorNameWithCompletion:(void (^)(NSString * _Nullable value, NSError * _Nullable error))completion MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); - (void)subscribeAttributeVendorNameWithParams:(MTRSubscribeParams *)params subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished @@ -17994,6 +18003,10 @@ typedef NS_ENUM(uint8_t, MTRBridgedDeviceBasicInformationProductFinish) { MTRBridgedDeviceBasicInformationProductFinishFabric MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0)) = 0x05, } MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0)); +typedef NS_OPTIONS(uint32_t, MTRBridgedDeviceBasicInformationFeature) { + MTRBridgedDeviceBasicInformationFeatureBridgedICDSupport MTR_PROVISIONALLY_AVAILABLE = 0x100000, +} MTR_PROVISIONALLY_AVAILABLE; + typedef NS_OPTIONS(uint32_t, MTRSwitchFeature) { MTRSwitchFeatureLatchingSwitch MTR_AVAILABLE(ios(16.2), macos(13.1), watchos(9.2), tvos(16.2)) = 0x1, MTRSwitchFeatureMomentarySwitch MTR_AVAILABLE(ios(16.2), macos(13.1), watchos(9.2), tvos(16.2)) = 0x2, diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm index b4f9ee8e82e653..ddb25fb19bd0a8 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm @@ -30658,6 +30658,35 @@ + (void)readAttributeClusterRevisionWithClusterStateCache:(MTRClusterStateCacheC @implementation MTRBaseClusterBridgedDeviceBasicInformation +- (void)keepActiveWithCompletion:(MTRStatusCompletion)completion +{ + [self keepActiveWithParams:nil completion:completion]; +} +- (void)keepActiveWithParams:(MTRBridgedDeviceBasicInformationClusterKeepActiveParams * _Nullable)params completion:(MTRStatusCompletion)completion +{ + if (params == nil) { + params = [[MTRBridgedDeviceBasicInformationClusterKeepActiveParams + alloc] init]; + } + + auto responseHandler = ^(id _Nullable response, NSError * _Nullable error) { + completion(error); + }; + + auto * timedInvokeTimeoutMs = params.timedInvokeTimeoutMs; + + using RequestType = BridgedDeviceBasicInformation::Commands::KeepActive::Type; + [self.device _invokeKnownCommandWithEndpointID:self.endpointID + clusterID:@(RequestType::GetClusterId()) + commandID:@(RequestType::GetCommandId()) + commandPayload:params + timedInvokeTimeout:timedInvokeTimeoutMs + serverSideProcessingTimeout:params.serverSideProcessingTimeout + responseClass:nil + queue:self.callbackQueue + completion:responseHandler]; +} + - (void)readAttributeVendorNameWithCompletion:(void (^)(NSString * _Nullable value, NSError * _Nullable error))completion { using TypeInfo = BridgedDeviceBasicInformation::Attributes::VendorName::TypeInfo; diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h b/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h index 094d58964241fa..ff6351d9a50ff3 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h @@ -6158,6 +6158,11 @@ typedef NS_ENUM(uint32_t, MTRCommandIDType) { MTRCommandIDTypeClusterTimeSynchronizationCommandSetDSTOffsetID MTR_PROVISIONALLY_AVAILABLE = 0x00000004, MTRCommandIDTypeClusterTimeSynchronizationCommandSetDefaultNTPID MTR_PROVISIONALLY_AVAILABLE = 0x00000005, + // Cluster BridgedDeviceBasic deprecated command id names + + // Cluster BridgedDeviceBasicInformation commands + MTRCommandIDTypeClusterBridgedDeviceBasicInformationCommandKeepActiveID MTR_PROVISIONALLY_AVAILABLE = 0x00000080, + // Cluster AdministratorCommissioning deprecated command id names MTRClusterAdministratorCommissioningCommandOpenCommissioningWindowID MTR_DEPRECATED("Please use MTRCommandIDTypeClusterAdministratorCommissioningCommandOpenCommissioningWindowID", ios(16.1, 16.4), macos(13.0, 13.3), watchos(9.1, 9.4), tvos(16.1, 16.4)) @@ -7246,6 +7251,7 @@ typedef NS_ENUM(uint32_t, MTREventIDType) { MTREventIDTypeClusterBridgedDeviceBasicInformationEventShutDownID MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) = 0x00000001, MTREventIDTypeClusterBridgedDeviceBasicInformationEventLeaveID MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) = 0x00000002, MTREventIDTypeClusterBridgedDeviceBasicInformationEventReachableChangedID MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) = 0x00000003, + MTREventIDTypeClusterBridgedDeviceBasicInformationEventActiveChangedID MTR_PROVISIONALLY_AVAILABLE = 0x00000080, // Cluster Switch deprecated event names MTRClusterSwitchEventSwitchLatchedID diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h index 7c4d66189c3b4d..a30b71bc75cb22 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h @@ -1699,6 +1699,10 @@ MTR_PROVISIONALLY_AVAILABLE MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) @interface MTRClusterBridgedDeviceBasicInformation : MTRGenericCluster +- (void)keepActiveWithParams:(MTRBridgedDeviceBasicInformationClusterKeepActiveParams * _Nullable)params expectedValues:(NSArray *> * _Nullable)expectedDataValueDictionaries expectedValueInterval:(NSNumber * _Nullable)expectedValueIntervalMs completion:(MTRStatusCompletion)completion MTR_PROVISIONALLY_AVAILABLE; +- (void)keepActiveWithExpectedValues:(NSArray *> * _Nullable)expectedValues expectedValueInterval:(NSNumber * _Nullable)expectedValueIntervalMs completion:(MTRStatusCompletion)completion + MTR_PROVISIONALLY_AVAILABLE; + - (NSDictionary * _Nullable)readAttributeVendorNameWithParams:(MTRReadParams * _Nullable)params MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); - (NSDictionary * _Nullable)readAttributeVendorIDWithParams:(MTRReadParams * _Nullable)params MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); @@ -1753,8 +1757,8 @@ MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) @interface MTRClusterBridgedDeviceBasicInformation (Availability) /** - * The queue is currently unused, but may be used in the future for calling completions - * for command invocations if commands are added to this cluster. + * For all instance methods that take a completion (i.e. command invocations), + * the completion will be called on the provided queue. */ - (instancetype _Nullable)initWithDevice:(MTRDevice *)device endpointID:(NSNumber *)endpointID diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm index b638b2c8f56890..bab799b843c24f 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm @@ -4944,6 +4944,37 @@ - (void)setDefaultNTPWithParams:(MTRTimeSynchronizationClusterSetDefaultNTPParam @implementation MTRClusterBridgedDeviceBasicInformation +- (void)keepActiveWithExpectedValues:(NSArray *> *)expectedValues expectedValueInterval:(NSNumber *)expectedValueIntervalMs completion:(MTRStatusCompletion)completion +{ + [self keepActiveWithParams:nil expectedValues:expectedValues expectedValueInterval:expectedValueIntervalMs completion:completion]; +} +- (void)keepActiveWithParams:(MTRBridgedDeviceBasicInformationClusterKeepActiveParams * _Nullable)params expectedValues:(NSArray *> * _Nullable)expectedValues expectedValueInterval:(NSNumber * _Nullable)expectedValueIntervalMs completion:(MTRStatusCompletion)completion +{ + if (params == nil) { + params = [[MTRBridgedDeviceBasicInformationClusterKeepActiveParams + alloc] init]; + } + + auto responseHandler = ^(id _Nullable response, NSError * _Nullable error) { + completion(error); + }; + + auto * timedInvokeTimeoutMs = params.timedInvokeTimeoutMs; + + using RequestType = BridgedDeviceBasicInformation::Commands::KeepActive::Type; + [self.device _invokeKnownCommandWithEndpointID:self.endpointID + clusterID:@(RequestType::GetClusterId()) + commandID:@(RequestType::GetCommandId()) + commandPayload:params + expectedValues:expectedValues + expectedValueInterval:expectedValueIntervalMs + timedInvokeTimeout:timedInvokeTimeoutMs + serverSideProcessingTimeout:params.serverSideProcessingTimeout + responseClass:nil + queue:self.callbackQueue + completion:responseHandler]; +} + - (NSDictionary * _Nullable)readAttributeVendorNameWithParams:(MTRReadParams * _Nullable)params { return [self.device readAttributeWithEndpointID:self.endpointID clusterID:@(MTRClusterIDTypeBridgedDeviceBasicInformationID) attributeID:@(MTRAttributeIDTypeClusterBridgedDeviceBasicInformationAttributeVendorNameID) params:params]; diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h index 5981d8292c039c..3746227a4f9d21 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h @@ -2915,6 +2915,34 @@ MTR_PROVISIONALLY_AVAILABLE @property (nonatomic, copy, nullable) NSNumber * serverSideProcessingTimeout; @end +MTR_PROVISIONALLY_AVAILABLE +@interface MTRBridgedDeviceBasicInformationClusterKeepActiveParams : NSObject +/** + * Controls whether the command is a timed command (using Timed Invoke). + * + * If nil (the default value), a regular invoke is done for commands that do + * not require a timed invoke and a timed invoke with some default timed request + * timeout is done for commands that require a timed invoke. + * + * If not nil, a timed invoke is done, with the provided value used as the timed + * request timeout. The value should be chosen small enough to provide the + * desired security properties but large enough that it will allow a round-trip + * from the sever to the client (for the status response and actual invoke + * request) within the timeout window. + * + */ +@property (nonatomic, copy, nullable) NSNumber * timedInvokeTimeoutMs; + +/** + * Controls how much time, in seconds, we will allow for the server to process the command. + * + * The command will then time out if that much time, plus an allowance for retransmits due to network failures, passes. + * + * If nil, the framework will try to select an appropriate timeout value itself. + */ +@property (nonatomic, copy, nullable) NSNumber * serverSideProcessingTimeout; +@end + MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) @interface MTRAdministratorCommissioningClusterOpenCommissioningWindowParams : NSObject diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm index 295f8444db0b63..8d18518670e808 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm @@ -7556,6 +7556,79 @@ - (CHIP_ERROR)_encodeToTLVReader:(chip::System::PacketBufferTLVReader &)reader } @end +@implementation MTRBridgedDeviceBasicInformationClusterKeepActiveParams +- (instancetype)init +{ + if (self = [super init]) { + _timedInvokeTimeoutMs = nil; + _serverSideProcessingTimeout = nil; + } + return self; +} + +- (id)copyWithZone:(NSZone * _Nullable)zone; +{ + auto other = [[MTRBridgedDeviceBasicInformationClusterKeepActiveParams alloc] init]; + + other.timedInvokeTimeoutMs = self.timedInvokeTimeoutMs; + other.serverSideProcessingTimeout = self.serverSideProcessingTimeout; + + return other; +} + +- (NSString *)description +{ + NSString * descriptionString = [NSString stringWithFormat:@"<%@: >", NSStringFromClass([self class])]; + return descriptionString; +} + +@end + +@implementation MTRBridgedDeviceBasicInformationClusterKeepActiveParams (InternalMethods) + +- (CHIP_ERROR)_encodeToTLVReader:(chip::System::PacketBufferTLVReader &)reader +{ + chip::app::Clusters::BridgedDeviceBasicInformation::Commands::KeepActive::Type encodableStruct; + ListFreer listFreer; + + auto buffer = chip::System::PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSizeWithoutReserve, 0); + if (buffer.IsNull()) { + return CHIP_ERROR_NO_MEMORY; + } + + chip::System::PacketBufferTLVWriter writer; + // Commands never need chained buffers, since they cannot be chunked. + writer.Init(std::move(buffer), /* useChainedBuffers = */ false); + + ReturnErrorOnFailure(chip::app::DataModel::Encode(writer, chip::TLV::AnonymousTag(), encodableStruct)); + + ReturnErrorOnFailure(writer.Finalize(&buffer)); + + reader.Init(std::move(buffer)); + return reader.Next(chip::TLV::kTLVType_Structure, chip::TLV::AnonymousTag()); +} + +- (NSDictionary * _Nullable)_encodeAsDataValue:(NSError * __autoreleasing *)error +{ + chip::System::PacketBufferTLVReader reader; + CHIP_ERROR err = [self _encodeToTLVReader:reader]; + if (err != CHIP_NO_ERROR) { + if (error) { + *error = [MTRError errorForCHIPErrorCode:err]; + } + return nil; + } + + auto decodedObj = MTRDecodeDataValueDictionaryFromCHIPTLV(&reader); + if (decodedObj == nil) { + if (error) { + *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INCORRECT_STATE]; + } + } + return decodedObj; +} +@end + @implementation MTRAdministratorCommissioningClusterOpenCommissioningWindowParams - (instancetype)init { diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloads_Internal.h b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloads_Internal.h index d3746f9d936895..67c37229a00dcc 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloads_Internal.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloads_Internal.h @@ -496,6 +496,12 @@ NS_ASSUME_NONNULL_BEGIN @end +@interface MTRBridgedDeviceBasicInformationClusterKeepActiveParams (InternalMethods) + +- (NSDictionary * _Nullable)_encodeAsDataValue:(NSError * __autoreleasing *)error; + +@end + @interface MTRAdministratorCommissioningClusterOpenCommissioningWindowParams (InternalMethods) - (NSDictionary * _Nullable)_encodeAsDataValue:(NSError * __autoreleasing *)error; diff --git a/src/darwin/Framework/CHIP/zap-generated/MTREventTLVValueDecoder.mm b/src/darwin/Framework/CHIP/zap-generated/MTREventTLVValueDecoder.mm index e2fc7e70cba67b..13f125b6328642 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTREventTLVValueDecoder.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTREventTLVValueDecoder.mm @@ -1382,6 +1382,23 @@ static id _Nullable DecodeEventPayloadForBridgedDeviceBasicInformationCluster(Ev return value; } + case Events::ActiveChanged::Id: { + Events::ActiveChanged::DecodableType cppValue; + *aError = DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) { + return nil; + } + + __auto_type * value = [MTRBridgedDeviceBasicInformationClusterActiveChangedEvent new]; + + do { + NSNumber * _Nonnull memberValue; + memberValue = [NSNumber numberWithUnsignedInt:cppValue.promisedActiveDuration]; + value.promisedActiveDuration = memberValue; + } while (0); + + return value; + } default: { break; } diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h index b804aee8b5ace1..d4da2052d3cb3e 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h @@ -649,6 +649,11 @@ MTR_DEPRECATED("Please use MTRBridgedDeviceBasicInformationClusterReachableChang @property (nonatomic, copy) NSNumber * _Nonnull reachableNewValue MTR_DEPRECATED("Please use MTRBridgedDeviceBasicInformationClusterReachableChangedEvent", ios(16.1, 17.0), macos(13.0, 14.0), watchos(9.1, 10.0), tvos(16.1, 17.0)); @end +MTR_PROVISIONALLY_AVAILABLE +@interface MTRBridgedDeviceBasicInformationClusterActiveChangedEvent : NSObject +@property (nonatomic, copy) NSNumber * _Nonnull promisedActiveDuration MTR_PROVISIONALLY_AVAILABLE; +@end + MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) @interface MTRSwitchClusterSwitchLatchedEvent : NSObject @property (nonatomic, copy, getter=getNewPosition) NSNumber * _Nonnull newPosition MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)); diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm index d3182118beb89c..99503c79d6e2bf 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm @@ -2246,6 +2246,33 @@ @implementation MTRBridgedDeviceBasicClusterReachableChangedEvent : MTRBridgedDe @dynamic reachableNewValue; @end +@implementation MTRBridgedDeviceBasicInformationClusterActiveChangedEvent +- (instancetype)init +{ + if (self = [super init]) { + + _promisedActiveDuration = @(0); + } + return self; +} + +- (id)copyWithZone:(NSZone * _Nullable)zone +{ + auto other = [[MTRBridgedDeviceBasicInformationClusterActiveChangedEvent alloc] init]; + + other.promisedActiveDuration = self.promisedActiveDuration; + + return other; +} + +- (NSString *)description +{ + NSString * descriptionString = [NSString stringWithFormat:@"<%@: promisedActiveDuration:%@; >", NSStringFromClass([self class]), _promisedActiveDuration]; + return descriptionString; +} + +@end + @implementation MTRSwitchClusterSwitchLatchedEvent - (instancetype)init { diff --git a/zzz_generated/app-common/app-common/zap-generated/callback.h b/zzz_generated/app-common/app-common/zap-generated/callback.h index 80e42e929fe08a..bb2d5a3f3dd501 100644 --- a/zzz_generated/app-common/app-common/zap-generated/callback.h +++ b/zzz_generated/app-common/app-common/zap-generated/callback.h @@ -5826,6 +5826,12 @@ bool emberAfTimeSynchronizationClusterSetDSTOffsetCallback( bool emberAfTimeSynchronizationClusterSetDefaultNTPCallback( chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, const chip::app::Clusters::TimeSynchronization::Commands::SetDefaultNTP::DecodableType & commandData); +/** + * @brief Bridged Device Basic Information Cluster KeepActive Command callback (from client) + */ +bool emberAfBridgedDeviceBasicInformationClusterKeepActiveCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::BridgedDeviceBasicInformation::Commands::KeepActive::DecodableType & commandData); /** * @brief Administrator Commissioning Cluster OpenCommissioningWindow Command callback (from client) */ diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h index 62bf3473649525..fd0f8d24d26dc6 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h @@ -1480,6 +1480,12 @@ enum class ProductFinishEnum : uint8_t // enum value. This specific should never be transmitted. kUnknownEnumValue = 6, }; + +// Bitmap for Feature +enum class Feature : uint32_t +{ + kBridgedICDSupport = 0x100000, +}; } // namespace BridgedDeviceBasicInformation namespace Switch { diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp index 8fc3c32562d3d7..7a08cb455c6452 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp @@ -7678,7 +7678,28 @@ CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) } // namespace ProductAppearanceStruct } // namespace Structs -namespace Commands {} // namespace Commands +namespace Commands { +namespace KeepActive { +CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const +{ + DataModel::WrappedStructEncoder encoder{ aWriter, aTag }; + return encoder.Finalize(); +} + +CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) +{ + detail::StructDecodeIterator __iterator(reader); + while (true) + { + auto __element = __iterator.Next(); + if (std::holds_alternative(__element)) + { + return std::get(__element); + } + } +} +} // namespace KeepActive. +} // namespace Commands namespace Attributes { CHIP_ERROR TypeInfo::DecodableType::Decode(TLV::TLVReader & reader, const ConcreteAttributePath & path) @@ -7848,6 +7869,41 @@ CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) } } } // namespace ReachableChanged. +namespace ActiveChanged { +CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const +{ + TLV::TLVType outer; + ReturnErrorOnFailure(aWriter.StartContainer(aTag, TLV::kTLVType_Structure, outer)); + ReturnErrorOnFailure(DataModel::Encode(aWriter, TLV::ContextTag(Fields::kPromisedActiveDuration), promisedActiveDuration)); + return aWriter.EndContainer(outer); +} + +CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) +{ + detail::StructDecodeIterator __iterator(reader); + while (true) + { + auto __element = __iterator.Next(); + if (std::holds_alternative(__element)) + { + return std::get(__element); + } + + CHIP_ERROR err = CHIP_NO_ERROR; + const uint8_t __context_tag = std::get(__element); + + if (__context_tag == to_underlying(Fields::kPromisedActiveDuration)) + { + err = DataModel::Decode(reader, promisedActiveDuration); + } + else + { + } + + ReturnErrorOnFailure(err); + } +} +} // namespace ActiveChanged. } // namespace Events } // namespace BridgedDeviceBasicInformation @@ -31605,6 +31661,13 @@ bool CommandIsFabricScoped(ClusterId aCluster, CommandId aCommand) return false; } } + case Clusters::BridgedDeviceBasicInformation::Id: { + switch (aCommand) + { + default: + return false; + } + } case Clusters::AdministratorCommissioning::Id: { switch (aCommand) { diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h index 9c335d4ba91dcb..5952309732b90c 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h @@ -10566,6 +10566,47 @@ using DecodableType = Type; } // namespace ProductAppearanceStruct } // namespace Structs +namespace Commands { +// Forward-declarations so we can reference these later. + +namespace KeepActive { +struct Type; +struct DecodableType; +} // namespace KeepActive + +} // namespace Commands + +namespace Commands { +namespace KeepActive { +enum class Fields : uint8_t +{ +}; + +struct Type +{ +public: + // Use GetCommandId instead of commandId directly to avoid naming conflict with CommandIdentification in ExecutionOfACommand + static constexpr CommandId GetCommandId() { return Commands::KeepActive::Id; } + static constexpr ClusterId GetClusterId() { return Clusters::BridgedDeviceBasicInformation::Id; } + + CHIP_ERROR Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; + + using ResponseType = DataModel::NullObjectType; + + static constexpr bool MustUseTimedInvoke() { return false; } +}; + +struct DecodableType +{ +public: + static constexpr CommandId GetCommandId() { return Commands::KeepActive::Id; } + static constexpr ClusterId GetClusterId() { return Clusters::BridgedDeviceBasicInformation::Id; } + + CHIP_ERROR Decode(TLV::TLVReader & reader); +}; +}; // namespace KeepActive +} // namespace Commands + namespace Attributes { namespace VendorName { @@ -10965,6 +11006,39 @@ struct DecodableType CHIP_ERROR Decode(TLV::TLVReader & reader); }; } // namespace ReachableChanged +namespace ActiveChanged { +static constexpr PriorityLevel kPriorityLevel = PriorityLevel::Info; + +enum class Fields : uint8_t +{ + kPromisedActiveDuration = 0, +}; + +struct Type +{ +public: + static constexpr PriorityLevel GetPriorityLevel() { return kPriorityLevel; } + static constexpr EventId GetEventId() { return Events::ActiveChanged::Id; } + static constexpr ClusterId GetClusterId() { return Clusters::BridgedDeviceBasicInformation::Id; } + static constexpr bool kIsFabricScoped = false; + + uint32_t promisedActiveDuration = static_cast(0); + + CHIP_ERROR Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; +}; + +struct DecodableType +{ +public: + static constexpr PriorityLevel GetPriorityLevel() { return kPriorityLevel; } + static constexpr EventId GetEventId() { return Events::ActiveChanged::Id; } + static constexpr ClusterId GetClusterId() { return Clusters::BridgedDeviceBasicInformation::Id; } + + uint32_t promisedActiveDuration = static_cast(0); + + CHIP_ERROR Decode(TLV::TLVReader & reader); +}; +} // namespace ActiveChanged } // namespace Events } // namespace BridgedDeviceBasicInformation namespace Switch { diff --git a/zzz_generated/app-common/app-common/zap-generated/ids/Commands.h b/zzz_generated/app-common/app-common/zap-generated/ids/Commands.h index 4a8e4a830a44f7..926df4190a620e 100644 --- a/zzz_generated/app-common/app-common/zap-generated/ids/Commands.h +++ b/zzz_generated/app-common/app-common/zap-generated/ids/Commands.h @@ -449,6 +449,16 @@ static constexpr CommandId Id = 0x00000005; } // namespace Commands } // namespace TimeSynchronization +namespace BridgedDeviceBasicInformation { +namespace Commands { + +namespace KeepActive { +static constexpr CommandId Id = 0x00000080; +} // namespace KeepActive + +} // namespace Commands +} // namespace BridgedDeviceBasicInformation + namespace AdministratorCommissioning { namespace Commands { diff --git a/zzz_generated/app-common/app-common/zap-generated/ids/Events.h b/zzz_generated/app-common/app-common/zap-generated/ids/Events.h index 6e6191fe45c21c..3a997b42d51bb5 100644 --- a/zzz_generated/app-common/app-common/zap-generated/ids/Events.h +++ b/zzz_generated/app-common/app-common/zap-generated/ids/Events.h @@ -220,6 +220,10 @@ namespace ReachableChanged { static constexpr EventId Id = 0x00000003; } // namespace ReachableChanged +namespace ActiveChanged { +static constexpr EventId Id = 0x00000080; +} // namespace ActiveChanged + } // namespace Events } // namespace BridgedDeviceBasicInformation diff --git a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h index dd2f2c40082af8..99221c08e3f71c 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h @@ -3392,6 +3392,7 @@ class TimeSynchronizationSetDefaultNTP : public ClusterCommand | Cluster BridgedDeviceBasicInformation | 0x0039 | |------------------------------------------------------------------------------| | Commands: | | +| * KeepActive | 0x80 | |------------------------------------------------------------------------------| | Attributes: | | | * VendorName | 0x0001 | @@ -3422,8 +3423,46 @@ class TimeSynchronizationSetDefaultNTP : public ClusterCommand | * ShutDown | 0x0001 | | * Leave | 0x0002 | | * ReachableChanged | 0x0003 | +| * ActiveChanged | 0x0080 | \*----------------------------------------------------------------------------*/ +/* + * Command KeepActive + */ +class BridgedDeviceBasicInformationKeepActive : public ClusterCommand +{ +public: + BridgedDeviceBasicInformationKeepActive(CredentialIssuerCommands * credsIssuerConfig) : + ClusterCommand("keep-active", credsIssuerConfig) + { + ClusterCommand::AddArguments(); + } + + CHIP_ERROR SendCommand(chip::DeviceProxy * device, std::vector endpointIds) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::BridgedDeviceBasicInformation::Id; + constexpr chip::CommandId commandId = chip::app::Clusters::BridgedDeviceBasicInformation::Commands::KeepActive::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") command (0x%08" PRIX32 ") on endpoint %u", clusterId, + commandId, endpointIds.at(0)); + return ClusterCommand::SendCommand(device, endpointIds.at(0), clusterId, commandId, mRequest); + } + + CHIP_ERROR SendGroupCommand(chip::GroupId groupId, chip::FabricIndex fabricIndex) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::BridgedDeviceBasicInformation::Id; + constexpr chip::CommandId commandId = chip::app::Clusters::BridgedDeviceBasicInformation::Commands::KeepActive::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") command (0x%08" PRIX32 ") on Group %u", clusterId, commandId, + groupId); + + return ClusterCommand::SendGroupCommand(groupId, fabricIndex, clusterId, commandId, mRequest); + } + +private: + chip::app::Clusters::BridgedDeviceBasicInformation::Commands::KeepActive::Type mRequest; +}; + /*----------------------------------------------------------------------------*\ | Cluster Switch | 0x003B | |------------------------------------------------------------------------------| @@ -18116,7 +18155,8 @@ void registerClusterBridgedDeviceBasicInformation(Commands & commands, Credentia // // Commands // - make_unique(Id, credsIssuerConfig), // + make_unique(Id, credsIssuerConfig), // + make_unique(credsIssuerConfig), // // // Attributes // @@ -18221,11 +18261,13 @@ void registerClusterBridgedDeviceBasicInformation(Commands & commands, Credentia make_unique(Id, "shut-down", Events::ShutDown::Id, credsIssuerConfig), // make_unique(Id, "leave", Events::Leave::Id, credsIssuerConfig), // make_unique(Id, "reachable-changed", Events::ReachableChanged::Id, credsIssuerConfig), // + make_unique(Id, "active-changed", Events::ActiveChanged::Id, credsIssuerConfig), // make_unique(Id, credsIssuerConfig), // make_unique(Id, "start-up", Events::StartUp::Id, credsIssuerConfig), // make_unique(Id, "shut-down", Events::ShutDown::Id, credsIssuerConfig), // make_unique(Id, "leave", Events::Leave::Id, credsIssuerConfig), // make_unique(Id, "reachable-changed", Events::ReachableChanged::Id, credsIssuerConfig), // + make_unique(Id, "active-changed", Events::ActiveChanged::Id, credsIssuerConfig), // }; commands.RegisterCluster(clusterName, clusterCommands); diff --git a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp index d062e7aeac77c5..5071a49a141e51 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp +++ b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp @@ -5883,6 +5883,22 @@ CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, return CHIP_NO_ERROR; } +CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, + const BridgedDeviceBasicInformation::Events::ActiveChanged::DecodableType & value) +{ + DataModelLogger::LogString(label, indent, "{"); + { + CHIP_ERROR err = DataModelLogger::LogValue("PromisedActiveDuration", indent + 1, value.promisedActiveDuration); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Event truncated due to invalid value for 'PromisedActiveDuration'"); + return err; + } + } + DataModelLogger::LogString(indent, "}"); + + return CHIP_NO_ERROR; +} CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, const Switch::Events::SwitchLatched::DecodableType & value) { DataModelLogger::LogString(label, indent, "{"); @@ -19824,6 +19840,11 @@ CHIP_ERROR DataModelLogger::LogEvent(const chip::app::EventHeader & header, chip ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); return DataModelLogger::LogValue("ReachableChanged", 1, value); } + case BridgedDeviceBasicInformation::Events::ActiveChanged::Id: { + chip::app::Clusters::BridgedDeviceBasicInformation::Events::ActiveChanged::DecodableType value; + ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); + return DataModelLogger::LogValue("ActiveChanged", 1, value); + } } break; } diff --git a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h index 8a74b57e503b30..95cc96d293b574 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h @@ -469,6 +469,8 @@ static CHIP_ERROR LogValue(const char * label, size_t indent, static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::BridgedDeviceBasicInformation::Events::ReachableChanged::DecodableType & value); +static CHIP_ERROR LogValue(const char * label, size_t indent, + const chip::app::Clusters::BridgedDeviceBasicInformation::Events::ActiveChanged::DecodableType & value); static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::Switch::Events::SwitchLatched::DecodableType & value); static CHIP_ERROR LogValue(const char * label, size_t indent, diff --git a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h index 39f9c949872dfe..17451309d1e621 100644 --- a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h @@ -39469,6 +39469,7 @@ class SubscribeAttributeTimeSynchronizationClusterRevision : public SubscribeAtt | Cluster BridgedDeviceBasicInformation | 0x0039 | |------------------------------------------------------------------------------| | Commands: | | +| * KeepActive | 0x80 | |------------------------------------------------------------------------------| | Attributes: | | | * VendorName | 0x0001 | @@ -39499,8 +39500,56 @@ class SubscribeAttributeTimeSynchronizationClusterRevision : public SubscribeAtt | * ShutDown | 0x0001 | | * Leave | 0x0002 | | * ReachableChanged | 0x0003 | +| * ActiveChanged | 0x0080 | \*----------------------------------------------------------------------------*/ +#if MTR_ENABLE_PROVISIONAL +/* + * Command KeepActive + */ +class BridgedDeviceBasicInformationKeepActive : public ClusterCommand { +public: + BridgedDeviceBasicInformationKeepActive() + : ClusterCommand("keep-active") + { + ClusterCommand::AddArguments(); + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::BridgedDeviceBasicInformation::Id; + constexpr chip::CommandId commandId = chip::app::Clusters::BridgedDeviceBasicInformation::Commands::KeepActive::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") command (0x%08" PRIX32 ") on endpoint %u", clusterId, commandId, endpointId); + + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterBridgedDeviceBasicInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + __auto_type * params = [[MTRBridgedDeviceBasicInformationClusterKeepActiveParams alloc] init]; + params.timedInvokeTimeoutMs = mTimedInteractionTimeoutMs.HasValue() ? [NSNumber numberWithUnsignedShort:mTimedInteractionTimeoutMs.Value()] : nil; + uint16_t repeatCount = mRepeatCount.ValueOr(1); + uint16_t __block responsesNeeded = repeatCount; + while (repeatCount--) { + [cluster keepActiveWithParams:params completion: + ^(NSError * _Nullable error) { + responsesNeeded--; + if (error != nil) { + mError = error; + LogNSError("Error", error); + RemoteDataModelLogger::LogCommandErrorAsJSON(@(endpointId), @(clusterId), @(commandId), error); + } + if (responsesNeeded == 0) { + SetCommandExitStatus(mError); + } + }]; + } + return CHIP_NO_ERROR; + } + +private: +}; + +#endif // MTR_ENABLE_PROVISIONAL + /* * Attribute VendorName */ @@ -190895,6 +190944,9 @@ void registerClusterBridgedDeviceBasicInformation(Commands & commands) commands_list clusterCommands = { make_unique(Id), // +#if MTR_ENABLE_PROVISIONAL + make_unique(), // +#endif // MTR_ENABLE_PROVISIONAL make_unique(Id), // make_unique(Id), // make_unique(Id), // From c097d826b9b3ca8a52026c2126f2e87bfe7eb924 Mon Sep 17 00:00:00 2001 From: Andy Salisbury Date: Thu, 25 Jul 2024 19:35:58 -0400 Subject: [PATCH 17/49] Add the missing implementation for BdxTransferSession::RejectTransfer. (#34484) * Add the missing implementation for BdxTransferSession::RejectTransfer. * Style fix. --------- Co-authored-by: Andrei Litvin --- src/protocols/bdx/BdxTransferSession.cpp | 11 ++++ .../bdx/tests/TestBdxTransferSession.cpp | 61 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/src/protocols/bdx/BdxTransferSession.cpp b/src/protocols/bdx/BdxTransferSession.cpp index f6c54985ab5cd1..99588f0770a72e 100644 --- a/src/protocols/bdx/BdxTransferSession.cpp +++ b/src/protocols/bdx/BdxTransferSession.cpp @@ -261,6 +261,17 @@ CHIP_ERROR TransferSession::AcceptTransfer(const TransferAcceptData & acceptData return CHIP_NO_ERROR; } +CHIP_ERROR TransferSession::RejectTransfer(StatusCode reason) +{ + VerifyOrReturnError(mState == TransferState::kNegotiateTransferParams, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mPendingOutput == OutputEventType::kNone, CHIP_ERROR_INCORRECT_STATE); + + PrepareStatusReport(reason); + mState = TransferState::kTransferDone; + + return CHIP_NO_ERROR; +} + CHIP_ERROR TransferSession::PrepareBlockQuery() { const MessageType msgType = MessageType::BlockQuery; diff --git a/src/protocols/bdx/tests/TestBdxTransferSession.cpp b/src/protocols/bdx/tests/TestBdxTransferSession.cpp index 8fcc4fab312f26..9a383752551064 100644 --- a/src/protocols/bdx/tests/TestBdxTransferSession.cpp +++ b/src/protocols/bdx/tests/TestBdxTransferSession.cpp @@ -124,6 +124,13 @@ void VerifyNoMoreOutput(TransferSession & transferSession) EXPECT_EQ(event.EventType, TransferSession::OutputEventType::kNone); } +void VerifyInternalError(TransferSession & transferSession) +{ + TransferSession::OutputEvent event; + transferSession.PollOutput(event, kNoAdvanceTime); + EXPECT_EQ(event.EventType, TransferSession::OutputEventType::kInternalError); +} + // Helper method for initializing two TransferSession objects, generating a TransferInit message, and passing it to a responding // TransferSession. void SendAndVerifyTransferInit(TransferSession::OutputEvent & outEvent, System::Clock::Timeout timeout, TransferSession & initiator, @@ -235,6 +242,30 @@ void SendAndVerifyAcceptMsg(TransferSession::OutputEvent & outEvent, TransferSes EXPECT_LE(acceptReceiver.GetTransferBlockSize(), initData.MaxBlockSize); } +void SendAndVerifyRejectMsg(TransferSession::OutputEvent & outEvent, TransferSession & rejectSender, StatusCode reason, + TransferSession & rejectReceiver) +{ + CHIP_ERROR err = rejectSender.RejectTransfer(reason); + EXPECT_EQ(err, CHIP_NO_ERROR); + + // Verify Sender emits status message for sending + rejectSender.PollOutput(outEvent, kNoAdvanceTime); + VerifyNoMoreOutput(rejectSender); + EXPECT_EQ(outEvent.EventType, TransferSession::OutputEventType::kMsgToSend); + System::PacketBufferHandle statusReportMsg = outEvent.MsgData.Retain(); + VerifyStatusReport(std::move(outEvent.MsgData), reason); + + // Pass status message to rejectReceiver + err = AttachHeaderAndSend(outEvent.msgTypeData, std::move(outEvent.MsgData), rejectReceiver); + EXPECT_EQ(err, CHIP_NO_ERROR); + + // Verify received status message. + rejectReceiver.PollOutput(outEvent, kNoAdvanceTime); + VerifyInternalError(rejectReceiver); + EXPECT_EQ(outEvent.EventType, TransferSession::OutputEventType::kStatusReceived); + EXPECT_EQ(outEvent.statusData.statusCode, reason); +} + // Helper method for preparing a sending a BlockQuery message between two TransferSession objects. void SendAndVerifyQuery(TransferSession & queryReceiver, TransferSession & querySender, TransferSession::OutputEvent & outEvent) { @@ -698,3 +729,33 @@ TEST_F(TestBdxTransferSession, TestDuplicateBlockError) EXPECT_EQ(outEvent.statusData.statusCode, StatusCode::kBadBlockCounter); } } + +TEST_F(TestBdxTransferSession, TestRejectTransfer) +{ + TransferSession::OutputEvent outEvent; + TransferSession initiatingReceiver; + TransferSession respondingSender; + + // Chosen arbitrarily for this test + uint16_t proposedBlockSize = 128; + System::Clock::Timeout timeout = System::Clock::Seconds16(24); + TransferControlFlags driveMode = TransferControlFlags::kReceiverDrive; + + // ReceiveInit parameters + TransferSession::TransferInitData initOptions; + initOptions.TransferCtlFlags = driveMode; + initOptions.MaxBlockSize = proposedBlockSize; + char testFileDes[9] = { "test.txt" }; + initOptions.FileDesLength = static_cast(strlen(testFileDes)); + initOptions.FileDesignator = reinterpret_cast(testFileDes); + + // Initialize respondingSender and pass ReceiveInit message + BitFlags senderOpts; + senderOpts.Set(driveMode); + + SendAndVerifyTransferInit(outEvent, timeout, initiatingReceiver, TransferRole::kReceiver, initOptions, respondingSender, + senderOpts, proposedBlockSize); + + // Reject the transfer with a status + SendAndVerifyRejectMsg(outEvent, respondingSender, StatusCode::kResponderBusy, initiatingReceiver); +} From 68ef6f11eb1c7f2c0a0b0b5d00795eb95c4f1dcf Mon Sep 17 00:00:00 2001 From: Alex Tsitsiura Date: Fri, 26 Jul 2024 03:22:43 +0300 Subject: [PATCH 18/49] [Telink] Readme files update & Add USB target to CI (#34502) * [Telink] Add USB target to CI * [Telink] update readme files * [Telink] fix the unit test --- .github/workflows/examples-telink.yaml | 8 ++-- .../air-quality-sensor-app/telink/README.md | 36 ++++++++++----- examples/all-clusters-app/telink/README.md | 36 ++++++++++----- .../all-clusters-minimal-app/telink/README.md | 44 +++++++++++++------ examples/bridge-app/telink/README.md | 36 ++++++++++----- examples/contact-sensor-app/telink/README.md | 36 ++++++++++----- examples/light-switch-app/telink/README.md | 36 ++++++++++----- examples/lighting-app/telink/README.md | 40 ++++++++++++----- examples/lock-app/telink/README.md | 36 ++++++++++----- examples/ota-requestor-app/telink/README.md | 44 +++++++++++++------ examples/pump-app/telink/README.md | 36 ++++++++++----- examples/pump-controller-app/telink/README.md | 36 ++++++++++----- examples/shell/telink/README.md | 24 ++++++++-- examples/smoke-co-alarm-app/telink/README.md | 36 ++++++++++----- .../telink/README.md | 36 ++++++++++----- examples/thermostat/telink/README.md | 36 ++++++++++----- examples/window-app/telink/README.md | 40 ++++++++++++----- scripts/build/build/targets.py | 1 + scripts/build/builders/telink.py | 7 ++- .../build/testdata/all_targets_linux_x64.txt | 2 +- 20 files changed, 436 insertions(+), 170 deletions(-) diff --git a/.github/workflows/examples-telink.yaml b/.github/workflows/examples-telink.yaml index 045972682ad04a..b26453fa1bbb9d 100644 --- a/.github/workflows/examples-telink.yaml +++ b/.github/workflows/examples-telink.yaml @@ -189,13 +189,13 @@ jobs: - name: clean out build output run: rm -rf ./out - - name: Build example Telink (B91) Pump App + - name: Build example Telink (B91 USB) Pump App run: | ./scripts/run_in_build_env.sh \ - "./scripts/build/build_examples.py --target 'telink-tlsr9518adk80d-pump' build" + "./scripts/build/build_examples.py --target 'telink-tlsr9518adk80d-pump-usb' build" .environment/pigweed-venv/bin/python3 scripts/tools/memory/gh_sizes.py \ - telink tlsr9518adk80d pump-app \ - out/telink-tlsr9518adk80d-pump/zephyr/zephyr.elf \ + telink tlsr9518adk80d pump-app-usb \ + out/telink-tlsr9518adk80d-pump-usb/zephyr/zephyr.elf \ /tmp/bloat_reports/ - name: clean out build output diff --git a/examples/air-quality-sensor-app/telink/README.md b/examples/air-quality-sensor-app/telink/README.md index 64b394c046b51a..e2c4b49a699991 100644 --- a/examples/air-quality-sensor-app/telink/README.md +++ b/examples/air-quality-sensor-app/telink/README.md @@ -4,6 +4,17 @@ You can use this example as a reference for creating your own application. ![Telink B91 EVK](http://wiki.telink-semi.cn/wiki/assets/Hardware/B91_Generic_Starter_Kit_Hardware_Guide/connection_chart.png) +## Supported devices + +The example supports building and running on the following devices: + +| Board/SoC | Build target | Zephyr Board Info | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| [B91](https://wiki.telink-semi.cn/wiki/Hardware/B91_Generic_Starter_Kit_Hardware_Guide) [TLSR9518ADK80D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR951x-Series) | `tlsr9518adk80d`, `tlsr9518adk80d-mars`, `tlsr9518adk80d-usb` | [TLSR9518ADK80D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9518adk80d/doc/index.rst) | +| [B92](https://wiki.telink-semi.cn/wiki/Hardware/B92_Generic_Starter_Kit_Hardware_Guide) [TLSR9528A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR952x-Series) | `tlsr9528a`, `tlsr9528a_retention` | [TLSR9528A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9528a/doc/index.rst) | +| [B95](https://wiki.telink-semi.cn/wiki/Hardware/B95_Generic_Starter_Kit_Hardware_Guide) [TLSR9258A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR925x-Series) | `tlsr9258a` | [TLSR9258A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9258a/doc/index.rst) | +| [W91](https://wiki.telink-semi.cn/wiki/Hardware/W91_Generic_Starter_Kit_Hardware_Guide) [TLSR9118BDK40D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR911x-Series) | `tlsr9118bdk40d` | [TLSR9118BDK40D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9118bdk40d/doc/index.rst) | + ## Build and flash 1. Run the Docker container: @@ -12,7 +23,7 @@ You can use this example as a reference for creating your own application. $ docker run -it --rm -v $PWD:/host -w /host ghcr.io/project-chip/chip-build-telink:$(wget -q -O - https://raw.githubusercontent.com/project-chip/connectedhomeip/master/.github/workflows/examples-telink.yaml 2> /dev/null | grep chip-build-telink | awk -F: '{print $NF}') ``` - Compatible docker image version can be found in next file: + You can find the compatible Docker image version in the file: ```bash $ .github/workflows/examples-telink.yaml @@ -24,8 +35,8 @@ You can use this example as a reference for creating your own application. $ source ./scripts/activate.sh -p all,telink ``` -3. In the example dir run (replace __ with your board name, for - example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): +3. Build the example (replace __ with your board name, see + [Supported devices](#supported-devices)): ```bash $ west build -b @@ -35,9 +46,12 @@ You can use this example as a reference for creating your own application. MB, for example, `-DFLASH_SIZE=1m` or `-DFLASH_SIZE=4m`: ```bash - $ west build -b tlsr9518adk80d -- -DFLASH_SIZE=4m + $ west build -b -- -DFLASH_SIZE=4m ``` + You can find the target built file called **_zephyr.bin_** under the + **_build/zephyr_** directory. + 4. Flash binary: ``` @@ -56,16 +70,18 @@ To get output from device, connect UART to following pins: | TX | PB2 (pin 16 of J34 connector) | | GND | GND | +Baud rate: 115200 bits/s + ### Buttons The following buttons are available on **tlsr9518adk80d** board: -| Name | Function | Description | -| :------- | :--------------------- | :----------------------------------------------------------------------------------------------------- | -| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and back to uncommissioned state | -| Button 2 | `AirQuality` control | Manually triggers the `AirQuality` state | -| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | -| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | +| Name | Function | Description | +| :------- | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and return to a decommissioned state (to activate, push the button 3 times) | +| Button 2 | `AirQuality` control | Manually triggers the `AirQuality` state | +| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | +| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | ### LEDs diff --git a/examples/all-clusters-app/telink/README.md b/examples/all-clusters-app/telink/README.md index 89d8221af75d3f..8404672d3134e6 100644 --- a/examples/all-clusters-app/telink/README.md +++ b/examples/all-clusters-app/telink/README.md @@ -6,6 +6,17 @@ creating your own application. ![Telink B91 EVK](http://wiki.telink-semi.cn/wiki/assets/Hardware/B91_Generic_Starter_Kit_Hardware_Guide/connection_chart.png) +## Supported devices + +The example supports building and running on the following devices: + +| Board/SoC | Build target | Zephyr Board Info | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| [B91](https://wiki.telink-semi.cn/wiki/Hardware/B91_Generic_Starter_Kit_Hardware_Guide) [TLSR9518ADK80D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR951x-Series) | `tlsr9518adk80d`, `tlsr9518adk80d-mars`, `tlsr9518adk80d-usb` | [TLSR9518ADK80D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9518adk80d/doc/index.rst) | +| [B92](https://wiki.telink-semi.cn/wiki/Hardware/B92_Generic_Starter_Kit_Hardware_Guide) [TLSR9528A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR952x-Series) | `tlsr9528a`, `tlsr9528a_retention` | [TLSR9528A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9528a/doc/index.rst) | +| [B95](https://wiki.telink-semi.cn/wiki/Hardware/B95_Generic_Starter_Kit_Hardware_Guide) [TLSR9258A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR925x-Series) | `tlsr9258a` | [TLSR9258A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9258a/doc/index.rst) | +| [W91](https://wiki.telink-semi.cn/wiki/Hardware/W91_Generic_Starter_Kit_Hardware_Guide) [TLSR9118BDK40D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR911x-Series) | `tlsr9118bdk40d` | [TLSR9118BDK40D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9118bdk40d/doc/index.rst) | + ## Build and flash 1. Run the Docker container: @@ -14,7 +25,7 @@ creating your own application. $ docker run -it --rm -v $PWD:/host -w /host ghcr.io/project-chip/chip-build-telink:$(wget -q -O - https://raw.githubusercontent.com/project-chip/connectedhomeip/master/.github/workflows/examples-telink.yaml 2> /dev/null | grep chip-build-telink | awk -F: '{print $NF}') ``` - Compatible docker image version can be found in next file: + You can find the compatible Docker image version in the file: ```bash $ .github/workflows/examples-telink.yaml @@ -26,8 +37,8 @@ creating your own application. $ source ./scripts/activate.sh -p all,telink ``` -3. In the example dir run (replace __ with your board name, for - example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): +3. Build the example (replace __ with your board name, see + [Supported devices](#supported-devices)): ```bash $ west build -b @@ -37,9 +48,12 @@ creating your own application. MB, for example, `-DFLASH_SIZE=1m` or `-DFLASH_SIZE=4m`: ```bash - $ west build -b tlsr9518adk80d -- -DFLASH_SIZE=4m + $ west build -b -- -DFLASH_SIZE=4m ``` + You can find the target built file called **_zephyr.bin_** under the + **_build/zephyr_** directory. + 4. Flash binary: ``` @@ -58,16 +72,18 @@ To get output from device, connect UART to following pins: | TX | PB2 (pin 16 of J34 connector) | | GND | GND | +Baud rate: 115200 bits/s + ### Buttons The following buttons are available on **tlsr9518adk80d** board: -| Name | Function | Description | -| :------- | :--------------------- | :----------------------------------------------------------------------------------------------------- | -| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and back to uncommissioned state | -| Button 2 | Not used | Not used | -| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | -| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | +| Name | Function | Description | +| :------- | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and return to a decommissioned state (to activate, push the button 3 times) | +| Button 2 | Not used | Not used | +| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | +| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | ### LEDs diff --git a/examples/all-clusters-minimal-app/telink/README.md b/examples/all-clusters-minimal-app/telink/README.md index 396afb1cc63091..f3a0646df97743 100644 --- a/examples/all-clusters-minimal-app/telink/README.md +++ b/examples/all-clusters-minimal-app/telink/README.md @@ -6,6 +6,17 @@ for creating your own application. ![Telink B91 EVK](http://wiki.telink-semi.cn/wiki/assets/Hardware/B91_Generic_Starter_Kit_Hardware_Guide/connection_chart.png) +## Supported devices + +The example supports building and running on the following devices: + +| Board/SoC | Build target | Zephyr Board Info | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| [B91](https://wiki.telink-semi.cn/wiki/Hardware/B91_Generic_Starter_Kit_Hardware_Guide) [TLSR9518ADK80D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR951x-Series) | `tlsr9518adk80d`, `tlsr9518adk80d-mars`, `tlsr9518adk80d-usb` | [TLSR9518ADK80D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9518adk80d/doc/index.rst) | +| [B92](https://wiki.telink-semi.cn/wiki/Hardware/B92_Generic_Starter_Kit_Hardware_Guide) [TLSR9528A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR952x-Series) | `tlsr9528a`, `tlsr9528a_retention` | [TLSR9528A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9528a/doc/index.rst) | +| [B95](https://wiki.telink-semi.cn/wiki/Hardware/B95_Generic_Starter_Kit_Hardware_Guide) [TLSR9258A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR925x-Series) | `tlsr9258a` | [TLSR9258A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9258a/doc/index.rst) | +| [W91](https://wiki.telink-semi.cn/wiki/Hardware/W91_Generic_Starter_Kit_Hardware_Guide) [TLSR9118BDK40D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR911x-Series) | `tlsr9118bdk40d` | [TLSR9118BDK40D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9118bdk40d/doc/index.rst) | + ## Build and flash 1. Run the Docker container: @@ -14,7 +25,7 @@ for creating your own application. $ docker run -it --rm -v $PWD:/host -w /host ghcr.io/project-chip/chip-build-telink:$(wget -q -O - https://raw.githubusercontent.com/project-chip/connectedhomeip/master/.github/workflows/examples-telink.yaml 2> /dev/null | grep chip-build-telink | awk -F: '{print $NF}') ``` - Compatible docker image version can be found in next file: + You can find the compatible Docker image version in the file: ```bash $ .github/workflows/examples-telink.yaml @@ -26,8 +37,8 @@ for creating your own application. $ source ./scripts/activate.sh -p all,telink ``` -3. In the example dir run (replace __ with your board name, for - example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): +3. Build the example (replace __ with your board name, see + [Supported devices](#supported-devices)): ```bash $ west build -b @@ -37,9 +48,12 @@ for creating your own application. MB, for example, `-DFLASH_SIZE=1m` or `-DFLASH_SIZE=4m`: ```bash - $ west build -b tlsr9518adk80d -- -DFLASH_SIZE=4m + $ west build -b -- -DFLASH_SIZE=4m ``` + You can find the target built file called **_zephyr.bin_** under the + **_build/zephyr_** directory. + 4. Flash binary: ``` @@ -58,27 +72,31 @@ To get output from device, connect UART to following pins: | TX | PB2 (pin 16 of J34 connector) | | GND | GND | +Baud rate: 115200 bits/s + ### Buttons The following buttons are available on **tlsr9518adk80d** board: -| Name | Function | Description | -| :------- | :--------------------- | :----------------------------------------------------------------------------------------------------- | -| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and back to uncommissioned state | -| Button 2 | Not used | Not used | -| Button 2 | Not used | Not used | -| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | +| Name | Function | Description | +| :------- | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and return to a decommissioned state (to activate, push the button 3 times) | +| Button 2 | Not used | Not used | +| Button 2 | Not used | Not used | +| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | ### LEDs -**Red** LED indicates current state of Thread network. It ables to be in +#### Indicate current state of Thread network + +**Red** LED indicates current state of Thread network. It is able to be in following states: | State | Description | | :-------------------------- | :--------------------------------------------------------------------------- | | Blinks with short pulses | Device is not commissioned to Thread, Thread is disabled | -| Blinls with frequent pulses | Device is commissioned, Thread enabled. Device trying to JOIN thread network | -| Blinks with whde pulses | Device commissioned and joined to thread network as CHILD | +| Blinks with frequent pulses | Device is commissioned, Thread enabled. Device trying to JOIN thread network | +| Blinks with wide pulses | Device commissioned and joined to thread network as CHILD | ### CHIP tool commands diff --git a/examples/bridge-app/telink/README.md b/examples/bridge-app/telink/README.md index 0855689cd1201f..7dc29dff9d1d58 100644 --- a/examples/bridge-app/telink/README.md +++ b/examples/bridge-app/telink/README.md @@ -83,6 +83,17 @@ defined: - This macro is used to declare an endpoint and its associated cluster list, which must be previously defined by the `DECLARE_DYNAMIC_CLUSTER...` macros. +## Supported devices + +The example supports building and running on the following devices: + +| Board/SoC | Build target | Zephyr Board Info | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| [B91](https://wiki.telink-semi.cn/wiki/Hardware/B91_Generic_Starter_Kit_Hardware_Guide) [TLSR9518ADK80D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR951x-Series) | `tlsr9518adk80d`, `tlsr9518adk80d-mars`, `tlsr9518adk80d-usb` | [TLSR9518ADK80D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9518adk80d/doc/index.rst) | +| [B92](https://wiki.telink-semi.cn/wiki/Hardware/B92_Generic_Starter_Kit_Hardware_Guide) [TLSR9528A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR952x-Series) | `tlsr9528a`, `tlsr9528a_retention` | [TLSR9528A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9528a/doc/index.rst) | +| [B95](https://wiki.telink-semi.cn/wiki/Hardware/B95_Generic_Starter_Kit_Hardware_Guide) [TLSR9258A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR925x-Series) | `tlsr9258a` | [TLSR9258A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9258a/doc/index.rst) | +| [W91](https://wiki.telink-semi.cn/wiki/Hardware/W91_Generic_Starter_Kit_Hardware_Guide) [TLSR9118BDK40D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR911x-Series) | `tlsr9118bdk40d` | [TLSR9118BDK40D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9118bdk40d/doc/index.rst) | + ## Build and flash 1. Run the Docker container: @@ -91,7 +102,7 @@ defined: $ docker run -it --rm -v $PWD:/host -w /host ghcr.io/project-chip/chip-build-telink:$(wget -q -O - https://raw.githubusercontent.com/project-chip/connectedhomeip/master/.github/workflows/examples-telink.yaml 2> /dev/null | grep chip-build-telink | awk -F: '{print $NF}') ``` - Compatible docker image version can be found in next file: + You can find the compatible Docker image version in the file: ```bash $ .github/workflows/examples-telink.yaml @@ -103,8 +114,8 @@ defined: $ source ./scripts/activate.sh -p all,telink ``` -3. In the example dir run (replace __ with your board name, for - example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): +3. Build the example (replace __ with your board name, see + [Supported devices](#supported-devices)): ```bash $ west build -b @@ -114,9 +125,12 @@ defined: MB, for example, `-DFLASH_SIZE=1m` or `-DFLASH_SIZE=4m`: ```bash - $ west build -b tlsr9518adk80d -- -DFLASH_SIZE=4m + $ west build -b -- -DFLASH_SIZE=4m ``` + You can find the target built file called **_zephyr.bin_** under the + **_build/zephyr_** directory. + 4. Flash binary: ``` @@ -135,16 +149,18 @@ To get output from device, connect UART to following pins: | TX | PB2 (pin 16 of J34 connector) | | GND | GND | +Baud rate: 115200 bits/s + ### Buttons The following buttons are available on **tlsr9518adk80d** board: -| Name | Function | Description | -| :------- | :--------------------- | :----------------------------------------------------------------------------------------------------- | -| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and back to uncommissioned state | -| Button 2 | Lighting control | Manually triggers the lighting state | -| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | -| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | +| Name | Function | Description | +| :------- | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and return to a decommissioned state (to activate, push the button 3 times) | +| Button 2 | Lighting control | Manually triggers the lighting state | +| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | +| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | ### LEDs diff --git a/examples/contact-sensor-app/telink/README.md b/examples/contact-sensor-app/telink/README.md index 964b05a76ba109..cbceff71361ccf 100755 --- a/examples/contact-sensor-app/telink/README.md +++ b/examples/contact-sensor-app/telink/README.md @@ -4,6 +4,17 @@ You can use this example as a reference for creating your own application. ![Telink B91 EVK](http://wiki.telink-semi.cn/wiki/assets/Hardware/B91_Generic_Starter_Kit_Hardware_Guide/connection_chart.png) +## Supported devices + +The example supports building and running on the following devices: + +| Board/SoC | Build target | Zephyr Board Info | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| [B91](https://wiki.telink-semi.cn/wiki/Hardware/B91_Generic_Starter_Kit_Hardware_Guide) [TLSR9518ADK80D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR951x-Series) | `tlsr9518adk80d`, `tlsr9518adk80d-mars`, `tlsr9518adk80d-usb` | [TLSR9518ADK80D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9518adk80d/doc/index.rst) | +| [B92](https://wiki.telink-semi.cn/wiki/Hardware/B92_Generic_Starter_Kit_Hardware_Guide) [TLSR9528A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR952x-Series) | `tlsr9528a`, `tlsr9528a_retention` | [TLSR9528A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9528a/doc/index.rst) | +| [B95](https://wiki.telink-semi.cn/wiki/Hardware/B95_Generic_Starter_Kit_Hardware_Guide) [TLSR9258A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR925x-Series) | `tlsr9258a` | [TLSR9258A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9258a/doc/index.rst) | +| [W91](https://wiki.telink-semi.cn/wiki/Hardware/W91_Generic_Starter_Kit_Hardware_Guide) [TLSR9118BDK40D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR911x-Series) | `tlsr9118bdk40d` | [TLSR9118BDK40D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9118bdk40d/doc/index.rst) | + ## Build and flash 1. Run the Docker container: @@ -12,7 +23,7 @@ You can use this example as a reference for creating your own application. $ docker run -it --rm -v $PWD:/host -w /host ghcr.io/project-chip/chip-build-telink:$(wget -q -O - https://raw.githubusercontent.com/project-chip/connectedhomeip/master/.github/workflows/examples-telink.yaml 2> /dev/null | grep chip-build-telink | awk -F: '{print $NF}') ``` - Compatible docker image version can be found in next file: + You can find the compatible Docker image version in the file: ```bash $ .github/workflows/examples-telink.yaml @@ -24,8 +35,8 @@ You can use this example as a reference for creating your own application. $ source ./scripts/activate.sh -p all,telink ``` -3. In the example dir run (replace __ with your board name, for - example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): +3. Build the example (replace __ with your board name, see + [Supported devices](#supported-devices)): ```bash $ west build -b @@ -35,9 +46,12 @@ You can use this example as a reference for creating your own application. MB, for example, `-DFLASH_SIZE=1m` or `-DFLASH_SIZE=4m`: ```bash - $ west build -b tlsr9518adk80d -- -DFLASH_SIZE=4m + $ west build -b -- -DFLASH_SIZE=4m ``` + You can find the target built file called **_zephyr.bin_** under the + **_build/zephyr_** directory. + 4. Flash binary: ``` @@ -56,16 +70,18 @@ To get output from device, connect UART to following pins: | TX | PB2 (pin 16 of J34 connector) | | GND | GND | +Baud rate: 115200 bits/s + ### Buttons The following buttons are available on **tlsr9518adk80d** board: -| Name | Function | Description | -| :------- | :--------------------- | :----------------------------------------------------------------------------------------------------- | -| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and back to uncommissioned state | -| Button 2 | Toggle Contact State | Manually triggers the Contact Sensor State | -| Button 3 | NA | NA | -| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | +| Name | Function | Description | +| :------- | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and return to a decommissioned state (to activate, push the button 3 times) | +| Button 2 | Toggle Contact State | Manually triggers the Contact Sensor State | +| Button 3 | NA | NA | +| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | ### LEDs diff --git a/examples/light-switch-app/telink/README.md b/examples/light-switch-app/telink/README.md index d222116f5f1a7f..c60dd6a27c3584 100755 --- a/examples/light-switch-app/telink/README.md +++ b/examples/light-switch-app/telink/README.md @@ -9,6 +9,17 @@ creating your own application. ![Telink B91 EVK](http://wiki.telink-semi.cn/wiki/assets/Hardware/B91_Generic_Starter_Kit_Hardware_Guide/connection_chart.png) +## Supported devices + +The example supports building and running on the following devices: + +| Board/SoC | Build target | Zephyr Board Info | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| [B91](https://wiki.telink-semi.cn/wiki/Hardware/B91_Generic_Starter_Kit_Hardware_Guide) [TLSR9518ADK80D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR951x-Series) | `tlsr9518adk80d`, `tlsr9518adk80d-mars`, `tlsr9518adk80d-usb` | [TLSR9518ADK80D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9518adk80d/doc/index.rst) | +| [B92](https://wiki.telink-semi.cn/wiki/Hardware/B92_Generic_Starter_Kit_Hardware_Guide) [TLSR9528A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR952x-Series) | `tlsr9528a`, `tlsr9528a_retention` | [TLSR9528A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9528a/doc/index.rst) | +| [B95](https://wiki.telink-semi.cn/wiki/Hardware/B95_Generic_Starter_Kit_Hardware_Guide) [TLSR9258A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR925x-Series) | `tlsr9258a` | [TLSR9258A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9258a/doc/index.rst) | +| [W91](https://wiki.telink-semi.cn/wiki/Hardware/W91_Generic_Starter_Kit_Hardware_Guide) [TLSR9118BDK40D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR911x-Series) | `tlsr9118bdk40d` | [TLSR9118BDK40D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9118bdk40d/doc/index.rst) | + ## Build and flash 1. Run the Docker container: @@ -17,7 +28,7 @@ creating your own application. $ docker run -it --rm -v $PWD:/host -w /host ghcr.io/project-chip/chip-build-telink:$(wget -q -O - https://raw.githubusercontent.com/project-chip/connectedhomeip/master/.github/workflows/examples-telink.yaml 2> /dev/null | grep chip-build-telink | awk -F: '{print $NF}') ``` - Compatible docker image version can be found in next file: + You can find the compatible Docker image version in the file: ```bash $ .github/workflows/examples-telink.yaml @@ -29,8 +40,8 @@ creating your own application. $ source ./scripts/activate.sh -p all,telink ``` -3. In the example dir run (replace __ with your board name, for - example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): +3. Build the example (replace __ with your board name, see + [Supported devices](#supported-devices)): ```bash $ west build -b @@ -40,9 +51,12 @@ creating your own application. MB, for example, `-DFLASH_SIZE=1m` or `-DFLASH_SIZE=4m`: ```bash - $ west build -b tlsr9518adk80d -- -DFLASH_SIZE=4m + $ west build -b -- -DFLASH_SIZE=4m ``` + You can find the target built file called **_zephyr.bin_** under the + **_build/zephyr_** directory. + 4. Flash binary: ``` @@ -61,16 +75,18 @@ To get output from device, connect UART to following pins: | TX | PB2 (pin 16 of J34 connector) | | GND | GND | +Baud rate: 115200 bits/s + ### Buttons The following buttons are available on **tlsr9518adk80d** board: -| Name | Function | Description | -| :------- | :--------------------- | :----------------------------------------------------------------------------------------------------- | -| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and back to uncommissioned state | -| Button 2 | Light Switch control | Manually triggers the light switch state | -| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | -| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | +| Name | Function | Description | +| :------- | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and return to a decommissioned state (to activate, push the button 3 times) | +| Button 2 | Light Switch control | Manually triggers the light switch state | +| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | +| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | ### LEDs diff --git a/examples/lighting-app/telink/README.md b/examples/lighting-app/telink/README.md index 6c2269007e8ac3..0bf52ea12c28fd 100644 --- a/examples/lighting-app/telink/README.md +++ b/examples/lighting-app/telink/README.md @@ -7,6 +7,17 @@ a reference for creating your own application. ![Telink B91 EVK](http://wiki.telink-semi.cn/wiki/assets/Hardware/B91_Generic_Starter_Kit_Hardware_Guide/connection_chart.png) +## Supported devices + +The example supports building and running on the following devices: + +| Board/SoC | Build target | Zephyr Board Info | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| [B91](https://wiki.telink-semi.cn/wiki/Hardware/B91_Generic_Starter_Kit_Hardware_Guide) [TLSR9518ADK80D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR951x-Series) | `tlsr9518adk80d`, `tlsr9518adk80d-mars`, `tlsr9518adk80d-usb` | [TLSR9518ADK80D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9518adk80d/doc/index.rst) | +| [B92](https://wiki.telink-semi.cn/wiki/Hardware/B92_Generic_Starter_Kit_Hardware_Guide) [TLSR9528A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR952x-Series) | `tlsr9528a`, `tlsr9528a_retention` | [TLSR9528A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9528a/doc/index.rst) | +| [B95](https://wiki.telink-semi.cn/wiki/Hardware/B95_Generic_Starter_Kit_Hardware_Guide) [TLSR9258A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR925x-Series) | `tlsr9258a` | [TLSR9258A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9258a/doc/index.rst) | +| [W91](https://wiki.telink-semi.cn/wiki/Hardware/W91_Generic_Starter_Kit_Hardware_Guide) [TLSR9118BDK40D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR911x-Series) | `tlsr9118bdk40d` | [TLSR9118BDK40D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9118bdk40d/doc/index.rst) | + ## Build and flash 1. Run the Docker container: @@ -15,7 +26,7 @@ a reference for creating your own application. $ docker run -it --rm -v $PWD:/host -w /host ghcr.io/project-chip/chip-build-telink:$(wget -q -O - https://raw.githubusercontent.com/project-chip/connectedhomeip/master/.github/workflows/examples-telink.yaml 2> /dev/null | grep chip-build-telink | awk -F: '{print $NF}') ``` - Compatible docker image version can be found in next file: + You can find the compatible Docker image version in the file: ```bash $ .github/workflows/examples-telink.yaml @@ -27,8 +38,8 @@ a reference for creating your own application. $ source ./scripts/activate.sh -p all,telink ``` -3. In the example dir run (replace __ with your board name, for - example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): +3. Build the example (replace __ with your board name, see + [Supported devices](#supported-devices)): ```bash $ west build -b @@ -38,9 +49,12 @@ a reference for creating your own application. MB, for example, `-DFLASH_SIZE=1m` or `-DFLASH_SIZE=4m`: ```bash - $ west build -b tlsr9518adk80d -- -DFLASH_SIZE=4m + $ west build -b -- -DFLASH_SIZE=4m ``` + You can find the target built file called **_zephyr.bin_** under the + **_build/zephyr_** directory. + 4. Flash binary: ``` @@ -59,16 +73,18 @@ To get output from device, connect UART to following pins: | TX | PB2 (pin 16 of J34 connector) | | GND | GND | +Baud rate: 115200 bits/s + ### Buttons The following buttons are available on **tlsr9518adk80d** board: -| Name | Function | Description | -| :------- | :--------------------- | :----------------------------------------------------------------------------------------------------- | -| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and back to uncommissioned state | -| Button 2 | Lighting control | Manually triggers the lighting state | -| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | -| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | +| Name | Function | Description | +| :------- | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and return to a decommissioned state (to activate, push the button 3 times) | +| Button 2 | Lighting control | Manually triggers the lighting state | +| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | +| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | ### LEDs @@ -249,9 +265,9 @@ application of OTA image. The RPCs in `lighting-common/lighting_service/lighting_service.proto` can be used to control various functionalities of the lighting app from a USB-connected host computer. To build the example with the RPC server, run the following -command with _build-target_ replaced with the build target name of the Telink +command with __ replaced with the build target name of the Telink Semiconductor's kit you own: ``` - $ west build -b tlsr9518adk80d -- -DOVERLAY_CONFIG=rpc.overlay + $ west build -b -- -DOVERLAY_CONFIG=rpc.overlay ``` diff --git a/examples/lock-app/telink/README.md b/examples/lock-app/telink/README.md index 8386a524c48d41..8e0d605fd85dc6 100755 --- a/examples/lock-app/telink/README.md +++ b/examples/lock-app/telink/README.md @@ -7,6 +7,17 @@ a reference for creating your own application. ![Telink B91 EVK](http://wiki.telink-semi.cn/wiki/assets/Hardware/B91_Generic_Starter_Kit_Hardware_Guide/connection_chart.png) +## Supported devices + +The example supports building and running on the following devices: + +| Board/SoC | Build target | Zephyr Board Info | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| [B91](https://wiki.telink-semi.cn/wiki/Hardware/B91_Generic_Starter_Kit_Hardware_Guide) [TLSR9518ADK80D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR951x-Series) | `tlsr9518adk80d`, `tlsr9518adk80d-mars`, `tlsr9518adk80d-usb` | [TLSR9518ADK80D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9518adk80d/doc/index.rst) | +| [B92](https://wiki.telink-semi.cn/wiki/Hardware/B92_Generic_Starter_Kit_Hardware_Guide) [TLSR9528A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR952x-Series) | `tlsr9528a`, `tlsr9528a_retention` | [TLSR9528A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9528a/doc/index.rst) | +| [B95](https://wiki.telink-semi.cn/wiki/Hardware/B95_Generic_Starter_Kit_Hardware_Guide) [TLSR9258A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR925x-Series) | `tlsr9258a` | [TLSR9258A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9258a/doc/index.rst) | +| [W91](https://wiki.telink-semi.cn/wiki/Hardware/W91_Generic_Starter_Kit_Hardware_Guide) [TLSR9118BDK40D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR911x-Series) | `tlsr9118bdk40d` | [TLSR9118BDK40D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9118bdk40d/doc/index.rst) | + ## Build and flash 1. Run the Docker container: @@ -15,7 +26,7 @@ a reference for creating your own application. $ docker run -it --rm -v $PWD:/host -w /host ghcr.io/project-chip/chip-build-telink:$(wget -q -O - https://raw.githubusercontent.com/project-chip/connectedhomeip/master/.github/workflows/examples-telink.yaml 2> /dev/null | grep chip-build-telink | awk -F: '{print $NF}') ``` - Compatible docker image version can be found in next file: + You can find the compatible Docker image version in the file: ```bash $ .github/workflows/examples-telink.yaml @@ -27,8 +38,8 @@ a reference for creating your own application. $ source ./scripts/activate.sh -p all,telink ``` -3. In the example dir run (replace __ with your board name, for - example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): +3. Build the example (replace __ with your board name, see + [Supported devices](#supported-devices)): ```bash $ west build -b @@ -38,9 +49,12 @@ a reference for creating your own application. MB, for example, `-DFLASH_SIZE=1m` or `-DFLASH_SIZE=4m`: ```bash - $ west build -b tlsr9518adk80d -- -DFLASH_SIZE=4m + $ west build -b -- -DFLASH_SIZE=4m ``` + You can find the target built file called **_zephyr.bin_** under the + **_build/zephyr_** directory. + 4. Flash binary: ``` @@ -59,16 +73,18 @@ To get output from device, connect UART to following pins: | TX | PB2 (pin 16 of J34 connector) | | GND | GND | +Baud rate: 115200 bits/s + ### Buttons The following buttons are available on **tlsr9518adk80d** board: -| Name | Function | Description | -| :------- | :--------------------- | :----------------------------------------------------------------------------------------------------- | -| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and back to uncommissioned state | -| Button 2 | Lock control | Manually triggers the bolt lock state | -| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | -| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | +| Name | Function | Description | +| :------- | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and return to a decommissioned state (to activate, push the button 3 times) | +| Button 2 | Lock control | Manually triggers the bolt lock state | +| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | +| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | ### LEDs diff --git a/examples/ota-requestor-app/telink/README.md b/examples/ota-requestor-app/telink/README.md index c549804e42f627..d8590c6a0cc4d7 100755 --- a/examples/ota-requestor-app/telink/README.md +++ b/examples/ota-requestor-app/telink/README.md @@ -1,5 +1,16 @@ ![Telink B91 EVK](http://wiki.telink-semi.cn/wiki/assets/Hardware/B91_Generic_Starter_Kit_Hardware_Guide/connection_chart.png) +## Supported devices + +The example supports building and running on the following devices: + +| Board/SoC | Build target | Zephyr Board Info | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| [B91](https://wiki.telink-semi.cn/wiki/Hardware/B91_Generic_Starter_Kit_Hardware_Guide) [TLSR9518ADK80D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR951x-Series) | `tlsr9518adk80d`, `tlsr9518adk80d-mars`, `tlsr9518adk80d-usb` | [TLSR9518ADK80D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9518adk80d/doc/index.rst) | +| [B92](https://wiki.telink-semi.cn/wiki/Hardware/B92_Generic_Starter_Kit_Hardware_Guide) [TLSR9528A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR952x-Series) | `tlsr9528a`, `tlsr9528a_retention` | [TLSR9528A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9528a/doc/index.rst) | +| [B95](https://wiki.telink-semi.cn/wiki/Hardware/B95_Generic_Starter_Kit_Hardware_Guide) [TLSR9258A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR925x-Series) | `tlsr9258a` | [TLSR9258A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9258a/doc/index.rst) | +| [W91](https://wiki.telink-semi.cn/wiki/Hardware/W91_Generic_Starter_Kit_Hardware_Guide) [TLSR9118BDK40D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR911x-Series) | `tlsr9118bdk40d` | [TLSR9118BDK40D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9118bdk40d/doc/index.rst) | + ## Build and flash 1. Run the Docker container: @@ -8,7 +19,7 @@ $ docker run -it --rm -v $PWD:/host -w /host ghcr.io/project-chip/chip-build-telink:$(wget -q -O - https://raw.githubusercontent.com/project-chip/connectedhomeip/master/.github/workflows/examples-telink.yaml 2> /dev/null | grep chip-build-telink | awk -F: '{print $NF}') ``` - Compatible docker image version can be found in next file: + You can find the compatible Docker image version in the file: ```bash $ .github/workflows/examples-telink.yaml @@ -20,8 +31,8 @@ $ source ./scripts/activate.sh -p all,telink ``` -3. In the example dir run (replace __ with your board name, for - example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): +3. Build the example (replace __ with your board name, see + [Supported devices](#supported-devices)): ```bash $ west build -b @@ -31,9 +42,12 @@ MB, for example, `-DFLASH_SIZE=1m` or `-DFLASH_SIZE=4m`: ```bash - $ west build -b tlsr9518adk80d -- -DFLASH_SIZE=4m + $ west build -b -- -DFLASH_SIZE=4m ``` + You can find the target built file called **_zephyr.bin_** under the + **_build/zephyr_** directory. + 4. Flash binary: ``` @@ -52,16 +66,18 @@ To get output from device, connect UART to following pins: | TX | PB2 (pin 16 of J34 connector) | | GND | GND | +Baud rate: 115200 bits/s + ### Buttons The following buttons are available on **tlsr9518adk80d** board: -| Name | Function | Description | -| :------- | :--------------------- | :----------------------------------------------------------------------------------------------------- | -| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and back to uncommissioned state | -| Button 2 | NA | NA | -| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | -| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | +| Name | Function | Description | +| :------- | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and return to a decommissioned state (to activate, push the button 3 times) | +| Button 2 | NA | NA | +| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | +| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | ### LEDs @@ -99,12 +115,14 @@ be used to specify the the effect. It is able to be in following effects: 2. Pair with device ``` - ${CHIP_TOOL_DIR}/chip-tool pairing code ${NODE_ID_TO_ASSIGN} MT:D8XA0CQM00KA0648G00 + ${CHIP_TOOL_DIR}/chip-tool pairing ble-thread ${NODE_ID} hex:${DATASET} ${PIN_CODE} ${DISCRIMINATOR} ``` - here: + Example: - - \${NODE_ID_TO_ASSIGN} is the node id to assign to the ota requestor + ``` + ./chip-tool pairing ble-thread 1234 hex:0e080000000000010000000300000f35060004001fffe0020811111111222222220708fd61f77bd3df233e051000112233445566778899aabbccddeeff030e4f70656e54687265616444656d6f010212340410445f2b5ca6f2a93a55ce570a70efeecb0c0402a0fff8 20202021 3840 + ``` ### OTA with Linux OTA Provider diff --git a/examples/pump-app/telink/README.md b/examples/pump-app/telink/README.md index 885cf7230856bc..2a89b7514b1c98 100755 --- a/examples/pump-app/telink/README.md +++ b/examples/pump-app/telink/README.md @@ -8,6 +8,17 @@ reference for creating your own pump application. ![Telink B91 EVK](http://wiki.telink-semi.cn/wiki/assets/Hardware/B91_Generic_Starter_Kit_Hardware_Guide/connection_chart.png) +## Supported devices + +The example supports building and running on the following devices: + +| Board/SoC | Build target | Zephyr Board Info | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| [B91](https://wiki.telink-semi.cn/wiki/Hardware/B91_Generic_Starter_Kit_Hardware_Guide) [TLSR9518ADK80D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR951x-Series) | `tlsr9518adk80d`, `tlsr9518adk80d-mars`, `tlsr9518adk80d-usb` | [TLSR9518ADK80D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9518adk80d/doc/index.rst) | +| [B92](https://wiki.telink-semi.cn/wiki/Hardware/B92_Generic_Starter_Kit_Hardware_Guide) [TLSR9528A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR952x-Series) | `tlsr9528a`, `tlsr9528a_retention` | [TLSR9528A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9528a/doc/index.rst) | +| [B95](https://wiki.telink-semi.cn/wiki/Hardware/B95_Generic_Starter_Kit_Hardware_Guide) [TLSR9258A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR925x-Series) | `tlsr9258a` | [TLSR9258A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9258a/doc/index.rst) | +| [W91](https://wiki.telink-semi.cn/wiki/Hardware/W91_Generic_Starter_Kit_Hardware_Guide) [TLSR9118BDK40D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR911x-Series) | `tlsr9118bdk40d` | [TLSR9118BDK40D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9118bdk40d/doc/index.rst) | + ## Build and flash 1. Run the Docker container: @@ -16,7 +27,7 @@ reference for creating your own pump application. $ docker run -it --rm -v $PWD:/host -w /host ghcr.io/project-chip/chip-build-telink:$(wget -q -O - https://raw.githubusercontent.com/project-chip/connectedhomeip/master/.github/workflows/examples-telink.yaml 2> /dev/null | grep chip-build-telink | awk -F: '{print $NF}') ``` - Compatible docker image version can be found in next file: + You can find the compatible Docker image version in the file: ```bash $ .github/workflows/examples-telink.yaml @@ -28,8 +39,8 @@ reference for creating your own pump application. $ source ./scripts/activate.sh -p all,telink ``` -3. In the example dir run (replace __ with your board name, for - example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): +3. Build the example (replace __ with your board name, see + [Supported devices](#supported-devices)): ```bash $ west build -b @@ -39,9 +50,12 @@ reference for creating your own pump application. MB, for example, `-DFLASH_SIZE=1m` or `-DFLASH_SIZE=4m`: ```bash - $ west build -b tlsr9518adk80d -- -DFLASH_SIZE=4m + $ west build -b -- -DFLASH_SIZE=4m ``` + You can find the target built file called **_zephyr.bin_** under the + **_build/zephyr_** directory. + 4. Flash binary: ``` @@ -60,16 +74,18 @@ To get output from device, connect UART to following pins: | TX | PB2 (pin 16 of J34 connector) | | GND | GND | +Baud rate: 115200 bits/s + ### Buttons The following buttons are available on **tlsr9518adk80d** board: -| Name | Function | Description | -| :------- | :--------------------- | :----------------------------------------------------------------------------------------------------- | -| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and back to uncommissioned state | -| Button 2 | Lock control | Manually triggers the bolt lock state | -| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | -| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | +| Name | Function | Description | +| :------- | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and return to a decommissioned state (to activate, push the button 3 times) | +| Button 2 | Lock control | Manually triggers the bolt lock state | +| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | +| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | ### LEDs diff --git a/examples/pump-controller-app/telink/README.md b/examples/pump-controller-app/telink/README.md index a49b0999ddebac..529cc37e002bf5 100755 --- a/examples/pump-controller-app/telink/README.md +++ b/examples/pump-controller-app/telink/README.md @@ -9,6 +9,17 @@ your own pump application. ![Telink B91 EVK](http://wiki.telink-semi.cn/wiki/assets/Hardware/B91_Generic_Starter_Kit_Hardware_Guide/connection_chart.png) +## Supported devices + +The example supports building and running on the following devices: + +| Board/SoC | Build target | Zephyr Board Info | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| [B91](https://wiki.telink-semi.cn/wiki/Hardware/B91_Generic_Starter_Kit_Hardware_Guide) [TLSR9518ADK80D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR951x-Series) | `tlsr9518adk80d`, `tlsr9518adk80d-mars`, `tlsr9518adk80d-usb` | [TLSR9518ADK80D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9518adk80d/doc/index.rst) | +| [B92](https://wiki.telink-semi.cn/wiki/Hardware/B92_Generic_Starter_Kit_Hardware_Guide) [TLSR9528A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR952x-Series) | `tlsr9528a`, `tlsr9528a_retention` | [TLSR9528A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9528a/doc/index.rst) | +| [B95](https://wiki.telink-semi.cn/wiki/Hardware/B95_Generic_Starter_Kit_Hardware_Guide) [TLSR9258A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR925x-Series) | `tlsr9258a` | [TLSR9258A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9258a/doc/index.rst) | +| [W91](https://wiki.telink-semi.cn/wiki/Hardware/W91_Generic_Starter_Kit_Hardware_Guide) [TLSR9118BDK40D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR911x-Series) | `tlsr9118bdk40d` | [TLSR9118BDK40D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9118bdk40d/doc/index.rst) | + ## Build and flash 1. Run the Docker container: @@ -17,7 +28,7 @@ your own pump application. $ docker run -it --rm -v $PWD:/host -w /host ghcr.io/project-chip/chip-build-telink:$(wget -q -O - https://raw.githubusercontent.com/project-chip/connectedhomeip/master/.github/workflows/examples-telink.yaml 2> /dev/null | grep chip-build-telink | awk -F: '{print $NF}') ``` - Compatible docker image version can be found in next file: + You can find the compatible Docker image version in the file: ```bash $ .github/workflows/examples-telink.yaml @@ -29,8 +40,8 @@ your own pump application. $ source ./scripts/activate.sh -p all,telink ``` -3. In the example dir run (replace __ with your board name, for - example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): +3. Build the example (replace __ with your board name, see + [Supported devices](#supported-devices)): ```bash $ west build -b @@ -40,9 +51,12 @@ your own pump application. MB, for example, `-DFLASH_SIZE=1m` or `-DFLASH_SIZE=4m`: ```bash - $ west build -b tlsr9518adk80d -- -DFLASH_SIZE=4m + $ west build -b -- -DFLASH_SIZE=4m ``` + You can find the target built file called **_zephyr.bin_** under the + **_build/zephyr_** directory. + 4. Flash binary: ``` @@ -61,16 +75,18 @@ To get output from device, connect UART to following pins: | TX | PB2 (pin 16 of J34 connector) | | GND | GND | +Baud rate: 115200 bits/s + ### Buttons The following buttons are available on **tlsr9518adk80d** board: -| Name | Function | Description | -| :------- | :--------------------- | :----------------------------------------------------------------------------------------------------- | -| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and back to uncommissioned state | -| Button 2 | Lock control | Manually triggers the bolt lock state | -| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | -| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | +| Name | Function | Description | +| :------- | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and return to a decommissioned state (to activate, push the button 3 times) | +| Button 2 | Lock control | Manually triggers the bolt lock state | +| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | +| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | ### LEDs diff --git a/examples/shell/telink/README.md b/examples/shell/telink/README.md index 0d445a3838ec3a..14e8bedb1c3bf0 100755 --- a/examples/shell/telink/README.md +++ b/examples/shell/telink/README.md @@ -4,6 +4,17 @@ You can use this example as a reference for creating your own application. ![Telink B91 EVK](http://wiki.telink-semi.cn/wiki/assets/Hardware/B91_Generic_Starter_Kit_Hardware_Guide/connection_chart.png) +## Supported devices + +The example supports building and running on the following devices: + +| Board/SoC | Build target | Zephyr Board Info | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| [B91](https://wiki.telink-semi.cn/wiki/Hardware/B91_Generic_Starter_Kit_Hardware_Guide) [TLSR9518ADK80D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR951x-Series) | `tlsr9518adk80d`, `tlsr9518adk80d-mars`, `tlsr9518adk80d-usb` | [TLSR9518ADK80D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9518adk80d/doc/index.rst) | +| [B92](https://wiki.telink-semi.cn/wiki/Hardware/B92_Generic_Starter_Kit_Hardware_Guide) [TLSR9528A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR952x-Series) | `tlsr9528a`, `tlsr9528a_retention` | [TLSR9528A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9528a/doc/index.rst) | +| [B95](https://wiki.telink-semi.cn/wiki/Hardware/B95_Generic_Starter_Kit_Hardware_Guide) [TLSR9258A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR925x-Series) | `tlsr9258a` | [TLSR9258A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9258a/doc/index.rst) | +| [W91](https://wiki.telink-semi.cn/wiki/Hardware/W91_Generic_Starter_Kit_Hardware_Guide) [TLSR9118BDK40D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR911x-Series) | `tlsr9118bdk40d` | [TLSR9118BDK40D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9118bdk40d/doc/index.rst) | + ## Build and flash 1. Run the Docker container: @@ -12,7 +23,7 @@ You can use this example as a reference for creating your own application. $ docker run -it --rm -v $PWD:/host -w /host ghcr.io/project-chip/chip-build-telink:$(wget -q -O - https://raw.githubusercontent.com/project-chip/connectedhomeip/master/.github/workflows/examples-telink.yaml 2> /dev/null | grep chip-build-telink | awk -F: '{print $NF}') ``` - Compatible docker image version can be found in next file: + You can find the compatible Docker image version in the file: ```bash $ .github/workflows/examples-telink.yaml @@ -24,8 +35,8 @@ You can use this example as a reference for creating your own application. $ source ./scripts/activate.sh -p all,telink ``` -3. In the example dir run (replace __ with your board name, for - example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): +3. Build the example (replace __ with your board name, see + [Supported devices](#supported-devices)): ```bash $ west build -b @@ -35,9 +46,12 @@ You can use this example as a reference for creating your own application. MB, for example, `-DFLASH_SIZE=1m` or `-DFLASH_SIZE=4m`: ```bash - $ west build -b tlsr9518adk80d -- -DFLASH_SIZE=4m + $ west build -b -- -DFLASH_SIZE=4m ``` + You can find the target built file called **_zephyr.bin_** under the + **_build/zephyr_** directory. + 4. Flash binary: ``` @@ -55,3 +69,5 @@ To get output from device, connect UART to following pins: | RX | PB3 (pin 17 of J34 connector) | | TX | PB2 (pin 16 of J34 connector) | | GND | GND | + +Baud rate: 115200 bits/s diff --git a/examples/smoke-co-alarm-app/telink/README.md b/examples/smoke-co-alarm-app/telink/README.md index 97a00f8d53a8ad..da8a084f4dfa4f 100755 --- a/examples/smoke-co-alarm-app/telink/README.md +++ b/examples/smoke-co-alarm-app/telink/README.md @@ -4,6 +4,17 @@ You can use this example as a reference for creating your own application. ![Telink B91 EVK](http://wiki.telink-semi.cn/wiki/assets/Hardware/B91_Generic_Starter_Kit_Hardware_Guide/connection_chart.png) +## Supported devices + +The example supports building and running on the following devices: + +| Board/SoC | Build target | Zephyr Board Info | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| [B91](https://wiki.telink-semi.cn/wiki/Hardware/B91_Generic_Starter_Kit_Hardware_Guide) [TLSR9518ADK80D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR951x-Series) | `tlsr9518adk80d`, `tlsr9518adk80d-mars`, `tlsr9518adk80d-usb` | [TLSR9518ADK80D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9518adk80d/doc/index.rst) | +| [B92](https://wiki.telink-semi.cn/wiki/Hardware/B92_Generic_Starter_Kit_Hardware_Guide) [TLSR9528A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR952x-Series) | `tlsr9528a`, `tlsr9528a_retention` | [TLSR9528A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9528a/doc/index.rst) | +| [B95](https://wiki.telink-semi.cn/wiki/Hardware/B95_Generic_Starter_Kit_Hardware_Guide) [TLSR9258A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR925x-Series) | `tlsr9258a` | [TLSR9258A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9258a/doc/index.rst) | +| [W91](https://wiki.telink-semi.cn/wiki/Hardware/W91_Generic_Starter_Kit_Hardware_Guide) [TLSR9118BDK40D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR911x-Series) | `tlsr9118bdk40d` | [TLSR9118BDK40D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9118bdk40d/doc/index.rst) | + ## Build and flash 1. Run the Docker container: @@ -12,7 +23,7 @@ You can use this example as a reference for creating your own application. $ docker run -it --rm -v $PWD:/host -w /host ghcr.io/project-chip/chip-build-telink:$(wget -q -O - https://raw.githubusercontent.com/project-chip/connectedhomeip/master/.github/workflows/examples-telink.yaml 2> /dev/null | grep chip-build-telink | awk -F: '{print $NF}') ``` - Compatible docker image version can be found in next file: + You can find the compatible Docker image version in the file: ```bash $ .github/workflows/examples-telink.yaml @@ -24,8 +35,8 @@ You can use this example as a reference for creating your own application. $ source ./scripts/activate.sh -p all,telink ``` -3. In the example dir run (replace __ with your board name, for - example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): +3. Build the example (replace __ with your board name, see + [Supported devices](#supported-devices)): ```bash $ west build -b @@ -35,9 +46,12 @@ You can use this example as a reference for creating your own application. MB, for example, `-DFLASH_SIZE=1m` or `-DFLASH_SIZE=4m`: ```bash - $ west build -b tlsr9518adk80d -- -DFLASH_SIZE=4m + $ west build -b -- -DFLASH_SIZE=4m ``` + You can find the target built file called **_zephyr.bin_** under the + **_build/zephyr_** directory. + 4. Flash binary: ``` @@ -56,16 +70,18 @@ To get output from device, connect UART to following pins: | TX | PB2 (pin 16 of J34 connector) | | GND | GND | +Baud rate: 115200 bits/s + ### Buttons The following buttons are available on **tlsr9518adk80d** board: -| Name | Function | Description | -| :------- | :--------------------- | :----------------------------------------------------------------------------------------------------- | -| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and back to uncommissioned state | -| Button 2 | Self test | Start self testing | -| Button 3 | NA | NA | -| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | +| Name | Function | Description | +| :------- | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and return to a decommissioned state (to activate, push the button 3 times) | +| Button 2 | Self test | Start self testing | +| Button 3 | NA | NA | +| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | ### LEDs diff --git a/examples/temperature-measurement-app/telink/README.md b/examples/temperature-measurement-app/telink/README.md index c78bd36ea8e4a6..04668c02cb0fc8 100644 --- a/examples/temperature-measurement-app/telink/README.md +++ b/examples/temperature-measurement-app/telink/README.md @@ -8,6 +8,17 @@ creating your own application. ![Telink B91 EVK](http://wiki.telink-semi.cn/wiki/assets/Hardware/B91_Generic_Starter_Kit_Hardware_Guide/connection_chart.png) +## Supported devices + +The example supports building and running on the following devices: + +| Board/SoC | Build target | Zephyr Board Info | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| [B91](https://wiki.telink-semi.cn/wiki/Hardware/B91_Generic_Starter_Kit_Hardware_Guide) [TLSR9518ADK80D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR951x-Series) | `tlsr9518adk80d`, `tlsr9518adk80d-mars`, `tlsr9518adk80d-usb` | [TLSR9518ADK80D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9518adk80d/doc/index.rst) | +| [B92](https://wiki.telink-semi.cn/wiki/Hardware/B92_Generic_Starter_Kit_Hardware_Guide) [TLSR9528A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR952x-Series) | `tlsr9528a`, `tlsr9528a_retention` | [TLSR9528A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9528a/doc/index.rst) | +| [B95](https://wiki.telink-semi.cn/wiki/Hardware/B95_Generic_Starter_Kit_Hardware_Guide) [TLSR9258A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR925x-Series) | `tlsr9258a` | [TLSR9258A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9258a/doc/index.rst) | +| [W91](https://wiki.telink-semi.cn/wiki/Hardware/W91_Generic_Starter_Kit_Hardware_Guide) [TLSR9118BDK40D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR911x-Series) | `tlsr9118bdk40d` | [TLSR9118BDK40D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9118bdk40d/doc/index.rst) | + ## Build and flash 1. Run the Docker container: @@ -16,7 +27,7 @@ creating your own application. $ docker run -it --rm -v $PWD:/host -w /host ghcr.io/project-chip/chip-build-telink:$(wget -q -O - https://raw.githubusercontent.com/project-chip/connectedhomeip/master/.github/workflows/examples-telink.yaml 2> /dev/null | grep chip-build-telink | awk -F: '{print $NF}') ``` - Compatible docker image version can be found in next file: + You can find the compatible Docker image version in the file: ```bash $ .github/workflows/examples-telink.yaml @@ -28,8 +39,8 @@ creating your own application. $ source ./scripts/activate.sh -p all,telink ``` -3. In the example dir run (replace __ with your board name, for - example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): +3. Build the example (replace __ with your board name, see + [Supported devices](#supported-devices)): ```bash $ west build -b @@ -39,9 +50,12 @@ creating your own application. MB, for example, `-DFLASH_SIZE=1m` or `-DFLASH_SIZE=4m`: ```bash - $ west build -b tlsr9518adk80d -- -DFLASH_SIZE=4m + $ west build -b -- -DFLASH_SIZE=4m ``` + You can find the target built file called **_zephyr.bin_** under the + **_build/zephyr_** directory. + 4. Flash binary: ``` @@ -60,16 +74,18 @@ To get output from device, connect UART to following pins: | TX | PB2 (pin 16 of J34 connector) | | GND | GND | +Baud rate: 115200 bits/s + ### Buttons The following buttons are available on **tlsr9518adk80d** board: -| Name | Function | Description | -| :------- | :--------------------- | :----------------------------------------------------------------------------------------------------- | -| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and back to uncommissioned state | -| Button 2 | NA | NA | -| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | -| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | +| Name | Function | Description | +| :------- | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and return to a decommissioned state (to activate, push the button 3 times) | +| Button 2 | NA | NA | +| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | +| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | ### LEDs diff --git a/examples/thermostat/telink/README.md b/examples/thermostat/telink/README.md index 604a917ff18ce9..1d6c66bb74cd7f 100755 --- a/examples/thermostat/telink/README.md +++ b/examples/thermostat/telink/README.md @@ -4,6 +4,17 @@ You can use this example as a reference for creating your own application. ![Telink B91 EVK](http://wiki.telink-semi.cn/wiki/assets/Hardware/B91_Generic_Starter_Kit_Hardware_Guide/connection_chart.png) +## Supported devices + +The example supports building and running on the following devices: + +| Board/SoC | Build target | Zephyr Board Info | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| [B91](https://wiki.telink-semi.cn/wiki/Hardware/B91_Generic_Starter_Kit_Hardware_Guide) [TLSR9518ADK80D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR951x-Series) | `tlsr9518adk80d`, `tlsr9518adk80d-mars`, `tlsr9518adk80d-usb` | [TLSR9518ADK80D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9518adk80d/doc/index.rst) | +| [B92](https://wiki.telink-semi.cn/wiki/Hardware/B92_Generic_Starter_Kit_Hardware_Guide) [TLSR9528A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR952x-Series) | `tlsr9528a`, `tlsr9528a_retention` | [TLSR9528A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9528a/doc/index.rst) | +| [B95](https://wiki.telink-semi.cn/wiki/Hardware/B95_Generic_Starter_Kit_Hardware_Guide) [TLSR9258A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR925x-Series) | `tlsr9258a` | [TLSR9258A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9258a/doc/index.rst) | +| [W91](https://wiki.telink-semi.cn/wiki/Hardware/W91_Generic_Starter_Kit_Hardware_Guide) [TLSR9118BDK40D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR911x-Series) | `tlsr9118bdk40d` | [TLSR9118BDK40D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9118bdk40d/doc/index.rst) | + ## Build and flash 1. Run the Docker container: @@ -12,7 +23,7 @@ You can use this example as a reference for creating your own application. $ docker run -it --rm -v $PWD:/host -w /host ghcr.io/project-chip/chip-build-telink:$(wget -q -O - https://raw.githubusercontent.com/project-chip/connectedhomeip/master/.github/workflows/examples-telink.yaml 2> /dev/null | grep chip-build-telink | awk -F: '{print $NF}') ``` - Compatible docker image version can be found in next file: + You can find the compatible Docker image version in the file: ```bash $ .github/workflows/examples-telink.yaml @@ -24,8 +35,8 @@ You can use this example as a reference for creating your own application. $ source ./scripts/activate.sh -p all,telink ``` -3. In the example dir run (replace __ with your board name, for - example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): +3. Build the example (replace __ with your board name, see + [Supported devices](#supported-devices)): ```bash $ west build -b @@ -35,9 +46,12 @@ You can use this example as a reference for creating your own application. MB, for example, `-DFLASH_SIZE=1m` or `-DFLASH_SIZE=4m`: ```bash - $ west build -b tlsr9518adk80d -- -DFLASH_SIZE=4m + $ west build -b -- -DFLASH_SIZE=4m ``` + You can find the target built file called **_zephyr.bin_** under the + **_build/zephyr_** directory. + 4. Flash binary: ``` @@ -56,16 +70,18 @@ To get output from device, connect UART to following pins: | TX | PB2 (pin 16 of J34 connector) | | GND | GND | +Baud rate: 115200 bits/s + ### Buttons The following buttons are available on **tlsr9518adk80d** board: -| Name | Function | Description | -| :------- | :--------------------- | :----------------------------------------------------------------------------------------------------- | -| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and back to uncommissioned state | -| Button 2 | NA | NA | -| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | -| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | +| Name | Function | Description | +| :------- | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and return to a decommissioned state (to activate, push the button 3 times) | +| Button 2 | NA | NA | +| Button 3 | Thread start | Commission thread with static credentials and enables the Thread on device | +| Button 4 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | ### LEDs diff --git a/examples/window-app/telink/README.md b/examples/window-app/telink/README.md index 9d35cbc2d5a360..ee788a7b018f13 100644 --- a/examples/window-app/telink/README.md +++ b/examples/window-app/telink/README.md @@ -7,6 +7,17 @@ for creating your own application. ![Telink B91 EVK](http://wiki.telink-semi.cn/wiki/assets/Hardware/B91_Generic_Starter_Kit_Hardware_Guide/connection_chart.png) +## Supported devices + +The example supports building and running on the following devices: + +| Board/SoC | Build target | Zephyr Board Info | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| [B91](https://wiki.telink-semi.cn/wiki/Hardware/B91_Generic_Starter_Kit_Hardware_Guide) [TLSR9518ADK80D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR951x-Series) | `tlsr9518adk80d`, `tlsr9518adk80d-mars`, `tlsr9518adk80d-usb` | [TLSR9518ADK80D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9518adk80d/doc/index.rst) | +| [B92](https://wiki.telink-semi.cn/wiki/Hardware/B92_Generic_Starter_Kit_Hardware_Guide) [TLSR9528A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR952x-Series) | `tlsr9528a`, `tlsr9528a_retention` | [TLSR9528A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9528a/doc/index.rst) | +| [B95](https://wiki.telink-semi.cn/wiki/Hardware/B95_Generic_Starter_Kit_Hardware_Guide) [TLSR9258A](https://wiki.telink-semi.cn/wiki/chip-series/TLSR925x-Series) | `tlsr9258a` | [TLSR9258A](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9258a/doc/index.rst) | +| [W91](https://wiki.telink-semi.cn/wiki/Hardware/W91_Generic_Starter_Kit_Hardware_Guide) [TLSR9118BDK40D](https://wiki.telink-semi.cn/wiki/chip-series/TLSR911x-Series) | `tlsr9118bdk40d` | [TLSR9118BDK40D](https://github.com/telink-semi/zephyr/blob/develop/boards/riscv/tlsr9118bdk40d/doc/index.rst) | + ## Build and flash 1. Run the Docker container: @@ -15,7 +26,7 @@ for creating your own application. $ docker run -it --rm -v $PWD:/host -w /host ghcr.io/project-chip/chip-build-telink:$(wget -q -O - https://raw.githubusercontent.com/project-chip/connectedhomeip/master/.github/workflows/examples-telink.yaml 2> /dev/null | grep chip-build-telink | awk -F: '{print $NF}') ``` - Compatible docker image version can be found in next file: + You can find the compatible Docker image version in the file: ```bash $ .github/workflows/examples-telink.yaml @@ -27,8 +38,8 @@ for creating your own application. $ source ./scripts/activate.sh -p all,telink ``` -3. In the example dir run (replace __ with your board name, for - example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): +3. Build the example (replace __ with your board name, see + [Supported devices](#supported-devices)): ```bash $ west build -b @@ -38,9 +49,12 @@ for creating your own application. MB, for example, `-DFLASH_SIZE=1m` or `-DFLASH_SIZE=4m`: ```bash - $ west build -b tlsr9518adk80d -- -DFLASH_SIZE=4m + $ west build -b -- -DFLASH_SIZE=4m ``` + You can find the target built file called **_zephyr.bin_** under the + **_build/zephyr_** directory. + 4. Flash binary: ``` @@ -59,16 +73,18 @@ To get output from device, connect UART to following pins: | TX | PB2 (pin 16 of J34 connector) | | GND | GND | +Baud rate: 115200 bits/s + ### Buttons The following buttons are available on **tlsr9518adk80d** board: -| Name | Function | Description | -| :------- | :-------------------------------- | :------------------------------------------------------------------------------------------------------------------- | -| Button 1 | Factory reset | Triple press performs factory reset to forget currently commissioned Thread network and back to uncommissioned state | -| Button 2 | Open and Toggle Move Type control | Manually triggers the Open state by one press and double press triggers the Lift-Tilt move type | -| Button 3 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | -| Button 4 | Close control | Manually triggers the Close state by one press | +| Name | Function | Description | +| :------- | :-------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| Button 1 | Factory reset | Perform factory reset to forget currently commissioned Thread network and return to a decommissioned state (to activate, push the button 3 times) | +| Button 2 | Open and Toggle Move Type control | Manually triggers the Open state by one press and double press triggers the Lift-Tilt move type | +| Button 3 | Open commission window | The button is opening commissioning window to perform commissioning over BLE | +| Button 4 | Close control | Manually triggers the Close state by one press | ### LEDs @@ -254,9 +270,9 @@ application of OTA image. The RPCs in `lighting-common/lighting_service/lighting_service.proto` can be used to control various functionalities of the lighting app from a USB-connected host computer. To build the example with the RPC server, run the following -command with _build-target_ replaced with the build target name of the Nordic +command with __ replaced with the build target name of the Telink Semiconductor's kit you own: ``` - $ west build -b tlsr9518adk80d -- -DOVERLAY_CONFIG=rpc.overlay + $ west build -b -- -DOVERLAY_CONFIG=rpc.overlay ``` diff --git a/scripts/build/build/targets.py b/scripts/build/build/targets.py index 97f8e90af507d5..2c5f84ca30ad37 100755 --- a/scripts/build/build/targets.py +++ b/scripts/build/build/targets.py @@ -784,6 +784,7 @@ def BuildTelinkTarget(): target.AppendModifier('factory-data', enable_factory_data=True) target.AppendModifier('4mb', enable_4mb_flash=True) target.AppendModifier('mars', mars_board_config=True) + target.AppendModifier('usb', usb_board_config=True) return target diff --git a/scripts/build/builders/telink.py b/scripts/build/builders/telink.py index c53e0b6321cd76..b9cd709ba6b44c 100644 --- a/scripts/build/builders/telink.py +++ b/scripts/build/builders/telink.py @@ -151,7 +151,8 @@ def __init__(self, enable_rpcs: bool = False, enable_factory_data: bool = False, enable_4mb_flash: bool = False, - mars_board_config: bool = False): + mars_board_config: bool = False, + usb_board_config: bool = False): super(TelinkBuilder, self).__init__(root, runner) self.app = app self.board = board @@ -162,6 +163,7 @@ def __init__(self, self.enable_factory_data = enable_factory_data self.enable_4mb_flash = enable_4mb_flash self.mars_board_config = mars_board_config + self.usb_board_config = usb_board_config def get_cmd_prefixes(self): if not self._runner.dry_run: @@ -203,6 +205,9 @@ def generate(self): if self.mars_board_config: flags.append("-DTLNK_MARS_BOARD=y") + if self.usb_board_config: + flags.append("-DTLNK_USB_DONGLE=y") + if self.options.pregen_dir: flags.append(f"-DCHIP_CODEGEN_PREGEN_DIR={shlex.quote(self.options.pregen_dir)}") diff --git a/scripts/build/testdata/all_targets_linux_x64.txt b/scripts/build/testdata/all_targets_linux_x64.txt index 2d9fee76e68ac0..d391093a153fcf 100644 --- a/scripts/build/testdata/all_targets_linux_x64.txt +++ b/scripts/build/testdata/all_targets_linux_x64.txt @@ -23,5 +23,5 @@ nuttx-x64-light qpg-qpg6105-{lock,light,shell,persistent-storage,light-switch,thermostat}[-updateimage] stm32-stm32wb5mm-dk-light tizen-arm-{all-clusters,all-clusters-minimal,chip-tool,light,tests}[-no-ble][-no-thread][-no-wifi][-asan][-ubsan][-with-ui] -telink-{tlsr9118bdk40d,tlsr9518adk80d,tlsr9528a,tlsr9528a_retention,tlsr9258a,tlsr9258a_retention}-{air-quality-sensor,all-clusters,all-clusters-minimal,bridge,contact-sensor,light,light-switch,lock,ota-requestor,pump,pump-controller,shell,smoke-co-alarm,temperature-measurement,thermostat,window-covering}[-ota][-dfu][-shell][-rpc][-factory-data][-4mb][-mars] +telink-{tlsr9118bdk40d,tlsr9518adk80d,tlsr9528a,tlsr9528a_retention,tlsr9258a,tlsr9258a_retention}-{air-quality-sensor,all-clusters,all-clusters-minimal,bridge,contact-sensor,light,light-switch,lock,ota-requestor,pump,pump-controller,shell,smoke-co-alarm,temperature-measurement,thermostat,window-covering}[-ota][-dfu][-shell][-rpc][-factory-data][-4mb][-mars][-usb] openiotsdk-{shell,lock}[-mbedtls][-psa] From 04fbb8f490b5db5b4a68951f5f404548440ccbe3 Mon Sep 17 00:00:00 2001 From: PeterC1965 <101805108+PeterC1965@users.noreply.github.com> Date: Fri, 26 Jul 2024 01:48:14 +0100 Subject: [PATCH 19/49] Add device-energy-management cluster example app code for 1.4 (#33910) * Get the EVSE app building and test TC_DEM_2_2 passing * Get all targets building * Address JamesH review comments * Rename utils.cpp to DEMUtils.cpp * Address JamesH review comments * Restyled by whitespace * Restyled by clang-format * Restyled by gn * Restyled by prettier-markdown * Restyled by autopep8 * Restyled by isort * Fix compilation problem by including lib/core/DataModelTypes.h * Fix compilation problem by including * Save examples/all-clusters-app/all-clusters-common/all-clusters-app.zap to update it * Restyled by clang-format * Apply further code review changes * Restyled by clang-format * Restyled by gn * Address code review comments from AndreiL * Restyled by whitespace * Restyled by clang-format * Fix ESP build * Fix ESP build * Restyled by clang-format * Rename src/python_testing/TC_DEM_Utils.py src/python_testing/DEMTestBase.py * Put time util funtions into namespace + drop the Utils prefix * Restyled by whitespace * Restyled by isort * Try to address setForecast comments from Boris * Remove unnecessary SetXXX methods from the device energy management cluster interface * Restyled by clang-format * Apply code review changes suggested by Louis-Philip Beliveau * Document the API for GetForecast and GetPowerAdjustmentCapability * Document the GetForecast and GetPowerAdjustmentCapability APIs * Update src/app/clusters/device-energy-management-server/device-energy-management-server.h Co-authored-by: Boris Zbarsky * Update src/app/clusters/device-energy-management-server/device-energy-management-server.h Co-authored-by: Boris Zbarsky * Address review comments from Boris * Sync up with code review comments from PR34234 * Sync up with code review comments from PR34234 that caused some return codes to change * Restyled by clang-format * modifyForecastRequest: Failure should be returned if a slot number > num slots in a forecast * Update examples/energy-management-app/energy-management-common/include/DeviceEnergyManagementDelegateImpl.h Co-authored-by: Boris Zbarsky * Update examples/energy-management-app/energy-management-common/include/DeviceEnergyManagementManager.h Co-authored-by: Boris Zbarsky * Update examples/energy-management-app/energy-management-common/include/EVSECallbacks.h Co-authored-by: Boris Zbarsky * Update examples/energy-management-app/energy-management-common/include/EVSEManufacturerImpl.h Co-authored-by: Boris Zbarsky * Update examples/energy-management-app/energy-management-common/include/EnergyEvseDelegateImpl.h Co-authored-by: Boris Zbarsky * Update examples/energy-management-app/energy-management-common/src/DEMTestEventTriggers.cpp Co-authored-by: Boris Zbarsky * Update examples/energy-management-app/energy-management-common/include/EnergyEvseManager.h Co-authored-by: Boris Zbarsky * Update examples/energy-management-app/energy-management-common/src/DEMTestEventTriggers.cpp Co-authored-by: Boris Zbarsky * Update examples/energy-management-app/energy-management-common/src/DEMTestEventTriggers.cpp Co-authored-by: Boris Zbarsky * Address review comments from Boris * Update examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp Co-authored-by: Boris Zbarsky * Address further review comments from Boris * Address further review comments from Boris * Address further review comments from Boris * Update examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp Co-authored-by: Boris Zbarsky * Address further review comments from Boris * Update examples/energy-management-app/energy-management-common/src/DEMTestEventTriggers.cpp Co-authored-by: Boris Zbarsky * Update examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp Co-authored-by: Boris Zbarsky * Update examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp Co-authored-by: Boris Zbarsky * Update examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp Co-authored-by: Boris Zbarsky * Apply further review comments from Boris * Used a bitmap rather than uint8_t and sync EnergyTimeUtils files from the EVSE_Add_Get_Set_Clear_Targets_Support branch * Update following review comments from Boris * Allow more time for forecast.startTime in test setup as tests can take variable lengths of time to run * Update examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp Co-authored-by: Boris Zbarsky * Update examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp Co-authored-by: Boris Zbarsky * Update examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp Co-authored-by: Boris Zbarsky * Addressing further review comments from Boris * Fix small issue found as a result of the DEM test script review * Protect against forecast being null * Remove src/python_testing/DEMTestBase.py as renamed in PR34234 * Update following review comment from Andrei * Restyled by clang-format --------- Co-authored-by: Restyled.io Co-authored-by: Boris Zbarsky --- .../all-clusters-app/ameba/chip_main.cmake | 2 + examples/all-clusters-app/asr/BUILD.gn | 2 + .../all-clusters-app/cc13x4_26x4/BUILD.gn | 2 + .../all-clusters-app/infineon/psoc6/BUILD.gn | 2 + examples/all-clusters-app/linux/BUILD.gn | 2 + examples/all-clusters-app/mbed/CMakeLists.txt | 7 +- .../nrfconnect/CMakeLists.txt | 6 +- examples/all-clusters-app/nxp/mw320/BUILD.gn | 2 + .../openiotsdk/CMakeLists.txt | 10 +- .../all-clusters-app/telink/CMakeLists.txt | 10 +- examples/all-clusters-app/tizen/BUILD.gn | 2 + .../energy-management-common/BUILD.gn | 2 +- .../include/DEMManufacturerDelegate.h | 88 ++ .../DeviceEnergyManagementDelegateImpl.h | 287 ++++++- .../include/DeviceEnergyManagementManager.h | 10 +- .../include/EVSECallbacks.h | 2 +- .../include/EVSEManufacturerImpl.h | 48 +- .../include/EnergyEvseDelegateImpl.h | 9 +- .../include/EnergyEvseManager.h | 2 +- .../EnergyManagementAppCmdLineOptions.h | 34 + .../include/EnergyTimeUtils.h | 74 ++ .../include/FakeReadings.h | 115 +++ .../src/DEMTestEventTriggers.cpp | 386 +++++++++ .../DeviceEnergyManagementDelegateImpl.cpp | 751 ++++++++++++++++-- .../src/DeviceEnergyManagementManager.cpp | 16 +- .../src/EVSEManufacturerImpl.cpp | 376 +-------- .../src/EnergyEvseDelegateImpl.cpp | 2 +- .../src/EnergyEvseEventTriggers.cpp | 184 +++++ .../src/EnergyEvseMain.cpp | 18 +- .../src/EnergyEvseManager.cpp | 2 +- .../src/EnergyReportingEventTriggers.cpp | 85 ++ .../src/EnergyTimeUtils.cpp | 153 ++++ .../src/FakeReadings.cpp | 176 ++++ .../esp32/main/CMakeLists.txt | 1 + .../esp32/main/include/CHIPProjectConfig.h | 2 +- .../energy-management-app/esp32/main/main.cpp | 20 + .../esp32/sdkconfig.optimize.defaults | 2 +- examples/energy-management-app/linux/BUILD.gn | 7 +- .../energy-management-app/linux/README.md | 47 ++ examples/energy-management-app/linux/args.gni | 3 +- examples/energy-management-app/linux/main.cpp | 89 ++- examples/platform/linux/AppMain.cpp | 7 + examples/platform/linux/BUILD.gn | 7 + examples/shell/shell_common/BUILD.gn | 3 + src/app/chip_data_model.gni | 6 + .../device-energy-management-server.cpp | 8 +- .../device-energy-management-server.h | 52 +- src/python_testing/TC_DEM_2_2.py | 367 +++++++++ src/python_testing/matter_testing_support.py | 18 +- 49 files changed, 3002 insertions(+), 504 deletions(-) mode change 100755 => 100644 examples/all-clusters-app/ameba/chip_main.cmake mode change 100755 => 100644 examples/all-clusters-app/asr/BUILD.gn create mode 100644 examples/energy-management-app/energy-management-common/include/DEMManufacturerDelegate.h create mode 100644 examples/energy-management-app/energy-management-common/include/EnergyManagementAppCmdLineOptions.h create mode 100644 examples/energy-management-app/energy-management-common/include/EnergyTimeUtils.h create mode 100644 examples/energy-management-app/energy-management-common/include/FakeReadings.h create mode 100644 examples/energy-management-app/energy-management-common/src/DEMTestEventTriggers.cpp create mode 100644 examples/energy-management-app/energy-management-common/src/EnergyEvseEventTriggers.cpp create mode 100644 examples/energy-management-app/energy-management-common/src/EnergyReportingEventTriggers.cpp create mode 100644 examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp create mode 100644 examples/energy-management-app/energy-management-common/src/FakeReadings.cpp create mode 100644 src/python_testing/TC_DEM_2_2.py diff --git a/examples/all-clusters-app/ameba/chip_main.cmake b/examples/all-clusters-app/ameba/chip_main.cmake old mode 100755 new mode 100644 index 00358771e5adbf..ce557a2bffaf30 --- a/examples/all-clusters-app/ameba/chip_main.cmake +++ b/examples/all-clusters-app/ameba/chip_main.cmake @@ -187,8 +187,10 @@ list( ${chip_dir}/examples/microwave-oven-app/microwave-oven-common/src/microwave-oven-device.cpp + ${chip_dir}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp ${chip_dir}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp ${chip_dir}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp + ${chip_dir}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp ${chip_dir}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp ${chip_dir}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp ${chip_dir}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp diff --git a/examples/all-clusters-app/asr/BUILD.gn b/examples/all-clusters-app/asr/BUILD.gn old mode 100755 new mode 100644 index 6b016c5d81119c..6c9d334a1ed6c2 --- a/examples/all-clusters-app/asr/BUILD.gn +++ b/examples/all-clusters-app/asr/BUILD.gn @@ -84,9 +84,11 @@ asr_executable("clusters_app") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp", "${examples_plat_dir}/ButtonHandler.cpp", "${examples_plat_dir}/CHIPDeviceManager.cpp", "${examples_plat_dir}/LEDWidget.cpp", diff --git a/examples/all-clusters-app/cc13x4_26x4/BUILD.gn b/examples/all-clusters-app/cc13x4_26x4/BUILD.gn index 2a5609fb98b928..45efe2d45e9bce 100644 --- a/examples/all-clusters-app/cc13x4_26x4/BUILD.gn +++ b/examples/all-clusters-app/cc13x4_26x4/BUILD.gn @@ -74,9 +74,11 @@ ti_simplelink_executable("all-clusters-app") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp", "${chip_root}/examples/providers/DeviceInfoProviderImpl.cpp", "${chip_root}/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.cpp", "${project_dir}/main/AppTask.cpp", diff --git a/examples/all-clusters-app/infineon/psoc6/BUILD.gn b/examples/all-clusters-app/infineon/psoc6/BUILD.gn index aab6fdfd275986..2c0b0a6fee7dfa 100644 --- a/examples/all-clusters-app/infineon/psoc6/BUILD.gn +++ b/examples/all-clusters-app/infineon/psoc6/BUILD.gn @@ -126,9 +126,11 @@ psoc6_executable("clusters_app") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp", "${examples_plat_dir}/LEDWidget.cpp", "${examples_plat_dir}/init_psoc6Platform.cpp", "src/AppTask.cpp", diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index a2f4ff0ab1f781..e71574ce4db5ac 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -60,9 +60,11 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/all-clusters-app/linux/diagnostic-logs-provider-delegate-impl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/device-energy-management-mode.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/energy-evse-mode.cpp", "AllClustersCommandDelegate.cpp", diff --git a/examples/all-clusters-app/mbed/CMakeLists.txt b/examples/all-clusters-app/mbed/CMakeLists.txt index 3d1a179a93bbb2..9b490ff687d1e3 100644 --- a/examples/all-clusters-app/mbed/CMakeLists.txt +++ b/examples/all-clusters-app/mbed/CMakeLists.txt @@ -72,12 +72,13 @@ target_sources(${APP_TARGET} PRIVATE ${ALL_CLUSTERS_COMMON}/src/smco-stub.cpp ${ALL_CLUSTERS_COMMON}/src/static-supported-modes-manager.cpp ${ALL_CLUSTERS_COMMON}/src/static-supported-temperature-levels.cpp + ${ENERGY_MANAGEMENT_COMMON}/src/EnergyTimeUtils.cpp + ${ENERGY_MANAGEMENT_COMMON}/src/DeviceEnergyManagementDelegateImpl.cpp + ${ENERGY_MANAGEMENT_COMMON}/src/DeviceEnergyManagementManager.cpp + ${ENERGY_MANAGEMENT_COMMON}/src/EVSEManufacturerImpl.cpp ${ENERGY_MANAGEMENT_COMMON}/src/ElectricalPowerMeasurementDelegate.cpp ${ENERGY_MANAGEMENT_COMMON}/src/EnergyEvseDelegateImpl.cpp ${ENERGY_MANAGEMENT_COMMON}/src/EnergyEvseManager.cpp - ${ENERGY_MANAGEMENT_COMMON}/src/DeviceEnergyManagementDelegateImpl.cpp - ${ENERGY_MANAGEMENT_COMMON}/src/DeviceEnergyManagementManager.cpp - ) chip_configure_data_model(${APP_TARGET} diff --git a/examples/all-clusters-app/nrfconnect/CMakeLists.txt b/examples/all-clusters-app/nrfconnect/CMakeLists.txt index 8b5c3c1c9e72fd..c290003532a431 100644 --- a/examples/all-clusters-app/nrfconnect/CMakeLists.txt +++ b/examples/all-clusters-app/nrfconnect/CMakeLists.txt @@ -42,7 +42,7 @@ include(${CHIP_ROOT}/src/app/chip_data_model.cmake) target_include_directories(app PRIVATE main/include ${ALL_CLUSTERS_COMMON_DIR}/include - ${ENERGY_MANAGEMENT_COMMON_DIR}/include + ${ENERGY_MANAGEMENT_COMMON_DIR}/include ${GEN_DIR}/app-common ${GEN_DIR}/all-clusters-app ${NRFCONNECT_COMMON}/util/include) @@ -57,13 +57,15 @@ target_sources(app PRIVATE ${ALL_CLUSTERS_COMMON_DIR}/src/fan-stub.cpp ${ALL_CLUSTERS_COMMON_DIR}/src/oven-modes.cpp ${ALL_CLUSTERS_COMMON_DIR}/src/energy-evse-stub.cpp - ${ALL_CLUSTERS_COMMON_DIR}/src/device-energy-management-stub.cpp + ${ALL_CLUSTERS_COMMON_DIR}/src/device-energy-management-stub.cpp ${ALL_CLUSTERS_COMMON_DIR}/src/binding-handler.cpp ${ALL_CLUSTERS_COMMON_DIR}/src/air-quality-instance.cpp ${ALL_CLUSTERS_COMMON_DIR}/src/concentration-measurement-instances.cpp ${ALL_CLUSTERS_COMMON_DIR}/src/resource-monitoring-delegates.cpp + ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EnergyTimeUtils.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/DeviceEnergyManagementDelegateImpl.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/DeviceEnergyManagementManager.cpp + ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EVSEManufacturerImpl.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/ElectricalPowerMeasurementDelegate.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EnergyEvseDelegateImpl.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EnergyEvseManager.cpp diff --git a/examples/all-clusters-app/nxp/mw320/BUILD.gn b/examples/all-clusters-app/nxp/mw320/BUILD.gn index ebae8c34bccdee..83877a175993df 100644 --- a/examples/all-clusters-app/nxp/mw320/BUILD.gn +++ b/examples/all-clusters-app/nxp/mw320/BUILD.gn @@ -91,9 +91,11 @@ mw320_executable("shell_mw320") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp", "${chip_root}/src/lib/shell/streamer_mw320.cpp", "binding-handler.cpp", "include/CHIPProjectConfig.h", diff --git a/examples/all-clusters-app/openiotsdk/CMakeLists.txt b/examples/all-clusters-app/openiotsdk/CMakeLists.txt index 6d2fb76c0db919..1395cf170a8b79 100644 --- a/examples/all-clusters-app/openiotsdk/CMakeLists.txt +++ b/examples/all-clusters-app/openiotsdk/CMakeLists.txt @@ -48,7 +48,7 @@ target_include_directories(${APP_TARGET} PRIVATE main/include ${ALL_CLUSTERS_COMMON}/include - ${ENERGY_MANAGEMENT_COMMON}/include + ${ENERGY_MANAGEMENT_COMMON}/include ) target_sources(${APP_TARGET} @@ -60,16 +60,18 @@ target_sources(${APP_TARGET} ${ALL_CLUSTERS_COMMON}/src/concentration-measurement-instances.cpp ${ALL_CLUSTERS_COMMON}/src/fan-stub.cpp ${ALL_CLUSTERS_COMMON}/src/oven-modes.cpp - ${ALL_CLUSTERS_COMMON}/src/device-energy-management-stub.cpp + ${ALL_CLUSTERS_COMMON}/src/device-energy-management-stub.cpp ${ALL_CLUSTERS_COMMON}/src/energy-evse-stub.cpp ${ALL_CLUSTERS_COMMON}/src/resource-monitoring-delegates.cpp ${ALL_CLUSTERS_COMMON}/src/static-supported-modes-manager.cpp ${ALL_CLUSTERS_COMMON}/src/binding-handler.cpp + ${ENERGY_MANAGEMENT_COMMON}/src/EnergyTimeUtils.cpp + ${ENERGY_MANAGEMENT_COMMON}/src/DeviceEnergyManagementDelegateImpl.cpp + ${ENERGY_MANAGEMENT_COMMON}/src/DeviceEnergyManagementManager.cpp + ${ENERGY_MANAGEMENT_COMMON}/src/EVSEManufacturerImpl.cpp ${ENERGY_MANAGEMENT_COMMON}/src/ElectricalPowerMeasurementDelegate.cpp ${ENERGY_MANAGEMENT_COMMON}/src/EnergyEvseDelegateImpl.cpp ${ENERGY_MANAGEMENT_COMMON}/src/EnergyEvseManager.cpp - ${ENERGY_MANAGEMENT_COMMON}/src/DeviceEnergyManagementDelegateImpl.cpp - ${ENERGY_MANAGEMENT_COMMON}/src/DeviceEnergyManagementManager.cpp ) target_link_libraries(${APP_TARGET} diff --git a/examples/all-clusters-app/telink/CMakeLists.txt b/examples/all-clusters-app/telink/CMakeLists.txt index d5f2132c9d2747..149b38e3528cce 100644 --- a/examples/all-clusters-app/telink/CMakeLists.txt +++ b/examples/all-clusters-app/telink/CMakeLists.txt @@ -31,7 +31,7 @@ project(chip-telink-all-clusters-app-example) target_include_directories(app PRIVATE include ${ALL_CLUSTERS_COMMON_DIR}/include - ${ENERGY_MANAGEMENT_COMMON_DIR}/include + ${ENERGY_MANAGEMENT_COMMON_DIR}/include ${GEN_DIR}/app-common ${GEN_DIR}/all-clusters-app ${TELINK_COMMON}/common/include @@ -48,14 +48,16 @@ target_sources(app PRIVATE ${ALL_CLUSTERS_COMMON_DIR}/src/air-quality-instance.cpp ${ALL_CLUSTERS_COMMON_DIR}/src/concentration-measurement-instances.cpp ${ALL_CLUSTERS_COMMON_DIR}/src/fan-stub.cpp - ${ALL_CLUSTERS_COMMON_DIR}/src/device-energy-management-stub.cpp + ${ALL_CLUSTERS_COMMON_DIR}/src/device-energy-management-stub.cpp ${ALL_CLUSTERS_COMMON_DIR}/src/energy-evse-stub.cpp ${ALL_CLUSTERS_COMMON_DIR}/src/resource-monitoring-delegates.cpp + ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EnergyTimeUtils.cpp + ${ENERGY_MANAGEMENT_COMMON_DIR}/src/DeviceEnergyManagementDelegateImpl.cpp + ${ENERGY_MANAGEMENT_COMMON_DIR}/src/DeviceEnergyManagementManager.cpp + ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EVSEManufacturerImpl.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/ElectricalPowerMeasurementDelegate.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EnergyEvseDelegateImpl.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EnergyEvseManager.cpp - ${ENERGY_MANAGEMENT_COMMON_DIR}/src/DeviceEnergyManagementDelegateImpl.cpp - ${ENERGY_MANAGEMENT_COMMON_DIR}/src/DeviceEnergyManagementManager.cpp ${TELINK_COMMON}/common/src/mainCommon.cpp ${TELINK_COMMON}/common/src/AppTaskCommon.cpp ${TELINK_COMMON}/util/src/LEDManager.cpp diff --git a/examples/all-clusters-app/tizen/BUILD.gn b/examples/all-clusters-app/tizen/BUILD.gn index 364e9ce857d5da..9aff82b57b2876 100644 --- a/examples/all-clusters-app/tizen/BUILD.gn +++ b/examples/all-clusters-app/tizen/BUILD.gn @@ -40,9 +40,11 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp", ] deps = [ diff --git a/examples/energy-management-app/energy-management-common/BUILD.gn b/examples/energy-management-app/energy-management-common/BUILD.gn index 937aca9f1746d0..e6b67461f40a3b 100644 --- a/examples/energy-management-app/energy-management-common/BUILD.gn +++ b/examples/energy-management-app/energy-management-common/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Project CHIP Authors +# Copyright (c) 2023-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. diff --git a/examples/energy-management-app/energy-management-common/include/DEMManufacturerDelegate.h b/examples/energy-management-app/energy-management-common/include/DEMManufacturerDelegate.h new file mode 100644 index 00000000000000..65d214d31904ab --- /dev/null +++ b/examples/energy-management-app/energy-management-common/include/DEMManufacturerDelegate.h @@ -0,0 +1,88 @@ +/* + * + * 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. + */ + +#pragma once + +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace DeviceEnergyManagement { + +/** + * Class to abstract manufacturer specific functionality + */ +class DEMManufacturerDelegate +{ +public: + DEMManufacturerDelegate() {} + + virtual ~DEMManufacturerDelegate() {} + + // The PowerAdjustEnd event needs to report the approximate energy used by the ESA during the session. + virtual int64_t GetApproxEnergyDuringSession() = 0; + + virtual CHIP_ERROR HandleDeviceEnergyManagementPowerAdjustRequest(const int64_t powerMw, const uint32_t durationS, + AdjustmentCauseEnum cause) + { + return CHIP_NO_ERROR; + } + + virtual CHIP_ERROR HandleDeviceEnergyManagementPowerAdjustCompletion() { return CHIP_NO_ERROR; } + + virtual CHIP_ERROR HandleDeviceEnergyManagementCancelPowerAdjustRequest(CauseEnum cause) { return CHIP_NO_ERROR; } + + virtual CHIP_ERROR HandleDeviceEnergyManagementStartTimeAdjustRequest(const uint32_t requestedStartTimeUtc, + AdjustmentCauseEnum cause) + { + return CHIP_NO_ERROR; + } + + virtual CHIP_ERROR HandleDeviceEnergyManagementPauseRequest(const uint32_t durationS, AdjustmentCauseEnum cause) + { + return CHIP_NO_ERROR; + } + + virtual CHIP_ERROR HandleDeviceEnergyManagementPauseCompletion() { return CHIP_NO_ERROR; } + + virtual CHIP_ERROR HandleDeviceEnergyManagementCancelPauseRequest(CauseEnum cause) { return CHIP_NO_ERROR; } + + virtual CHIP_ERROR HandleDeviceEnergyManagementCancelRequest() { return CHIP_NO_ERROR; } + + virtual CHIP_ERROR + HandleModifyForecastRequest(const uint32_t forecastID, + const DataModel::DecodableList & slotAdjustments, + AdjustmentCauseEnum cause) + { + return CHIP_NO_ERROR; + } + + virtual CHIP_ERROR RequestConstraintBasedForecast( + const DataModel::DecodableList & constraints, + AdjustmentCauseEnum cause) + { + return CHIP_NO_ERROR; + } +}; + +} // namespace DeviceEnergyManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/energy-management-app/energy-management-common/include/DeviceEnergyManagementDelegateImpl.h b/examples/energy-management-app/energy-management-common/include/DeviceEnergyManagementDelegateImpl.h index 1248fc405a3c8a..e540d56e030058 100644 --- a/examples/energy-management-app/energy-management-common/include/DeviceEnergyManagementDelegateImpl.h +++ b/examples/energy-management-app/energy-management-common/include/DeviceEnergyManagementDelegateImpl.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,12 +18,10 @@ #pragma once -#include "app/clusters/device-energy-management-server/device-energy-management-server.h" - +#include +#include #include -#include -using chip::Protocols::InteractionModel::Status; namespace chip { namespace app { namespace Clusters { @@ -35,52 +33,267 @@ namespace DeviceEnergyManagement { class DeviceEnergyManagementDelegate : public DeviceEnergyManagement::Delegate { public: - virtual Status PowerAdjustRequest(const int64_t power, const uint32_t duration, AdjustmentCauseEnum cause) override; - virtual Status CancelPowerAdjustRequest() override; - virtual Status StartTimeAdjustRequest(const uint32_t requestedStartTime, AdjustmentCauseEnum cause) override; - virtual Status PauseRequest(const uint32_t duration, AdjustmentCauseEnum cause) override; - virtual Status ResumeRequest() override; - virtual Status - ModifyForecastRequest(const uint32_t forecastId, + DeviceEnergyManagementDelegate(); + + void SetDeviceEnergyManagementInstance(DeviceEnergyManagement::Instance & instance); + + void SetDEMManufacturerDelegate(DEMManufacturerDelegate & deviceEnergyManagementManufacturerDelegate); + + /** + * + * Implement the DeviceEnergyManagement::Delegate interface + * + */ + + /** + * @brief Implements a handler to begin to adjust client power + * consumption/generation to the level requested. + * + * Note callers must call GetPowerAdjustmentCapability and ensure the return value is not null + * before calling PowerAdjustRequest. + * + * @param power Milli-Watts the ESA SHALL use during the adjustment period. + * @param duration The duration that the ESA SHALL maintain the requested power for. + * @return Success if the adjustment is accepted; otherwise the command SHALL be rejected with appropriate error. + */ + chip::Protocols::InteractionModel::Status PowerAdjustRequest(const int64_t powerMw, const uint32_t durationS, + AdjustmentCauseEnum cause) override; + + /** + * @brief Make the ESA end the active power adjustment session & return to normal (or idle) power levels. + * The ESA SHALL also generate an PowerAdjustEnd Event and the ESAState SHALL be restored to Online. + * + * @return It should report SUCCESS if successful and FAILURE otherwise. + */ + chip::Protocols::InteractionModel::Status CancelPowerAdjustRequest() override; + + /** + * @brief The ESA SHALL update its Forecast attribute with the RequestedStartTime including a new ForecastID. + * + * If the ESA supports ForecastAdjustment, and the ESAState is not UserOptOut and the RequestedStartTime is after + * the EarliestStartTime and the resulting EndTime is before the LatestEndTime, then ESA SHALL accept the request + * to modify the Start Time. + * A client can estimate the entire Forecast sequence duration by computing the EndTime - StartTime fields from the + * Forecast attribute, and therefore avoid scheduling the start time too late. + * + * @param requestedStartTime The requested start time in UTC that the client would like the appliance to shift its power + * forecast to. + * @param cause Who (Grid/local) is triggering this change. + * + * @return Success if the StartTime in the Forecast is updated, otherwise the command SHALL be rejected with appropriate + * IM_Status. + */ + chip::Protocols::InteractionModel::Status StartTimeAdjustRequest(const uint32_t requestedStartTimeUtc, + AdjustmentCauseEnum cause) override; + + /** + * @brief Handler for PauseRequest command + * + * If the ESA supports FA and the SlotIsPauseable field is true in the ActiveSlotNumber + * index in the Slots list, and the ESAState is not UserOptOut then the ESA SHALL allow its current + * operation to be Paused. + * + * During this state the ESA SHALL not consume or produce significant power (other than required to keep its + * basic control system operational). + * + * @param duration Duration that the ESA SHALL be paused for. + * @return Success if the ESA is paused, otherwise returns other IM_Status. + */ + chip::Protocols::InteractionModel::Status PauseRequest(const uint32_t durationS, AdjustmentCauseEnum cause) override; + + /** + * @brief Handler for ResumeRequest command + * + * If the ESA supports FA and it is currently Paused then the ESA SHALL resume its operation. + * The ESA SHALL also generate a Resumed Event and the ESAState SHALL be updated accordingly to + * reflect its current state. + * + * @return Success if the ESA is resumed, otherwise returns other IM_Status. + */ + chip::Protocols::InteractionModel::Status ResumeRequest() override; + + /** + * @brief Handler for ModifyForecastRequest + * + * If the ESA supports FA, and the ESAState is not UserOptOut it SHALL attempt to adjust its power forecast. + * This allows a one or more modifications in a single command by sending a list of modifications (one for each 'slot'). + * Attempts to modify slots which have already past, SHALL result in the entire command being rejected. + * If the ESA accepts the requested Forecast then it SHALL update its Forecast attribute (incrementing its ForecastID) + * and run the revised Forecast as its new intended operation. + * + * *** NOTE *** for the memory management of the forecast object, see the comment before the mForecast delaration below. + * + * @param forecastID Indicates the ESA ForecastID that is to be modified. + * @param slotAdjustments List of adjustments to be applied to the ESA, corresponding to the expected ESA forecastID. + * @return Success if the entire list of SlotAdjustmentStruct are accepted, otherwise the command + * SHALL be rejected returning other IM_Status. + */ + chip::Protocols::InteractionModel::Status + ModifyForecastRequest(const uint32_t forecastID, const DataModel::DecodableList & slotAdjustments, AdjustmentCauseEnum cause) override; - virtual Status + + /** + * @brief Handler for RequestConstraintBasedForecast + * + * The ESA SHALL inspect the requested power limits to ensure that there are no overlapping elements. The ESA + * manufacturer may also reject the request if it could cause the user’s preferences to be breached (e.g. may + * cause the home to be too hot or too cold, or a battery to be insufficiently charged). + * If the ESA can meet the requested power limits, it SHALL regenerate a new Power Forecast with a new ForecastID. + * + * @param constraints Sequence of turn up/down power requests that the ESA is being asked to constrain its operation within. + * @return Success if successful, otherwise the command SHALL be rejected returning other IM_Status. + */ + chip::Protocols::InteractionModel::Status RequestConstraintBasedForecast(const DataModel::DecodableList & constraints, AdjustmentCauseEnum cause) override; - virtual Status CancelRequest() override; + + /** + * @brief Handler for CancelRequest + * + * The ESA SHALL attempt to cancel the effects of any previous adjustment request commands, and re-evaluate its + * forecast for intended operation ignoring those previous requests. + * + * If the ESA ForecastStruct ForecastUpdateReason was already `Internal Optimization`, then the command SHALL + * be rejected with FAILURE. + * + * If the command is accepted, the ESA SHALL update its ESAState if required, and the command status returned + * SHALL be SUCCESS. + * + * The ESA SHALL update its Forecast attribute to match its new intended operation, and update the + * ForecastStruct.ForecastUpdateReason to `Internal Optimization` + * + * @return Success if successful, otherwise the command SHALL be rejected returning other IM_Status. + */ + chip::Protocols::InteractionModel::Status CancelRequest() override; // ------------------------------------------------------------------ - // Get attribute methods - virtual ESATypeEnum GetESAType() override; - virtual bool GetESACanGenerate() override; - virtual ESAStateEnum GetESAState() override; - virtual int64_t GetAbsMinPower() override; - virtual int64_t GetAbsMaxPower() override; - virtual Attributes::PowerAdjustmentCapability::TypeInfo::Type GetPowerAdjustmentCapability() override; - virtual DataModel::Nullable GetForecast() override; - virtual OptOutStateEnum GetOptOutState() override; + // Overridden DeviceEnergyManagement::Delegate Get attribute methods + + ESATypeEnum GetESAType() override; + bool GetESACanGenerate() override; + ESAStateEnum GetESAState() override; + int64_t GetAbsMinPower() override; + int64_t GetAbsMaxPower() override; + const DataModel::Nullable & GetPowerAdjustmentCapability() override; + const DataModel::Nullable & GetForecast() override; + OptOutStateEnum GetOptOutState() override; // ------------------------------------------------------------------ - // Set attribute methods - virtual CHIP_ERROR SetESAType(ESATypeEnum) override; - virtual CHIP_ERROR SetESACanGenerate(bool) override; - virtual CHIP_ERROR SetESAState(ESAStateEnum) override; - virtual CHIP_ERROR SetAbsMinPower(int64_t) override; - virtual CHIP_ERROR SetAbsMaxPower(int64_t) override; - virtual CHIP_ERROR SetPowerAdjustmentCapability(Attributes::PowerAdjustmentCapability::TypeInfo::Type) override; - virtual CHIP_ERROR SetForecast(DataModel::Nullable) override; - virtual CHIP_ERROR SetOptOutState(OptOutStateEnum) override; + // Overridden DeviceEnergyManagement::Delegate Set attribute methods + CHIP_ERROR SetESAState(ESAStateEnum) override; + + // Local Set methods + CHIP_ERROR SetESAType(ESATypeEnum); + CHIP_ERROR SetESACanGenerate(bool); + CHIP_ERROR SetAbsMinPower(int64_t); + CHIP_ERROR SetAbsMaxPower(int64_t); + CHIP_ERROR SetPowerAdjustmentCapability(const DataModel::Nullable &); + + // The DeviceEnergyManagementDelegate owns the master copy of the ForecastStruct object which is accessed via GetForecast and + // SetForecast. The slots field of forecast is owned and managed by the object that implements the DEMManufacturerDelegate + // interface. The slots memory MUST exist for the lifetime of the forecast object from where it is referenced. + // + // The rationale for this is as follows: + // It is envisioned there will be one master forecast object declared in DeviceEnergyManagementDelegate. When + // constructed, the field DataModel::List slots will be empty. + // + // The EVSEManufacturerImpl class (examples/energy-management-app/energy-management-common/include/EVSEManufacturerImpl.h) is + // an example implementation that a specific vendor can use as a template. It understands how the underlying energy appliance + // functions. EVSEManufacturerImpl inherits from DEMManufacturerDelegate + // (examples/energy-management-app/energy-management-common/include/DEMManufacturerDelegate.h) which is a generic interface + // and how the DeviceEnergyManagementDelegate class + // (examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp) communicates from the + // generic cluster world to the specific appliance implementation (EVSEManufacturerImpl). + // + // EVSEManufacturerImpl understands the slot structures of the appliance and configures the slot structures as follows: + // + // Call DeviceEnergyManagementDelegate::GetForecast() to get the current forecast + // Modify the slot structure - the slots memory is owned by EVSEManufacturerImpl + // Call DeviceEnergyManagementDelegate::GetForecast() to set the current forecast + // + // + // The cluster object DeviceEnergyManagement::Instance + // (src/app/clusters/device-energy-management-server/device-energy-management-server.cpp) only reads the slots field of + // forecast when checking commands (indeed it does not modify any forecast fields itself). The DeviceEnergyManagementDelegate + // object does modify some of forecast's fields but does NOT modify the slots field. The only command that can modify the + // slots field is HandleModifyForecastRequest. Whilst DeviceEnergyManagementDelegate::ModifyForecastRequest does some state + // checking, the slots field is only modified by the EVSEManufacturerImpl object via the call + // DEMManufacturerDelegate::HandleModifyForecastRequest. DEMManufacturerDelegate::HandleModifyForecastRequest may + // delete/allocate the slots memory but this will be done atomically in the call to + // DEMManufacturerDelegate::HandleModifyForecastRequest so the underlying memory is coherent => the call to + // DEMManufacturerDelegate::HandleModifyForecastRequest cannot be interrupted by any other CHIP task activity. + CHIP_ERROR SetForecast(const DataModel::Nullable &); + + CHIP_ERROR SetOptOutState(OptOutStateEnum); + + // Returns whether the DeviceEnergyManagement is supported + uint32_t HasFeature(Feature feature) const; private: + /** + * @brief Handle a PowerAdjustRequest failing + * + * Cleans up the PowerAdjust state should the request fail + */ + void HandlePowerAdjustRequestFailure(); + + // Methods to handle when a PowerAdjustment completes + static void PowerAdjustTimerExpiry(System::Layer * systemLayer, void * delegate); + void HandlePowerAdjustTimerExpiry(); + + // Method to cancel a PowerAdjustment + CHIP_ERROR CancelPowerAdjustRequestAndGenerateEvent(CauseEnum cause); + + // Method to generate a PowerAdjustEnd event + CHIP_ERROR GeneratePowerAdjustEndEvent(CauseEnum cause); + + /** + * @brief Handle a PauseRequest failing + * + * Cleans up the state should the PauseRequest fail + */ + void HandlePauseRequestFailure(); + + // Methods to handle when a PauseRequest completes + static void PauseRequestTimerExpiry(System::Layer * systemLayer, void * delegate); + void HandlePauseRequestTimerExpiry(); + + // Method to cancel a PauseRequest + CHIP_ERROR CancelPauseRequestAndGenerateEvent(CauseEnum cause); + + // Method to generate a Paused event + CHIP_ERROR GenerateResumedEvent(CauseEnum cause); + +private: + // Have a pointer to partner instance object + DeviceEnergyManagement::Instance * mpDEMInstance; + + // The DEMManufacturerDelegate object knows how to handle + // manufacturer/product specific operations + DEMManufacturerDelegate * mpDEMManufacturerDelegate; + + // Various attributes ESATypeEnum mEsaType; bool mEsaCanGenerate; ESAStateEnum mEsaState; - int64_t mAbsMinPower; - int64_t mAbsMaxPower; - Attributes::PowerAdjustmentCapability::TypeInfo::Type mPowerAdjustmentCapability; + int64_t mAbsMinPowerMw; + int64_t mAbsMaxPowerMw; + OptOutStateEnum mOptOutState; + + DataModel::Nullable mPowerAdjustCapabilityStruct; + + // See note above on SetForecast() about mForecast memory management DataModel::Nullable mForecast; - // Default to NoOptOut - OptOutStateEnum mOptOutState = OptOutStateEnum::kNoOptOut; + + // Keep track whether a PowerAdjustment is in progress + bool mPowerAdjustmentInProgress; + + // Keep track of when that PowerAdjustment started + uint32_t mPowerAdjustmentStartTimeUtc; + + // Keep track whether a PauseRequest is in progress + bool mPauseRequestInProgress; }; } // namespace DeviceEnergyManagement diff --git a/examples/energy-management-app/energy-management-common/include/DeviceEnergyManagementManager.h b/examples/energy-management-app/energy-management-common/include/DeviceEnergyManagementManager.h index aec875ff0d1f99..b7ae4565656ff3 100644 --- a/examples/energy-management-app/energy-management-common/include/DeviceEnergyManagementManager.h +++ b/examples/energy-management-app/energy-management-common/include/DeviceEnergyManagementManager.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,7 +19,6 @@ #pragma once #include -#include #include #include @@ -28,14 +27,11 @@ namespace app { namespace Clusters { using namespace chip::app::Clusters::DeviceEnergyManagement; + class DeviceEnergyManagementManager : public Instance { public: - DeviceEnergyManagementManager(EndpointId aEndpointId, DeviceEnergyManagementDelegate & aDelegate, Feature aFeature) : - DeviceEnergyManagement::Instance(aEndpointId, aDelegate, aFeature) - { - mDelegate = &aDelegate; - } + DeviceEnergyManagementManager(EndpointId aEndpointId, DeviceEnergyManagementDelegate & aDelegate, Feature aFeature); // Delete copy constructor and assignment operator. DeviceEnergyManagementManager(const DeviceEnergyManagementManager &) = delete; diff --git a/examples/energy-management-app/energy-management-common/include/EVSECallbacks.h b/examples/energy-management-app/energy-management-common/include/EVSECallbacks.h index 7d288809952ee3..4cbee2a6b7ca9f 100644 --- a/examples/energy-management-app/energy-management-common/include/EVSECallbacks.h +++ b/examples/energy-management-app/energy-management-common/include/EVSECallbacks.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/examples/energy-management-app/energy-management-common/include/EVSEManufacturerImpl.h b/examples/energy-management-app/energy-management-common/include/EVSEManufacturerImpl.h index 23994e7f5d9454..2faf81046cfdf2 100644 --- a/examples/energy-management-app/energy-management-common/include/EVSEManufacturerImpl.h +++ b/examples/energy-management-app/energy-management-common/include/EVSEManufacturerImpl.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,6 +18,7 @@ #pragma once +#include #include #include #include @@ -33,20 +34,24 @@ namespace EnergyEvse { * The EVSEManufacturer example class */ -class EVSEManufacturer +class EVSEManufacturer : public DEMManufacturerDelegate { public: EVSEManufacturer(EnergyEvseManager * aEvseInstance, ElectricalPowerMeasurement::ElectricalPowerMeasurementInstance * aEPMInstance, - PowerTopology::PowerTopologyInstance * aPTInstance) + PowerTopology::PowerTopologyInstance * aPTInstance, DeviceEnergyManagementManager * aDEMInstance) { mEvseInstance = aEvseInstance; mEPMInstance = aEPMInstance; mPTInstance = aPTInstance; + mDEMInstance = aDEMInstance; } + + virtual ~EVSEManufacturer() {} + EnergyEvseManager * GetEvseInstance() { return mEvseInstance; } + ElectricalPowerMeasurement::ElectricalPowerMeasurementInstance * GetEPMInstance() { return mEPMInstance; } - PowerTopology::PowerTopologyInstance * GetPTInstance() { return mPTInstance; } EnergyEvseDelegate * GetEvseDelegate() { @@ -75,6 +80,40 @@ class EVSEManufacturer return nullptr; } + DeviceEnergyManagementDelegate * GetDEMDelegate() + { + if (mDEMInstance) + { + return mDEMInstance->GetDelegate(); + } + return nullptr; + } + + /** + * + * Implement the DEMManufacturerDelegate interface + * + */ + // The PowerAdjustEnd event needs to report the approximate energy used by the ESA during the session. + int64_t GetApproxEnergyDuringSession() override; + CHIP_ERROR HandleDeviceEnergyManagementPowerAdjustRequest(const int64_t powerMw, const uint32_t durationS, + AdjustmentCauseEnum cause) override; + CHIP_ERROR HandleDeviceEnergyManagementPowerAdjustCompletion() override; + CHIP_ERROR HandleDeviceEnergyManagementCancelPowerAdjustRequest(CauseEnum cause) override; + CHIP_ERROR HandleDeviceEnergyManagementStartTimeAdjustRequest(const uint32_t requestedStartTime, + AdjustmentCauseEnum cause) override; + CHIP_ERROR HandleDeviceEnergyManagementPauseRequest(const uint32_t durationS, AdjustmentCauseEnum cause) override; + CHIP_ERROR HandleDeviceEnergyManagementPauseCompletion() override; + CHIP_ERROR HandleDeviceEnergyManagementCancelPauseRequest(CauseEnum cause) override; + CHIP_ERROR HandleDeviceEnergyManagementCancelRequest() override; + CHIP_ERROR HandleModifyForecastRequest( + const uint32_t forecastID, + const DataModel::DecodableList & slotAdjustments, + AdjustmentCauseEnum cause) override; + CHIP_ERROR RequestConstraintBasedForecast( + const DataModel::DecodableList & constraints, + AdjustmentCauseEnum cause) override; + /** * @brief Called at start up to apply hardware settings */ @@ -172,6 +211,7 @@ class EVSEManufacturer EnergyEvseManager * mEvseInstance; ElectricalPowerMeasurement::ElectricalPowerMeasurementInstance * mEPMInstance; PowerTopology::PowerTopologyInstance * mPTInstance; + DeviceEnergyManagementManager * mDEMInstance; int64_t mLastChargingEnergyMeter = 0; int64_t mLastDischargingEnergyMeter = 0; diff --git a/examples/energy-management-app/energy-management-common/include/EnergyEvseDelegateImpl.h b/examples/energy-management-app/energy-management-common/include/EnergyEvseDelegateImpl.h index df0d08b76f7e4e..aed42fa857968a 100644 --- a/examples/energy-management-app/energy-management-common/include/EnergyEvseDelegateImpl.h +++ b/examples/energy-management-app/energy-management-common/include/EnergyEvseDelegateImpl.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,13 +26,6 @@ using chip::Protocols::InteractionModel::Status; -/** - * @brief Helper function to get current timestamp in Epoch format - * - * @param chipEpoch reference to hold return timestamp - */ -CHIP_ERROR GetEpochTS(uint32_t & chipEpoch); - namespace chip { namespace app { namespace Clusters { diff --git a/examples/energy-management-app/energy-management-common/include/EnergyEvseManager.h b/examples/energy-management-app/energy-management-common/include/EnergyEvseManager.h index fc0d41b9259643..f39b0c0c5dd34a 100644 --- a/examples/energy-management-app/energy-management-common/include/EnergyEvseManager.h +++ b/examples/energy-management-app/energy-management-common/include/EnergyEvseManager.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/examples/energy-management-app/energy-management-common/include/EnergyManagementAppCmdLineOptions.h b/examples/energy-management-app/energy-management-common/include/EnergyManagementAppCmdLineOptions.h new file mode 100644 index 00000000000000..a44140b94c762b --- /dev/null +++ b/examples/energy-management-app/energy-management-common/include/EnergyManagementAppCmdLineOptions.h @@ -0,0 +1,34 @@ +/* + * + * 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. + */ + +#pragma once + +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace DeviceEnergyManagement { + +chip::BitMask GetFeatureMapFromCmdLine(); + +} // namespace DeviceEnergyManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/energy-management-app/energy-management-common/include/EnergyTimeUtils.h b/examples/energy-management-app/energy-management-common/include/EnergyTimeUtils.h new file mode 100644 index 00000000000000..5e882063ad7540 --- /dev/null +++ b/examples/energy-management-app/energy-management-common/include/EnergyTimeUtils.h @@ -0,0 +1,74 @@ +/* + * + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace DeviceEnergyManagement { + +/** + * @brief Helper function to get current timestamp in Epoch format + * + * @param chipEpoch reference to hold return timestamp + */ +CHIP_ERROR GetEpochTS(uint32_t & chipEpoch); + +/** + * @brief Helper function to get current timestamp and work out the day of week + * + * NOTE that the time_t is converted using localtime to provide the timestamp + * in local time. If this is not supported on some platforms an alternative + * implementation may be required. + * + * @param unixEpoch (as time_t) + * + * @return bitmap value for day of week as defined by EnergyEvse::TargetDayOfWeekBitmap. Note + * only one bit will be set for the day of the week. + */ +BitMask GetLocalDayOfWeekFromUnixEpoch(time_t unixEpoch); + +/** + * @brief Helper function to get current timestamp and work out the day of week based on localtime + * + * @param reference to hold the day of week as a bitmap as defined by EnergyEvse::TargetDayOfWeekBitmap. + * Note only one bit will be set for the current day. + */ +CHIP_ERROR GetLocalDayOfWeekNow(BitMask & dayOfWeekMap); + +/** + * @brief Helper function to get current timestamp and work out the current number of minutes + * past midnight based on localtime + * + * @param reference to hold the number of minutes past midnight + */ +CHIP_ERROR GetMinutesPastMidnight(uint16_t & minutesPastMidnight); + +} // namespace DeviceEnergyManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/energy-management-app/energy-management-common/include/FakeReadings.h b/examples/energy-management-app/energy-management-common/include/FakeReadings.h new file mode 100644 index 00000000000000..f8334ba2708b7e --- /dev/null +++ b/examples/energy-management-app/energy-management-common/include/FakeReadings.h @@ -0,0 +1,115 @@ +/* + * + * 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. + */ + +#pragma once + +#include +#include + +class FakeReadings +{ +public: + static FakeReadings & GetInstance(); + + /** + * @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 max randomness of the + * random power values around the mean power of the load + * @param[in] aVoltage_mV - the nominal voltage measurement + * @param[in] aVoltageRandomness_mV This is used to define the max randomness of the + * random voltage values + * @param[in] aCurrent_mA - the nominal current measurement + * @param[in] aCurrentRandomness_mA This is used to define the max randomness of the + * random current values + * @param[in] aInterval_s - the callback interval in seconds + * @param[in] aReset - boolean: true will reset the energy values to 0 + */ + void StartFakeReadings(chip::EndpointId aEndpointId, int64_t aPower_mW, uint32_t aPowerRandomness_mW, int64_t aVoltage_mV, + uint32_t aVoltageRandomness_mV, int64_t aCurrent_mA, uint32_t aCurrentRandomness_mA, uint8_t aInterval_s, + bool aReset); + + /** + * @brief Stops any active updates to the fake load data callbacks + */ + void StopFakeReadings(); + + /** + * @brief Sends fake meter data into the cluster and restarts the timer + */ + void FakeReadingsUpdate(); + + /** + * @brief Timer expiry callback to handle fake load + */ + static void FakeReadingsTimerExpiry(chip::System::Layer * systemLayer, void * manufacturer); + + CHIP_ERROR ConfigureForecast(uint16_t numSlots); + +private: + FakeReadings(); + ~FakeReadings(); + +private: + /* If enabled then the timer callback will re-trigger */ + bool bEnabled; + + /* Which endpoint the meter is on */ + chip::EndpointId mEndpointId; + + /* Interval in seconds to callback */ + uint8_t mInterval_s; + + /* Active Power on the load in mW (signed value) +ve = imported */ + int64_t mPower_mW; + + /* The amount to randomize the Power on the load in mW */ + uint32_t mPowerRandomness_mW; + + /* Voltage reading in mV (signed value) */ + int64_t mVoltage_mV; + + /* The amount to randomize the Voltage in mV */ + uint32_t mVoltageRandomness_mV; + + /* ActiveCurrent reading in mA (signed value) */ + int64_t mCurrent_mA; + + /* The amount to randomize the ActiveCurrent in mA */ + uint32_t mCurrentRandomness_mA; + + /* These energy values can only be positive values. However the underlying + * energy type (power_mWh) is signed, so keeping with that convention. + */ + + /* Cumulative Energy Imported which is updated if mPower > 0 */ + int64_t mTotalEnergyImported = 0; + + /* Cumulative Energy Imported which is updated if mPower < 0 */ + int64_t mTotalEnergyExported = 0; + + /* Periodic Energy Imported which is updated if mPower > 0 */ + int64_t mPeriodicEnergyImported = 0; + + /* Periodic Energy Imported which is updated if mPower < 0 */ + int64_t mPeriodicEnergyExported = 0; +}; diff --git a/examples/energy-management-app/energy-management-common/src/DEMTestEventTriggers.cpp b/examples/energy-management-app/energy-management-common/src/DEMTestEventTriggers.cpp new file mode 100644 index 00000000000000..469b781a9b6211 --- /dev/null +++ b/examples/energy-management-app/energy-management-common/src/DEMTestEventTriggers.cpp @@ -0,0 +1,386 @@ +/* + * + * 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 "FakeReadings.h" + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::DeviceEnergyManagement; + +static constexpr uint16_t MAX_SLOTS = 10; +static constexpr uint16_t MAX_POWER_ADJUSTMENTS = 5; + +static chip::app::Clusters::DeviceEnergyManagement::Structs::SlotStruct::Type sSlots[MAX_SLOTS]; +static chip::app::Clusters::DeviceEnergyManagement::Structs::ForecastStruct::Type sForecastStruct; +static chip::app::DataModel::Nullable sForecast; + +static chip::app::Clusters::DeviceEnergyManagement::Structs::PowerAdjustStruct::Type sPowerAdjustments[MAX_POWER_ADJUSTMENTS]; +static chip::app::Clusters::DeviceEnergyManagement::Structs::PowerAdjustCapabilityStruct::Type sPowerAdjustCapabilityStruct; +static chip::app::DataModel::Nullable + sPowerAdjustmentCapability; + +DeviceEnergyManagementDelegate * GetDEMDelegate() +{ + EVSEManufacturer * mn = GetEvseManufacturer(); + VerifyOrDieWithMsg(mn != nullptr, AppServer, "EVSEManufacturer is null"); + DeviceEnergyManagementDelegate * dg = mn->GetDEMDelegate(); + VerifyOrDieWithMsg(dg != nullptr, AppServer, "DEM Delegate is null"); + + return dg; +} + +CHIP_ERROR ConfigureForecast(uint16_t numSlots) +{ + uint32_t chipEpoch = 0; + + CHIP_ERROR err = GetEpochTS(chipEpoch); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Support, "ConfigureForecast could not get time"); + return err; + } + + // planned start time, in UTC, for the entire Forecast. Allow to be a little + // time in the future as forecastStruct.startTime is used in some tests. + sForecastStruct.startTime = chipEpoch + 60; + + // earliest start time, in UTC, that the entire Forecast can be shifted to. null value indicates that it can be started + // immediately. + sForecastStruct.earliestStartTime = MakeOptional(DataModel::MakeNullable(chipEpoch)); + + // planned end time, in UTC, for the entire Forecast. + sForecastStruct.endTime = chipEpoch * 3; + + // latest end time, in UTC, for the entire Forecast + sForecastStruct.latestEndTime = MakeOptional(chipEpoch * 3); + + sForecastStruct.isPausable = true; + + sForecastStruct.activeSlotNumber.SetNonNull(0); + + sSlots[0].minDuration = 10; + sSlots[0].maxDuration = 20; + sSlots[0].defaultDuration = 15; + sSlots[0].elapsedSlotTime = 0; + sSlots[0].remainingSlotTime = 0; + + sSlots[0].slotIsPausable.SetValue(true); + sSlots[0].minPauseDuration.SetValue(10); + sSlots[0].maxPauseDuration.SetValue(60); + + if (GetDEMDelegate()->HasFeature(DeviceEnergyManagement::Feature::kPowerForecastReporting)) + { + sSlots[0].nominalPower.SetValue(1500); + sSlots[0].minPower.SetValue(1000); + sSlots[0].maxPower.SetValue(2000); + } + + sSlots[0].nominalEnergy.SetValue(2000); + + if (GetDEMDelegate()->HasFeature(DeviceEnergyManagement::Feature::kStateForecastReporting)) + { + sSlots[0].manufacturerESAState.SetValue(23); + } + + for (uint16_t slotNo = 1; slotNo < numSlots; slotNo++) + { + sSlots[slotNo].minDuration = 2 * sSlots[slotNo - 1].minDuration; + sSlots[slotNo].maxDuration = 2 * sSlots[slotNo - 1].maxDuration; + sSlots[slotNo].defaultDuration = 2 * sSlots[slotNo - 1].defaultDuration; + sSlots[slotNo].elapsedSlotTime = 2 * sSlots[slotNo - 1].elapsedSlotTime; + sSlots[slotNo].remainingSlotTime = 2 * sSlots[slotNo - 1].remainingSlotTime; + + // Need slotNo == 1 not to be pausible for test DEM 2.4 step 3b + sSlots[slotNo].slotIsPausable.SetValue((slotNo & 1) == 0 ? true : false); + sSlots[slotNo].minPauseDuration.SetValue(2 * sSlots[slotNo - 1].slotIsPausable.Value()); + sSlots[slotNo].maxPauseDuration.SetValue(2 * sSlots[slotNo - 1].maxPauseDuration.Value()); + + if (GetDEMDelegate()->HasFeature(DeviceEnergyManagement::Feature::kPowerForecastReporting)) + { + sSlots[slotNo].nominalPower.SetValue(2 * sSlots[slotNo - 1].nominalPower.Value()); + sSlots[slotNo].minPower.SetValue(2 * sSlots[slotNo - 1].minPower.Value()); + sSlots[slotNo].maxPower.SetValue(2 * sSlots[slotNo - 1].maxPower.Value()); + + sSlots[slotNo].nominalEnergy.SetValue(2 * sSlots[slotNo - 1].nominalEnergy.Value()); + } + + if (GetDEMDelegate()->HasFeature(DeviceEnergyManagement::Feature::kStateForecastReporting)) + { + sSlots[slotNo].manufacturerESAState.SetValue(sSlots[slotNo - 1].manufacturerESAState.Value() + 1); + } + } + + sForecastStruct.slots = DataModel::List(sSlots, numSlots); + + EVSEManufacturer * mn = GetEvseManufacturer(); + mn->GetDEMDelegate()->SetForecast(DataModel::MakeNullable(sForecastStruct)); + mn->GetDEMDelegate()->SetAbsMinPower(1000); + mn->GetDEMDelegate()->SetAbsMaxPower(256 * 2000 * 1000); + + return CHIP_NO_ERROR; +} + +void SetTestEventTrigger_PowerAdjustment() +{ + sPowerAdjustments[0].minPower = 5000 * 1000; // 5kW + sPowerAdjustments[0].maxPower = 30000 * 1000; // 30kW + sPowerAdjustments[0].minDuration = 10; // 30s + sPowerAdjustments[0].maxDuration = 60; // 60s + + DataModel::List powerAdjustmentList(sPowerAdjustments, 1); + + sPowerAdjustCapabilityStruct.cause = PowerAdjustReasonEnum::kNoAdjustment; + sPowerAdjustCapabilityStruct.powerAdjustCapability.SetNonNull(powerAdjustmentList); + sPowerAdjustmentCapability.SetNonNull(sPowerAdjustCapabilityStruct); + + CHIP_ERROR err = GetDEMDelegate()->SetPowerAdjustmentCapability(sPowerAdjustmentCapability); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Support, "SetTestEventTrigger_PowerAdjustment failed %s", chip::ErrorStr(err)); + } +} + +void SetTestEventTrigger_ClearForecast() +{ + sPowerAdjustments[0].minPower = 0; + sPowerAdjustments[0].maxPower = 0; + sPowerAdjustments[0].minDuration = 0; + sPowerAdjustments[0].maxDuration = 0; + + DataModel::List powerAdjustmentList(sPowerAdjustments, 1); + + sPowerAdjustCapabilityStruct.powerAdjustCapability.SetNonNull(powerAdjustmentList); + sPowerAdjustCapabilityStruct.cause = PowerAdjustReasonEnum::kNoAdjustment; + + DataModel::Nullable powerAdjustmentCapabilityStruct( + sPowerAdjustCapabilityStruct); + + CHIP_ERROR err = GetDEMDelegate()->SetPowerAdjustmentCapability(powerAdjustmentCapabilityStruct); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Support, "SetTestEventTrigger_PowerAdjustment failed %s", chip::ErrorStr(err)); + } +} + +void SetTestEventTrigger_StartTimeAdjustment() +{ + ConfigureForecast(2); + + // Get the current forecast ad update the earliestStartTime and latestEndTime + sForecastStruct = GetDEMDelegate()->GetForecast().Value(); + + uint32_t chipEpoch = 0; + + CHIP_ERROR err = GetEpochTS(chipEpoch); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Support, "ConfigureForecast_EarliestStartLatestEndTimes could not get time"); + } + + // planned start time, in UTC, for the entire Forecast. + sForecastStruct.startTime = chipEpoch; + + // Set the earliest start time, in UTC, to that before the startTime + sForecastStruct.earliestStartTime = Optional>{ DataModel::Nullable{ chipEpoch - 60 } }; + + // Planned end time, in UTC, for the entire Forecast. + sForecastStruct.endTime = chipEpoch * 3; + + // Latest end time, in UTC, for the entire Forecast which is > sForecastStruct.endTime + sForecastStruct.latestEndTime = Optional(chipEpoch * 3 + 60); + + GetDEMDelegate()->SetForecast(DataModel::MakeNullable(sForecastStruct)); +} + +void SetTestEventTrigger_StartTimeAdjustmentClear() +{ + // Get the current forecast ad update the earliestStartTime and latestEndTime + sForecastStruct = GetDEMDelegate()->GetForecast().Value(); + + sForecastStruct.startTime = static_cast(0); + sForecastStruct.endTime = static_cast(0); + + sForecastStruct.earliestStartTime = NullOptional; + sForecastStruct.latestEndTime = NullOptional; + + GetDEMDelegate()->SetForecast(DataModel::MakeNullable(sForecastStruct)); +} + +void SetTestEventTrigger_UserOptOutOptimization(OptOutStateEnum optOutState) +{ + GetDEMDelegate()->SetOptOutState(optOutState); +} + +void SetTestEventTrigger_Pausable() +{ + ConfigureForecast(2); +} + +void SetTestEventTrigger_PausableNextSlot() +{ + // Get the current forecast ad update the active slot number + sForecastStruct = GetDEMDelegate()->GetForecast().Value(); + sForecastStruct.activeSlotNumber.SetNonNull(1); + + GetDEMDelegate()->SetForecast(DataModel::MakeNullable(sForecastStruct)); +} + +void SetTestEventTrigger_Forecast() +{ + ConfigureForecast(2); +} + +void SetTestEventTrigger_ForecastClear() +{ + sForecastStruct.startTime = 0; + sForecastStruct.endTime = 0; + sForecastStruct.earliestStartTime.ClearValue(); + sForecastStruct.latestEndTime.ClearValue(); + sForecastStruct.isPausable = false; + sForecastStruct.activeSlotNumber.SetNull(); + sForecastStruct.slots = DataModel::List(); + + GetDEMDelegate()->SetForecast(DataModel::MakeNullable(sForecastStruct)); +} + +void SetTestEventTrigger_ForecastAdjustment() +{ + ConfigureForecast(2); + + // The following values need to match the equivalent values in src/python_testing/TC_DEM_2_5.py + sForecastStruct = GetDEMDelegate()->GetForecast().Value(); + sSlots[0].minPowerAdjustment.SetValue(20); + sSlots[0].maxPowerAdjustment.SetValue(2000); + sSlots[0].minDurationAdjustment.SetValue(120); + sSlots[0].maxDurationAdjustment.SetValue(240); + + sForecastStruct.slots = DataModel::List(sSlots, 2); + + GetDEMDelegate()->SetForecast(DataModel::MakeNullable(sForecastStruct)); +} + +void SetTestEventTrigger_ForecastAdjustmentNextSlot() +{ + sForecastStruct = GetDEMDelegate()->GetForecast().Value(); + sForecastStruct.activeSlotNumber.SetNonNull(sForecastStruct.activeSlotNumber.Value() + 1); + + GetDEMDelegate()->SetForecast(DataModel::MakeNullable(sForecastStruct)); +} + +void SetTestEventTrigger_ConstraintBasedAdjustment() +{ + ConfigureForecast(4); +} + +bool HandleDeviceEnergyManagementTestEventTrigger(uint64_t eventTrigger) +{ + DeviceEnergyManagementTrigger trigger = static_cast(eventTrigger); + + switch (trigger) + { + case DeviceEnergyManagementTrigger::kPowerAdjustment: + ChipLogProgress( + Support, + "[PowerAdjustment-Test-Event] => Simulate a fixed forecast power usage including one or more PowerAdjustmentStructs"); + SetTestEventTrigger_PowerAdjustment(); + break; + case DeviceEnergyManagementTrigger::kPowerAdjustmentClear: + ChipLogProgress(Support, "[PowerAdjustmentClear-Test-Event] => Clear the PowerAdjustment structs"); + SetTestEventTrigger_ClearForecast(); + break; + case DeviceEnergyManagementTrigger::kUserOptOutLocalOptimization: + ChipLogProgress(Support, "[UserOptOutLocalOptimization-Test-Event] => Simulate user opt-out of Local Optimization"); + SetTestEventTrigger_UserOptOutOptimization(OptOutStateEnum::kLocalOptOut); + break; + case DeviceEnergyManagementTrigger::kUserOptOutGridOptimization: + ChipLogProgress(Support, "[UserOptOutGrisOptimization-Test-Event] => Simulate user opt-out of Grid Optimization"); + SetTestEventTrigger_UserOptOutOptimization(OptOutStateEnum::kGridOptOut); + break; + case DeviceEnergyManagementTrigger::kUserOptOutClearAll: + ChipLogProgress(Support, "[UserOptOutClearAll-Test-Event] => Remove all user opt-out opting out"); + SetTestEventTrigger_UserOptOutOptimization(OptOutStateEnum::kNoOptOut); + break; + case DeviceEnergyManagementTrigger::kStartTimeAdjustment: + ChipLogProgress(Support, + "[StartTimeAdjustment-Test-Event] => Simulate a fixed forecast with EarliestStartTime earlier than " + "startTime, and LatestEndTime greater than EndTime"); + SetTestEventTrigger_StartTimeAdjustment(); + break; + case DeviceEnergyManagementTrigger::kStartTimeAdjustmentClear: + ChipLogProgress(Support, "[StartTimeAdjustmentClear-Test-Event] => Clear the StartTimeAdjustment simulated forecast"); + SetTestEventTrigger_StartTimeAdjustmentClear(); + break; + case DeviceEnergyManagementTrigger::kPausable: + ChipLogProgress(Support, + "[Pausable-Test-Event] => Simulate a fixed forecast with one pausable slot with MinPauseDuration >1, " + "MaxPauseDuration>1 and one non pausable slot"); + SetTestEventTrigger_Pausable(); + break; + case DeviceEnergyManagementTrigger::kPausableNextSlot: + ChipLogProgress(Support, "[PausableNextSlot-Test-Event] => Simulate a moving time to the next forecast slot"); + SetTestEventTrigger_PausableNextSlot(); + break; + case DeviceEnergyManagementTrigger::kPausableClear: + ChipLogProgress(Support, "[PausableClear-Test-Event] => Clear the Pausable simulated forecast"); + SetTestEventTrigger_ClearForecast(); + break; + case DeviceEnergyManagementTrigger::kForecastAdjustment: + ChipLogProgress(Support, + "[ForecastAdjustment-Test-Event] => Simulate a forecast power usage with at least 2 and at most 4 slots"); + SetTestEventTrigger_ForecastAdjustment(); + break; + case DeviceEnergyManagementTrigger::kForecastAdjustmentNextSlot: + ChipLogProgress(Support, "[ForecastAdjustmentNextSlot-Test-Event] => Simulate moving time to the next forecast slot"); + SetTestEventTrigger_ForecastAdjustmentNextSlot(); + break; + case DeviceEnergyManagementTrigger::kForecastAdjustmentClear: + ChipLogProgress(Support, "[ForecastAdjustmentClear-Test-Event] => Clear the forecast adjustment"); + SetTestEventTrigger_ClearForecast(); + break; + case DeviceEnergyManagementTrigger::kConstraintBasedAdjustment: + ChipLogProgress( + Support, + "[ConstraintBasedAdjustment-Test-Event] => Simulate a forecast power usage with at least 2 and at most 4 slots"); + SetTestEventTrigger_ConstraintBasedAdjustment(); + break; + case DeviceEnergyManagementTrigger::kConstraintBasedAdjustmentClear: + ChipLogProgress(Support, "[ConstraintBasedAdjustmentClear-Test-Event] => Clear the constraint based adjustment"); + SetTestEventTrigger_ClearForecast(); + break; + case DeviceEnergyManagementTrigger::kForecast: + ChipLogProgress(Support, "[Forecast-Test-Event] => Create a forecast with at least 1 slot"); + SetTestEventTrigger_Forecast(); + break; + case DeviceEnergyManagementTrigger::kForecastClear: + ChipLogProgress(Support, "[ForecastClear-Test-Event] => Clear the forecast"); + SetTestEventTrigger_ForecastClear(); + break; + + default: + return false; + } + + return true; +} diff --git a/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp b/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp index 8f122bdac2b046..273bc9e0614333 100644 --- a/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp +++ b/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,8 +17,10 @@ */ #include "DeviceEnergyManagementDelegateImpl.h" - +#include "DEMManufacturerDelegate.h" +#include "EnergyTimeUtils.h" #include +#include using namespace chip; using namespace chip::app; @@ -26,10 +28,40 @@ using namespace chip::app::Clusters; using namespace chip::app::Clusters::DeviceEnergyManagement; using namespace chip::app::Clusters::DeviceEnergyManagement::Attributes; +using chip::Protocols::InteractionModel::Status; + using chip::Optional; -using namespace chip::app; using CostsList = DataModel::List; +DeviceEnergyManagementDelegate::DeviceEnergyManagementDelegate() : + mpDEMManufacturerDelegate(nullptr), mEsaType(ESATypeEnum::kEvse), mEsaCanGenerate(false), mEsaState(ESAStateEnum::kOffline), + mAbsMinPowerMw(0), mAbsMaxPowerMw(0), mOptOutState(OptOutStateEnum::kNoOptOut), mPowerAdjustmentInProgress(false), + mPowerAdjustmentStartTimeUtc(0), mPauseRequestInProgress(false) +{} + +void DeviceEnergyManagementDelegate::SetDeviceEnergyManagementInstance(DeviceEnergyManagement::Instance & instance) +{ + mpDEMInstance = &instance; +} + +uint32_t DeviceEnergyManagementDelegate::HasFeature(Feature feature) const +{ + bool hasFeature = false; + + if (mpDEMInstance != nullptr) + { + hasFeature = mpDEMInstance->HasFeature(feature); + } + + return hasFeature; +} + +void DeviceEnergyManagementDelegate::SetDEMManufacturerDelegate( + DEMManufacturerDelegate & deviceEnergyManagementManufacturerDelegate) +{ + mpDEMManufacturerDelegate = &deviceEnergyManagementManufacturerDelegate; +} + /** * @brief Delegate handler for PowerAdjustRequest * @@ -47,27 +79,156 @@ using CostsList = DataModel::List; * 6) generate a PowerAdjustEnd event with cause NormalCompletion * 7) if necessary, update the forecast with new expected end time */ -Status DeviceEnergyManagementDelegate::PowerAdjustRequest(const int64_t power, const uint32_t duration, AdjustmentCauseEnum cause) +Status DeviceEnergyManagementDelegate::PowerAdjustRequest(const int64_t powerMw, const uint32_t durationS, + AdjustmentCauseEnum cause) { - Status status = Status::UnsupportedCommand; // Status::Success; + bool generateEvent = false; - // TODO: implement - mEsaState = ESAStateEnum::kPowerAdjustActive; + // If a timer is running, cancel it so we can start it with the new duration + if (mPowerAdjustmentInProgress) + { + DeviceLayer::SystemLayer().CancelTimer(PowerAdjustTimerExpiry, this); + } + else + { + // Going to start a new power adjustment so will need to generate an event + generateEvent = true; + + // Record when this PowerAdjustment starts. Note if we do not set this value if a PowerAdjustment is in progress + CHIP_ERROR err = GetEpochTS(mPowerAdjustmentStartTimeUtc); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Unable to get time: %" CHIP_ERROR_FORMAT, err.Format()); + return Status::Failure; + } + } - // TODO: Generate a PowerAdjustStart Event, then begins to adjust its power - // When done, raise PowerAdjustEnd & ESAState set to kOnline. + // Update the forecast with the new expected end time + if (mpDEMManufacturerDelegate != nullptr) + { + CHIP_ERROR err = mpDEMManufacturerDelegate->HandleDeviceEnergyManagementPowerAdjustRequest(powerMw, durationS, cause); + if (err != CHIP_NO_ERROR) + { + return Status::Failure; + } + } - MatterReportingAttributeChangeCallback(mEndpointId, DeviceEnergyManagement::Id, ESAState::Id); + SetESAState(ESAStateEnum::kPowerAdjustActive); - return status; + // mPowerAdjustCapabilityStruct is guaranteed to have a value as validated in Instance::HandlePowerAdjustRequest. + // If it did not have a value, this method would not have been called. + switch (cause) + { + case AdjustmentCauseEnum::kLocalOptimization: + mPowerAdjustCapabilityStruct.Value().cause = PowerAdjustReasonEnum::kLocalOptimizationAdjustment; + break; + + case AdjustmentCauseEnum::kGridOptimization: + mPowerAdjustCapabilityStruct.Value().cause = PowerAdjustReasonEnum::kGridOptimizationAdjustment; + break; + + default: + HandlePowerAdjustRequestFailure(); + return Status::Failure; + } + + // Remember we have a timer running so we don't generate a PowerAdjustStart event should another request come + // in before this timer expires + mPowerAdjustmentInProgress = true; + + CHIP_ERROR err = DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds32(durationS), PowerAdjustTimerExpiry, this); + if (err != CHIP_NO_ERROR) + { + // TODO: Note: should the PowerAdjust just initiated be cancelled because an Event could not be logged? + ChipLogError(AppServer, "Unable to start a PowerAdjustStart timer: %" CHIP_ERROR_FORMAT, err.Format()); + HandlePowerAdjustRequestFailure(); + return Status::Failure; + } + + if (generateEvent) + { + Events::PowerAdjustStart::Type event; + EventNumber eventNumber; + err = LogEvent(event, mEndpointId, eventNumber); + if (CHIP_NO_ERROR != err) + { + // TODO: Note: should the PowerAdjust just initiated be cancelled because an Event could not be logged? + ChipLogError(AppServer, "Unable to generate PowerAdjustStart event: %" CHIP_ERROR_FORMAT, err.Format()); + HandlePowerAdjustRequestFailure(); + return Status::Failure; + } + } + + return Status::Success; +} + +/** + * @brief Handle a PowerAdjustRequest failing + * + * Cleans up the PowerAdjust state should the request fail + */ +void DeviceEnergyManagementDelegate::HandlePowerAdjustRequestFailure() +{ + DeviceLayer::SystemLayer().CancelTimer(PowerAdjustTimerExpiry, this); + + SetESAState(ESAStateEnum::kOnline); + + mPowerAdjustmentInProgress = false; + + mPowerAdjustCapabilityStruct.Value().cause = PowerAdjustReasonEnum::kNoAdjustment; + + // TODO + // Should we inform the mpDEMManufacturerDelegate that PowerAdjustRequest has failed? } + +/** + * @brief Timer for handling the PowerAdjustRequest + * + * This static function calls the non-static HandlePowerAdjustTimerExpiry method. + */ +void DeviceEnergyManagementDelegate::PowerAdjustTimerExpiry(System::Layer * systemLayer, void * delegate) +{ + DeviceEnergyManagementDelegate * dg = reinterpret_cast(delegate); + + dg->HandlePowerAdjustTimerExpiry(); +} + +/** + * @brief Timer for handling the completion of a PowerAdjustRequest + * + * When the timer expires: + * 1) notify the appliance's that it can resume its intended power setting (or go idle) + * 2) generate a PowerAdjustEnd event with cause NormalCompletion + * 3) if necessary, update the forecast with new expected end time + */ +void DeviceEnergyManagementDelegate::HandlePowerAdjustTimerExpiry() +{ + ChipLogError(AppServer, "DeviceEnergyManagementDelegate::HandlePowerAdjustTimerExpiry"); + + // The PowerAdjustment is no longer in progress + mPowerAdjustmentInProgress = false; + + SetESAState(ESAStateEnum::kOnline); + + mPowerAdjustCapabilityStruct.Value().cause = PowerAdjustReasonEnum::kNoAdjustment; + + // Generate a PowerAdjustEnd event + GeneratePowerAdjustEndEvent(CauseEnum::kNormalCompletion); + + // Update the forecast with new expected end time + if (mpDEMManufacturerDelegate != nullptr) + { + mpDEMManufacturerDelegate->HandleDeviceEnergyManagementPowerAdjustCompletion(); + } +} + /** * @brief Delegate handler for CancelPowerAdjustRequest * * Note: checking of the validity of the CancelPowerAdjustRequest has been done by the lower layer * * This function needs to notify the appliance that it should resume its intended power setting (or go idle). - + * * It should: * 1) notify the appliance's that it can resume its intended power setting (or go idle) * 2) generate a PowerAdjustEnd event with cause code Cancelled @@ -75,16 +236,91 @@ Status DeviceEnergyManagementDelegate::PowerAdjustRequest(const int64_t power, c */ Status DeviceEnergyManagementDelegate::CancelPowerAdjustRequest() { - Status status = Status::UnsupportedCommand; // Status::Success; + Status status = Status::Success; - // TODO: implement - /* TODO: If the command is accepted, the ESA SHALL generate an PowerAdjustEnd Event. */ - mEsaState = ESAStateEnum::kOnline; - MatterReportingAttributeChangeCallback(mEndpointId, DeviceEnergyManagement::Id, ESAState::Id); + CHIP_ERROR err = CancelPowerAdjustRequestAndGenerateEvent(DeviceEnergyManagement::CauseEnum::kCancelled); + if (CHIP_NO_ERROR != err) + { + status = Status::Failure; + } return status; } +/** + * @brief Handles the cancelation of a PowerAdjust operation + * + * This function needs to notify the appliance that it should resume its intended power setting (or go idle). + * + * It should: + * 1) notify the appliance's that it can resume its intended power setting (or go idle) + * 2) generate a PowerAdjustEnd event with cause code Cancelled + * 3) if necessary, update the forecast with new expected end time + */ +CHIP_ERROR DeviceEnergyManagementDelegate::CancelPowerAdjustRequestAndGenerateEvent(CauseEnum cause) +{ + DeviceLayer::SystemLayer().CancelTimer(PowerAdjustTimerExpiry, this); + + SetESAState(ESAStateEnum::kOnline); + + mPowerAdjustmentInProgress = false; + + mPowerAdjustCapabilityStruct.Value().cause = PowerAdjustReasonEnum::kNoAdjustment; + + CHIP_ERROR err = GeneratePowerAdjustEndEvent(cause); + + // Notify the appliance's that it can resume its intended power setting (or go idle) + if (mpDEMManufacturerDelegate != nullptr) + { + // It is expected the mpDEMManufacturerDelegate will update the forecast with new expected end time + // as a consequence of the cancel request. + err = mpDEMManufacturerDelegate->HandleDeviceEnergyManagementCancelPowerAdjustRequest(cause); + } + + return err; +} + +/** + * @brief Generate a PowerAdjustEvent + * + */ +CHIP_ERROR DeviceEnergyManagementDelegate::GeneratePowerAdjustEndEvent(CauseEnum cause) +{ + Events::PowerAdjustEnd::Type event; + EventNumber eventNumber; + event.cause = cause; + + uint32_t timeNowUtc; + CHIP_ERROR err = GetEpochTS(timeNowUtc); + if (err == CHIP_NO_ERROR) + { + event.duration = timeNowUtc - mPowerAdjustmentStartTimeUtc; + } + else + { + ChipLogError(AppServer, "Unable to get time: %" CHIP_ERROR_FORMAT, err.Format()); + return err; + } + + if (mpDEMManufacturerDelegate != nullptr) + { + event.energyUse = mpDEMManufacturerDelegate->GetApproxEnergyDuringSession(); + } + else + { + event.energyUse = 0; + } + + err = LogEvent(event, mEndpointId, eventNumber); + if (CHIP_NO_ERROR != err) + { + ChipLogError(AppServer, "Unable to generate PowerAdjustEnd event: %" CHIP_ERROR_FORMAT, err.Format()); + return err; + } + + return err; +} + /** * @brief Delegate handler for StartTimeAdjustRequest * @@ -96,27 +332,58 @@ Status DeviceEnergyManagementDelegate::CancelPowerAdjustRequest() * 1) update the forecast attribute with the revised start time * 2) send a callback notification to the appliance so it can refresh its internal schedule */ -Status DeviceEnergyManagementDelegate::StartTimeAdjustRequest(const uint32_t requestedStartTime, AdjustmentCauseEnum cause) +Status DeviceEnergyManagementDelegate::StartTimeAdjustRequest(const uint32_t requestedStartTimeUtc, AdjustmentCauseEnum cause) { - DataModel::Nullable forecast = GetForecast(); + if (mForecast.IsNull()) + { + return Status::Failure; + } - if (forecast.IsNull()) + switch (cause) { + case AdjustmentCauseEnum::kLocalOptimization: + mForecast.Value().forecastUpdateReason = ForecastUpdateReasonEnum::kLocalOptimization; + break; + case AdjustmentCauseEnum::kGridOptimization: + mForecast.Value().forecastUpdateReason = ForecastUpdateReasonEnum::kGridOptimization; + break; + default: + ChipLogDetail(AppServer, "Bad cause %d", to_underlying(cause)); return Status::Failure; + break; } - uint32_t duration = forecast.Value().endTime - forecast.Value().startTime; // the current entire forecast duration + mForecast.Value().forecastID++; - /* Modify start time and end time */ - forecast.Value().startTime = requestedStartTime; - forecast.Value().endTime = requestedStartTime + duration; + uint32_t durationS = mForecast.Value().endTime - mForecast.Value().startTime; // the current entire forecast duration + + // Save the start and end time in case there is an issue with the mpDEMManufacturerDelegate handling this + // startTimeAdjustment request + uint32_t savedStartTime = mForecast.Value().startTime; + uint32_t savedEndTime = mForecast.Value().endTime; - SetForecast(forecast); // This will increment forecast ID + /* Modify start time and end time */ + mForecast.Value().startTime = requestedStartTimeUtc; + mForecast.Value().endTime = requestedStartTimeUtc + durationS; - // TODO: callback to the appliance to notify it of a new start time + if (mpDEMManufacturerDelegate != nullptr) + { + CHIP_ERROR err = + mpDEMManufacturerDelegate->HandleDeviceEnergyManagementStartTimeAdjustRequest(requestedStartTimeUtc, cause); + if (err != CHIP_NO_ERROR) + { + // Reset state + mForecast.Value().forecastUpdateReason = ForecastUpdateReasonEnum::kInternalOptimization; + mForecast.Value().startTime = savedStartTime; + mForecast.Value().endTime = savedEndTime; + + return Status::Failure; + } + } return Status::Success; } + /** * @brief Delegate handler for Pause Request * @@ -134,11 +401,184 @@ Status DeviceEnergyManagementDelegate::StartTimeAdjustRequest(const uint32_t req * 6) generate a Resumed event * 7) if necessary, update the forecast with new expected end time */ -Status DeviceEnergyManagementDelegate::PauseRequest(const uint32_t duration, AdjustmentCauseEnum cause) +Status DeviceEnergyManagementDelegate::PauseRequest(const uint32_t durationS, AdjustmentCauseEnum cause) { - Status status = Status::UnsupportedCommand; // Status::Success; - // TODO: implement the behaviour above - return status; + bool generateEvent = false; + + // If a timer is running, cancel it so we can start it with the new duration + if (mPauseRequestInProgress) + { + DeviceLayer::SystemLayer().CancelTimer(PauseRequestTimerExpiry, this); + } + else + { + generateEvent = true; + + // Remember we have a timer running so we don't generate a Paused event should another request come + // in before this timer expires + mPauseRequestInProgress = true; + } + + CHIP_ERROR err = DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds32(durationS), PauseRequestTimerExpiry, this); + if (err != CHIP_NO_ERROR) + { + HandlePauseRequestFailure(); + return Status::Failure; + } + + // Pause the appliance + if (mpDEMManufacturerDelegate != nullptr) + { + // It is expected that the mpDEMManufacturerDelegate will update the forecast with the new expected end time + err = mpDEMManufacturerDelegate->HandleDeviceEnergyManagementPauseRequest(durationS, cause); + if (err != CHIP_NO_ERROR) + { + HandlePauseRequestFailure(); + return Status::Failure; + } + } + + if (generateEvent) + { + Events::Paused::Type event; + EventNumber eventNumber; + err = LogEvent(event, mEndpointId, eventNumber); + if (CHIP_NO_ERROR != err) + { + ChipLogError(AppServer, "Unable to generate Paused event: %" CHIP_ERROR_FORMAT, err.Format()); + HandlePauseRequestFailure(); + return Status::Failure; + } + } + + SetESAState(ESAStateEnum::kPaused); + + // Update the forecaseUpdateReason based on the AdjustmentCause + if (cause == AdjustmentCauseEnum::kLocalOptimization) + { + mForecast.Value().forecastUpdateReason = ForecastUpdateReasonEnum::kLocalOptimization; + } + else if (cause == AdjustmentCauseEnum::kGridOptimization) + { + mForecast.Value().forecastUpdateReason = ForecastUpdateReasonEnum::kGridOptimization; + } + + return Status::Success; +} + +/** + * @brief Handle a PauseRequest failing + * + * Cleans up the state should the PauseRequest fail + */ +void DeviceEnergyManagementDelegate::HandlePauseRequestFailure() +{ + DeviceLayer::SystemLayer().CancelTimer(PowerAdjustTimerExpiry, this); + + SetESAState(ESAStateEnum::kOnline); + + mPauseRequestInProgress = false; + + // TODO + // Should we inform the mpDEMManufacturerDelegate that PauseRequest has failed? +} + +/** + * @brief Timer for handling the PauseRequest + * + * This static function calls the non-static HandlePauseRequestTimerExpiry method. + */ +void DeviceEnergyManagementDelegate::PauseRequestTimerExpiry(System::Layer * systemLayer, void * delegate) +{ + DeviceEnergyManagementDelegate * dg = reinterpret_cast(delegate); + + dg->HandlePauseRequestTimerExpiry(); +} + +/** + * @brief Timer for handling the completion of a PauseRequest + * + * When the timer expires: + * 1) restore the appliance's operational state + * 2) generate a Resumed event + * 3) if necessary, update the forecast with new expected end time + */ +void DeviceEnergyManagementDelegate::HandlePauseRequestTimerExpiry() +{ + // The PauseRequestment is no longer in progress + mPauseRequestInProgress = false; + + SetESAState(ESAStateEnum::kOnline); + + // Generate a Resumed event + GenerateResumedEvent(CauseEnum::kNormalCompletion); + + // It is expected the mpDEMManufacturerDelegate will update the forecast with new expected end time + if (mpDEMManufacturerDelegate != nullptr) + { + mpDEMManufacturerDelegate->HandleDeviceEnergyManagementPauseCompletion(); + } +} + +/** + * @brief Handles the cancelation of a pause operation + * + * This function needs to notify the appliance that it should resume its intended power setting (or go idle). + * + * It should: + * 1) notify the appliance's that it can resume its intended power setting (or go idle) + * 2) generate a PowerAdjustEnd event with cause code Cancelled + * 3) if necessary, update the forecast with new expected end time + */ +CHIP_ERROR DeviceEnergyManagementDelegate::CancelPauseRequestAndGenerateEvent(CauseEnum cause) +{ + mPauseRequestInProgress = false; + + SetESAState(ESAStateEnum::kOnline); + + DeviceLayer::SystemLayer().CancelTimer(PauseRequestTimerExpiry, this); + + CHIP_ERROR err = GenerateResumedEvent(cause); + CHIP_ERROR err2 = CHIP_NO_ERROR; + + // Notify the appliance's that it can resume its intended power setting (or go idle) + if (mpDEMManufacturerDelegate != nullptr) + { + // It is expected that the mpDEMManufacturerDelegate will update the forecast with new expected end time + err2 = mpDEMManufacturerDelegate->HandleDeviceEnergyManagementCancelPauseRequest(cause); + } + + // Need to pick one of the error codes two return... + if (err == CHIP_NO_ERROR && err2 == CHIP_NO_ERROR) + { + return CHIP_NO_ERROR; + } + + if (err2 != CHIP_NO_ERROR) + { + return err2; + } + + return err; +} + +/** + * @brief Generate a Resumed event + * + */ +CHIP_ERROR DeviceEnergyManagementDelegate::GenerateResumedEvent(CauseEnum cause) +{ + Events::Resumed::Type event; + EventNumber eventNumber; + event.cause = cause; + + CHIP_ERROR err = LogEvent(event, mEndpointId, eventNumber); + if (CHIP_NO_ERROR != err) + { + ChipLogError(AppServer, "Unable to generate Resumed event: %" CHIP_ERROR_FORMAT, err.Format()); + } + + return err; } /** @@ -156,10 +596,24 @@ Status DeviceEnergyManagementDelegate::PauseRequest(const uint32_t duration, Adj */ Status DeviceEnergyManagementDelegate::ResumeRequest() { - Status status = Status::UnsupportedCommand; // Status::Success; + Status status = Status::Failure; - // TODO: implement the behaviour above - SetESAState(ESAStateEnum::kOnline); + if (mPauseRequestInProgress) + { + // Guard against mForecast being null + if (!mForecast.IsNull()) + { + // The PauseRequest has effectively been cancelled so as a result the device should + // go back to InternalOptimisation + mForecast.Value().forecastUpdateReason = ForecastUpdateReasonEnum::kInternalOptimization; + } + + CHIP_ERROR err = CancelPauseRequestAndGenerateEvent(CauseEnum::kCancelled); + if (err == CHIP_NO_ERROR) + { + status = Status::Success; + } + } return status; } @@ -179,12 +633,47 @@ Status DeviceEnergyManagementDelegate::ResumeRequest() * 3) notify the appliance to follow the revised schedule */ Status DeviceEnergyManagementDelegate::ModifyForecastRequest( - const uint32_t forecastId, const DataModel::DecodableList & slotAdjustments, + const uint32_t forecastID, const DataModel::DecodableList & slotAdjustments, AdjustmentCauseEnum cause) { - Status status = Status::UnsupportedCommand; // Status::Success; + Status status = Status::Success; + + if (mForecast.IsNull()) + { + status = Status::Failure; + } + else if (mForecast.Value().forecastID != forecastID) + { + status = Status::Failure; + } + else if (mpDEMManufacturerDelegate != nullptr) + { + // Determine if the new forecast adjustments are acceptable to the appliance + CHIP_ERROR err = mpDEMManufacturerDelegate->HandleModifyForecastRequest(forecastID, slotAdjustments, cause); + if (err != CHIP_NO_ERROR) + { + status = Status::Failure; + } + } + + if (status == Status::Success) + { + switch (cause) + { + case AdjustmentCauseEnum::kLocalOptimization: + mForecast.Value().forecastUpdateReason = ForecastUpdateReasonEnum::kLocalOptimization; + break; + case AdjustmentCauseEnum::kGridOptimization: + mForecast.Value().forecastUpdateReason = ForecastUpdateReasonEnum::kGridOptimization; + break; + default: + // Already checked in chip::app::Clusters::DeviceEnergyManagement::Instance::HandleModifyForecastRequest + break; + } + + mForecast.Value().forecastID++; + } - // TODO: implement the behaviour above return status; } @@ -203,8 +692,42 @@ Status DeviceEnergyManagementDelegate::ModifyForecastRequest( Status DeviceEnergyManagementDelegate::RequestConstraintBasedForecast( const DataModel::DecodableList & constraints, AdjustmentCauseEnum cause) { - Status status = Status::UnsupportedCommand; // Status::Success; - // TODO: implement the behaviour above + Status status = Status::Success; + + if (mForecast.IsNull()) + { + status = Status::Failure; + } + else if (mpDEMManufacturerDelegate != nullptr) + { + // Determine if the new forecast adjustments are acceptable to the appliance + CHIP_ERROR err = mpDEMManufacturerDelegate->RequestConstraintBasedForecast(constraints, cause); + if (err != CHIP_NO_ERROR) + { + status = Status::Failure; + } + } + + if (status == Status::Success) + { + switch (cause) + { + case AdjustmentCauseEnum::kLocalOptimization: + mForecast.Value().forecastUpdateReason = ForecastUpdateReasonEnum::kLocalOptimization; + break; + case AdjustmentCauseEnum::kGridOptimization: + mForecast.Value().forecastUpdateReason = ForecastUpdateReasonEnum::kGridOptimization; + break; + default: + // Already checked in chip::app::Clusters::DeviceEnergyManagement::Instance::HandleModifyForecastRequest + break; + } + + mForecast.Value().forecastID++; + + status = Status::Success; + } + return status; } @@ -221,8 +744,23 @@ Status DeviceEnergyManagementDelegate::RequestConstraintBasedForecast( */ Status DeviceEnergyManagementDelegate::CancelRequest() { - Status status = Status::UnsupportedCommand; // Status::Success; - // TODO: implement the behaviour above + Status status = Status::Success; + + mForecast.Value().forecastUpdateReason = ForecastUpdateReasonEnum::kInternalOptimization; + + /* It is expected the mpDEMManufacturerDelegate will cancel the effects of any previous adjustment + * request commands, and re-evaluate its forecast for intended operation ignoring those previous + * requests. + */ + if (mpDEMManufacturerDelegate != nullptr) + { + CHIP_ERROR error = mpDEMManufacturerDelegate->HandleDeviceEnergyManagementCancelRequest(); + if (error != CHIP_NO_ERROR) + { + status = Status::Failure; + } + } + return status; } @@ -245,26 +783,30 @@ ESAStateEnum DeviceEnergyManagementDelegate::GetESAState() int64_t DeviceEnergyManagementDelegate::GetAbsMinPower() { - return mAbsMinPower; + return mAbsMinPowerMw; } int64_t DeviceEnergyManagementDelegate::GetAbsMaxPower() { - return mAbsMaxPower; + return mAbsMaxPowerMw; } -PowerAdjustmentCapability::TypeInfo::Type DeviceEnergyManagementDelegate::GetPowerAdjustmentCapability() +const DataModel::Nullable & +DeviceEnergyManagementDelegate::GetPowerAdjustmentCapability() { - return mPowerAdjustmentCapability; + return mPowerAdjustCapabilityStruct; } -DataModel::Nullable DeviceEnergyManagementDelegate::GetForecast() +const DataModel::Nullable & DeviceEnergyManagementDelegate::GetForecast() { + ChipLogDetail(Zcl, "DeviceEnergyManagementDelegate::GetForecast"); + return mForecast; } OptOutStateEnum DeviceEnergyManagementDelegate::GetOptOutState() { + ChipLogDetail(AppServer, "mOptOutState %d", to_underlying(mOptOutState)); return mOptOutState; } @@ -323,28 +865,28 @@ CHIP_ERROR DeviceEnergyManagementDelegate::SetESAState(ESAStateEnum newValue) return CHIP_NO_ERROR; } -CHIP_ERROR DeviceEnergyManagementDelegate::SetAbsMinPower(int64_t newValue) +CHIP_ERROR DeviceEnergyManagementDelegate::SetAbsMinPower(int64_t newValueMw) { - int64_t oldValue = mAbsMinPower; + int64_t oldValueMw = mAbsMinPowerMw; - mAbsMinPower = newValue; - if (oldValue != newValue) + mAbsMinPowerMw = newValueMw; + if (oldValueMw != newValueMw) { - ChipLogDetail(AppServer, "mAbsMinPower updated to %d", static_cast(mAbsMinPower)); + ChipLogDetail(AppServer, "mAbsMinPower updated to " ChipLogFormatX64, ChipLogValueX64(mAbsMinPowerMw)); MatterReportingAttributeChangeCallback(mEndpointId, DeviceEnergyManagement::Id, AbsMinPower::Id); } return CHIP_NO_ERROR; } -CHIP_ERROR DeviceEnergyManagementDelegate::SetAbsMaxPower(int64_t newValue) +CHIP_ERROR DeviceEnergyManagementDelegate::SetAbsMaxPower(int64_t newValueMw) { - int64_t oldValue = mAbsMaxPower; + int64_t oldValueMw = mAbsMaxPowerMw; - mAbsMaxPower = newValue; - if (oldValue != newValue) + mAbsMaxPowerMw = newValueMw; + if (oldValueMw != newValueMw) { - ChipLogDetail(AppServer, "mAbsMaxPower updated to %d", static_cast(mAbsMaxPower)); + ChipLogDetail(AppServer, "mAbsMaxPower updated to " ChipLogFormatX64, ChipLogValueX64(mAbsMaxPowerMw)); MatterReportingAttributeChangeCallback(mEndpointId, DeviceEnergyManagement::Id, AbsMaxPower::Id); } @@ -352,20 +894,111 @@ CHIP_ERROR DeviceEnergyManagementDelegate::SetAbsMaxPower(int64_t newValue) } CHIP_ERROR -DeviceEnergyManagementDelegate::SetPowerAdjustmentCapability(PowerAdjustmentCapability::TypeInfo::Type powerAdjustmentCapability) +DeviceEnergyManagementDelegate::SetPowerAdjustmentCapability( + const DataModel::Nullable & powerAdjustCapabilityStruct) { - // TODO see Issue #31147 + assertChipStackLockedByCurrentThread(); + + mPowerAdjustCapabilityStruct = powerAdjustCapabilityStruct; + + MatterReportingAttributeChangeCallback(mEndpointId, DeviceEnergyManagement::Id, PowerAdjustmentCapability::Id); + return CHIP_NO_ERROR; } -CHIP_ERROR DeviceEnergyManagementDelegate::SetForecast(DataModel::Nullable forecast) +CHIP_ERROR DeviceEnergyManagementDelegate::SetForecast(const DataModel::Nullable & forecast) { + assertChipStackLockedByCurrentThread(); + // TODO see Issue #31147 + mForecast = forecast; + + MatterReportingAttributeChangeCallback(mEndpointId, DeviceEnergyManagement::Id, Forecast::Id); return CHIP_NO_ERROR; } CHIP_ERROR DeviceEnergyManagementDelegate::SetOptOutState(OptOutStateEnum newValue) { - return CHIP_NO_ERROR; + CHIP_ERROR err = CHIP_NO_ERROR; + + OptOutStateEnum oldValue = mOptOutState; + + // The OptOutState is cumulative + if ((oldValue == OptOutStateEnum::kGridOptOut && newValue == OptOutStateEnum::kLocalOptOut) || + (oldValue == OptOutStateEnum::kLocalOptOut && newValue == OptOutStateEnum::kGridOptOut)) + { + mOptOutState = OptOutStateEnum::kOptOut; + } + else + { + mOptOutState = newValue; + } + + if (oldValue != newValue) + { + ChipLogDetail(AppServer, "mOptOutState updated to %d mPowerAdjustmentInProgress %d", to_underlying(mOptOutState), + mPowerAdjustmentInProgress); + MatterReportingAttributeChangeCallback(mEndpointId, DeviceEnergyManagement::Id, OptOutState::Id); + } + + // Cancel any outstanding PowerAdjustment if necessary + if (mPowerAdjustmentInProgress) + { + if ((newValue == OptOutStateEnum::kLocalOptOut && + mPowerAdjustCapabilityStruct.Value().cause == PowerAdjustReasonEnum::kLocalOptimizationAdjustment) || + (newValue == OptOutStateEnum::kGridOptOut && + mPowerAdjustCapabilityStruct.Value().cause == PowerAdjustReasonEnum::kGridOptimizationAdjustment) || + newValue == OptOutStateEnum::kOptOut) + { + err = CancelPowerAdjustRequestAndGenerateEvent(DeviceEnergyManagement::CauseEnum::kUserOptOut); + } + } + + // Cancel any outstanding PauseRequest if necessary + if (mPauseRequestInProgress) + { + // Cancel any outstanding PauseRequest + if ((newValue == OptOutStateEnum::kLocalOptOut && + mForecast.Value().forecastUpdateReason == ForecastUpdateReasonEnum::kLocalOptimization) || + (newValue == OptOutStateEnum::kGridOptOut && + mForecast.Value().forecastUpdateReason == ForecastUpdateReasonEnum::kGridOptimization) || + newValue == OptOutStateEnum::kOptOut) + { + err = CancelPauseRequestAndGenerateEvent(DeviceEnergyManagement::CauseEnum::kUserOptOut); + } + } + + if (!mForecast.IsNull()) + { + switch (mForecast.Value().forecastUpdateReason) + { + case ForecastUpdateReasonEnum::kInternalOptimization: + // We don't need to redo a forecast since its internal already + break; + case ForecastUpdateReasonEnum::kLocalOptimization: + if ((mOptOutState == OptOutStateEnum::kOptOut) || (mOptOutState == OptOutStateEnum::kLocalOptOut)) + { + mForecast.Value().forecastUpdateReason = ForecastUpdateReasonEnum::kInternalOptimization; + // Generate a new forecast with Internal Optimization + // TODO + } + break; + case ForecastUpdateReasonEnum::kGridOptimization: + if ((mOptOutState == OptOutStateEnum::kOptOut) || (mOptOutState == OptOutStateEnum::kGridOptOut)) + { + mForecast.Value().forecastUpdateReason = ForecastUpdateReasonEnum::kInternalOptimization; + // Generate a new forecast with Internal Optimization + // TODO + } + break; + default: + ChipLogDetail(AppServer, "Bad ForecastUpdateReasonEnum value of %d", + to_underlying(mForecast.Value().forecastUpdateReason)); + return CHIP_ERROR_BAD_REQUEST; + break; + } + } + + return err; } diff --git a/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp b/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp index c31e0624e4c743..12f754a53ff804 100644 --- a/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp +++ b/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,6 +22,16 @@ using namespace chip::app; using namespace chip::app::Clusters; using namespace chip::app::Clusters::DeviceEnergyManagement; +namespace chip { +namespace app { +namespace Clusters { + +DeviceEnergyManagementManager::DeviceEnergyManagementManager(EndpointId aEndpointId, DeviceEnergyManagementDelegate & aDelegate, + Feature aFeature) : + DeviceEnergyManagement::Instance(aEndpointId, aDelegate, aFeature), + mDelegate(&aDelegate) +{} + CHIP_ERROR DeviceEnergyManagementManager::Init() { return Instance::Init(); @@ -31,3 +41,7 @@ void DeviceEnergyManagementManager::Shutdown() { Instance::Shutdown(); } + +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp b/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp index d9848bcbc53515..9006477b3662d5 100644 --- a/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp +++ b/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,9 +16,13 @@ * limitations under the License. */ +#include +#include #include #include +#include +#include #include #include #include @@ -58,6 +62,13 @@ CHIP_ERROR EVSEManufacturer::Init() ReturnErrorOnFailure(InitializePowerMeasurementCluster()); ReturnErrorOnFailure(InitializePowerSourceCluster()); + + DeviceEnergyManagementDelegate * dem = GetEvseManufacturer()->GetDEMDelegate(); + VerifyOrReturnLogError(dem != nullptr, CHIP_ERROR_UNINITIALIZED); + + /* For Device Energy Management we need the ESA to be Online and ready to accept commands */ + dem->SetESAState(ESAStateEnum::kOnline); + /* * This is an example implementation for manufacturers to consider * @@ -322,147 +333,6 @@ CHIP_ERROR EVSEManufacturer::SendPeriodicEnergyReading(EndpointId aEndpointId, i 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; /* Active 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 */ - int64_t mVoltage_mV; /* Voltage reading in mV (signed value) */ - uint32_t mVoltageRandomness_mV; /* The amount to randomize the Voltage in mV */ - int64_t mCurrent_mA; /* ActiveCurrent reading in mA (signed value) */ - uint32_t mCurrentRandomness_mA; /* The amount to randomize the ActiveCurrent in mA */ - - /* 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; /* Cumulative Energy Imported which is updated if mPower > 0 */ - int64_t mTotalEnergyExported = 0; /* Cumulative Energy Imported which is updated if mPower < 0 */ - int64_t mPeriodicEnergyImported = 0; /* Periodic Energy Imported which is updated if mPower > 0 */ - int64_t mPeriodicEnergyExported = 0; /* Periodic 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 max randomness of the - * random power values around the mean power of the load - * @param[in] aVoltage_mV - the nominal voltage measurement - * @param[in] aVoltageRandomness_mV This is used to define the max randomness of the - * random voltage values - * @param[in] aCurrent_mA - the nominal current measurement - * @param[in] aCurrentRandomness_mA This is used to define the max randomness of the - * random current values - * @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, - int64_t aVoltage_mV, uint32_t aVoltageRandomness_mV, int64_t aCurrent_mA, - uint32_t aCurrentRandomness_mA, uint8_t aInterval_s, bool bReset) -{ - gFakeReadingsData.bEnabled = true; - gFakeReadingsData.mEndpointId = aEndpointId; - gFakeReadingsData.mPower_mW = aPower_mW; - gFakeReadingsData.mPowerRandomness_mW = aPowerRandomness_mW; - gFakeReadingsData.mVoltage_mV = aVoltage_mV; - gFakeReadingsData.mVoltageRandomness_mV = aVoltageRandomness_mV; - gFakeReadingsData.mCurrent_mA = aCurrent_mA; - gFakeReadingsData.mCurrentRandomness_mA = aCurrentRandomness_mA; - 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 readings - // 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 +/- mPowerRandomness_mW - int64_t power = - (static_cast(rand()) % (2 * gFakeReadingsData.mPowerRandomness_mW)) - gFakeReadingsData.mPowerRandomness_mW; - power += gFakeReadingsData.mPower_mW; // add in the base power - - int64_t voltage = - (static_cast(rand()) % (2 * gFakeReadingsData.mVoltageRandomness_mV)) - gFakeReadingsData.mVoltageRandomness_mV; - voltage += gFakeReadingsData.mVoltage_mV; // add in the base voltage - - /* Note: whilst we could compute a current from the power and voltage, - * there will always be some random error from the sensor - * that measures it. To keep this simple and to avoid doing divides in integer - * format etc use the same approach here too. - * This is meant more as an example to show how to use the APIs, not - * to be a real representation of laws of physics. - */ - int64_t current = - (static_cast(rand()) % (2 * gFakeReadingsData.mCurrentRandomness_mA)) - gFakeReadingsData.mCurrentRandomness_mA; - current += gFakeReadingsData.mCurrent_mA; // add in the base current - - SendPowerReading(gFakeReadingsData.mEndpointId, power, voltage, current); - - // 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.mPeriodicEnergyImported = ((power * gFakeReadingsData.mInterval_s) / 3600); - gFakeReadingsData.mPeriodicEnergyExported = 0; - gFakeReadingsData.mTotalEnergyImported += gFakeReadingsData.mPeriodicEnergyImported; - } - else - { - // Negative power - means power is exported, but the exported energy is reported positive - gFakeReadingsData.mPeriodicEnergyImported = 0; - gFakeReadingsData.mPeriodicEnergyExported = ((-power * gFakeReadingsData.mInterval_s) / 3600); - gFakeReadingsData.mTotalEnergyExported += gFakeReadingsData.mPeriodicEnergyExported; - } - - SendPeriodicEnergyReading(gFakeReadingsData.mEndpointId, gFakeReadingsData.mPeriodicEnergyImported, - gFakeReadingsData.mPeriodicEnergyExported); - - SendCumulativeEnergyReading(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 * @@ -501,227 +371,65 @@ void EVSEManufacturer::ApplicationCallbackHandler(const EVSECbInfo * cb, intptr_ } } -struct EVSETestEventSaveData +// The PowerAdjustEnd event needs to report the approximate energy used by the ESA during the session. +int64_t EVSEManufacturer::GetApproxEnergyDuringSession() { - 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->GetEvseDelegate(); - 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); + return 300; } -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() +CHIP_ERROR EVSEManufacturer::HandleDeviceEnergyManagementPowerAdjustRequest(const int64_t powerMw, const uint32_t durationS, + AdjustmentCauseEnum cause) { - 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); + return CHIP_NO_ERROR; } -void SetTestEventTrigger_EVChargeDemand() +CHIP_ERROR EVSEManufacturer::HandleDeviceEnergyManagementPowerAdjustCompletion() { - 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); + return CHIP_NO_ERROR; } -void SetTestEventTrigger_EVSEOverTemperatureFault() +CHIP_ERROR EVSEManufacturer::HandleDeviceEnergyManagementCancelPowerAdjustRequest(CauseEnum cause) { - EnergyEvseDelegate * dg = GetEvseDelegate(); - - dg->HwSetFault(FaultStateEnum::kOverTemperature); + return CHIP_NO_ERROR; } -void SetTestEventTrigger_EVSEFaultClear() +CHIP_ERROR EVSEManufacturer::HandleDeviceEnergyManagementStartTimeAdjustRequest(const uint32_t requestedStartTimeUtc, + AdjustmentCauseEnum cause) { - EnergyEvseDelegate * dg = GetEvseDelegate(); - - dg->HwSetFault(FaultStateEnum::kNoError); + return CHIP_NO_ERROR; } -void SetTestEventTrigger_EVSEDiagnosticsComplete() +CHIP_ERROR EVSEManufacturer::HandleDeviceEnergyManagementPauseRequest(const uint32_t durationS, AdjustmentCauseEnum cause) { - EnergyEvseDelegate * dg = GetEvseDelegate(); - - dg->HwDiagnosticsComplete(); + return CHIP_NO_ERROR; } -void SetTestEventTrigger_FakeReadingsLoadStart() +CHIP_ERROR EVSEManufacturer::HandleDeviceEnergyManagementCancelPauseRequest(CauseEnum cause) { - 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 - int64_t aVoltage_mV = 230'000; // Fake Voltage 230V - uint32_t aVoltageRandomness_mV = 1'000; // randomness 1V - int64_t aCurrent_mA = 4'348; // Fake Current (at 1kW@230V = 4.3478 Amps) - uint32_t aCurrentRandomness_mA = 500; // randomness 500mA - uint8_t aInterval_s = 2; // 2s updates - bool bReset = true; - mn->StartFakeReadings(EndpointId(1), aPower_mW, aPowerRandomness_mW, aVoltage_mV, aVoltageRandomness_mV, aCurrent_mA, - aCurrentRandomness_mA, aInterval_s, bReset); + return CHIP_NO_ERROR; } -void SetTestEventTrigger_FakeReadingsGeneratorStart() +CHIP_ERROR EVSEManufacturer::HandleDeviceEnergyManagementPauseCompletion() { - 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 - int64_t aVoltage_mV = 230'000; // Fake Voltage 230V - uint32_t aVoltageRandomness_mV = 1'000; // randomness 1V - int64_t aCurrent_mA = -13'043; // Fake Current (at -3kW@230V = -13.0434 Amps) - uint32_t aCurrentRandomness_mA = 500; // randomness 500mA - uint8_t aInterval_s = 5; // 5s updates - bool bReset = true; - mn->StartFakeReadings(EndpointId(1), aPower_mW, aPowerRandomness_mW, aVoltage_mV, aVoltageRandomness_mV, aCurrent_mA, - aCurrentRandomness_mA, aInterval_s, bReset); + return CHIP_NO_ERROR; } -void SetTestEventTrigger_FakeReadingsStop() +CHIP_ERROR EVSEManufacturer::HandleDeviceEnergyManagementCancelRequest() { - EVSEManufacturer * mn = GetEvseManufacturer(); - VerifyOrDieWithMsg(mn != nullptr, AppServer, "EVSEManufacturer is null"); - mn->StopFakeReadings(); + return CHIP_NO_ERROR; } -bool HandleEnergyEvseTestEventTrigger(uint64_t eventTrigger) +CHIP_ERROR EVSEManufacturer::HandleModifyForecastRequest( + const uint32_t forecastID, + const DataModel::DecodableList & slotAdjustments, + AdjustmentCauseEnum cause) { - 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; + return CHIP_NO_ERROR; } -bool HandleEnergyReportingTestEventTrigger(uint64_t eventTrigger) +CHIP_ERROR EVSEManufacturer::RequestConstraintBasedForecast( + const DataModel::DecodableList & constraints, + AdjustmentCauseEnum cause) { - 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; + return CHIP_NO_ERROR; } diff --git a/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp b/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp index 99619fc95b5fb0..b0cfd6fc5ce0ef 100644 --- a/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp +++ b/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/examples/energy-management-app/energy-management-common/src/EnergyEvseEventTriggers.cpp b/examples/energy-management-app/energy-management-common/src/EnergyEvseEventTriggers.cpp new file mode 100644 index 00000000000000..62329068610f47 --- /dev/null +++ b/examples/energy-management-app/energy-management-common/src/EnergyEvseEventTriggers.cpp @@ -0,0 +1,184 @@ +/* + * + * 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 + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::EnergyEvse; + +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->GetEvseDelegate(); + 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(); +} + +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; +} diff --git a/examples/energy-management-app/energy-management-common/src/EnergyEvseMain.cpp b/examples/energy-management-app/energy-management-common/src/EnergyEvseMain.cpp index 6eeaaf8369c38c..0a4bc0456e2254 100644 --- a/examples/energy-management-app/energy-management-common/src/EnergyEvseMain.cpp +++ b/examples/energy-management-app/energy-management-common/src/EnergyEvseMain.cpp @@ -16,6 +16,8 @@ * limitations under the License. */ +#include "EnergyManagementAppCmdLineOptions.h" + #include #include #include @@ -86,13 +88,10 @@ CHIP_ERROR DeviceEnergyManagementInit() return CHIP_ERROR_NO_MEMORY; } + BitMask featureMap = GetFeatureMapFromCmdLine(); + /* Manufacturer may optionally not support all features, commands & attributes */ - gDEMInstance = std::make_unique( - EndpointId(ENERGY_EVSE_ENDPOINT), *gDEMDelegate, - BitMask( - DeviceEnergyManagement::Feature::kPowerAdjustment, DeviceEnergyManagement::Feature::kPowerForecastReporting, - DeviceEnergyManagement::Feature::kStateForecastReporting, DeviceEnergyManagement::Feature::kStartTimeAdjustment, - DeviceEnergyManagement::Feature::kPausable)); + gDEMInstance = std::make_unique(EndpointId(ENERGY_EVSE_ENDPOINT), *gDEMDelegate, featureMap); if (!gDEMInstance) { @@ -101,6 +100,8 @@ CHIP_ERROR DeviceEnergyManagementInit() return CHIP_ERROR_NO_MEMORY; } + gDEMDelegate->SetDeviceEnergyManagementInstance(*gDEMInstance); + CHIP_ERROR err = gDEMInstance->Init(); /* Register Attribute & Command handlers */ if (err != CHIP_NO_ERROR) { @@ -375,13 +376,16 @@ CHIP_ERROR EVSEManufacturerInit() } /* Now create EVSEManufacturer */ - gEvseManufacturer = std::make_unique(gEvseInstance.get(), gEPMInstance.get(), gPTInstance.get()); + gEvseManufacturer = + std::make_unique(gEvseInstance.get(), gEPMInstance.get(), gPTInstance.get(), gDEMInstance.get()); if (!gEvseManufacturer) { ChipLogError(AppServer, "Failed to allocate memory for EvseManufacturer"); return CHIP_ERROR_NO_MEMORY; } + gDEMDelegate.get()->SetDEMManufacturerDelegate(*gEvseManufacturer.get()); + /* Call Manufacturer specific init */ err = gEvseManufacturer->Init(); if (err != CHIP_NO_ERROR) diff --git a/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp b/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp index f4b3941e8c1ca7..c8b43342f8bd1c 100644 --- a/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp +++ b/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/examples/energy-management-app/energy-management-common/src/EnergyReportingEventTriggers.cpp b/examples/energy-management-app/energy-management-common/src/EnergyReportingEventTriggers.cpp new file mode 100644 index 00000000000000..e01612a5f408d3 --- /dev/null +++ b/examples/energy-management-app/energy-management-common/src/EnergyReportingEventTriggers.cpp @@ -0,0 +1,85 @@ +/* + * + * 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 "FakeReadings.h" + +#include + +using namespace chip; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::ElectricalEnergyMeasurement; +using namespace chip::app::Clusters::ElectricalEnergyMeasurement::Structs; + +void SetTestEventTrigger_FakeReadingsLoadStart() +{ + int64_t aPower_mW = 1'000'000; // Fake load 1000 W + uint32_t aPowerRandomness_mW = 20'000; // randomness 20W + int64_t aVoltage_mV = 230'000; // Fake Voltage 230V + uint32_t aVoltageRandomness_mV = 1'000; // randomness 1V + int64_t aCurrent_mA = 4'348; // Fake Current (at 1kW@230V = 4.3478 Amps) + uint32_t aCurrentRandomness_mA = 500; // randomness 500mA + uint8_t aInterval_s = 2; // 2s updates + bool bReset = true; + FakeReadings::GetInstance().StartFakeReadings(EndpointId(1), aPower_mW, aPowerRandomness_mW, aVoltage_mV, aVoltageRandomness_mV, + aCurrent_mA, aCurrentRandomness_mA, aInterval_s, bReset); +} + +void SetTestEventTrigger_FakeReadingsGeneratorStart() +{ + int64_t aPower_mW = -3'000'000; // Fake Generator -3000 W + uint32_t aPowerRandomness_mW = 20'000; // randomness 20W + int64_t aVoltage_mV = 230'000; // Fake Voltage 230V + uint32_t aVoltageRandomness_mV = 1'000; // randomness 1V + int64_t aCurrent_mA = -13'043; // Fake Current (at -3kW@230V = -13.0434 Amps) + uint32_t aCurrentRandomness_mA = 500; // randomness 500mA + uint8_t aInterval_s = 5; // 5s updates + bool bReset = true; + FakeReadings::GetInstance().StartFakeReadings(EndpointId(1), aPower_mW, aPowerRandomness_mW, aVoltage_mV, aVoltageRandomness_mV, + aCurrent_mA, aCurrentRandomness_mA, aInterval_s, bReset); +} + +void SetTestEventTrigger_FakeReadingsStop() +{ + FakeReadings::GetInstance().StopFakeReadings(); +} + +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/energy-management-common/src/EnergyTimeUtils.cpp b/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp new file mode 100644 index 00000000000000..60732c0f3b0497 --- /dev/null +++ b/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp @@ -0,0 +1,153 @@ +/* + * + * 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 + +using namespace chip; +using namespace chip::app; +using namespace chip::app::DataModel; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::EnergyEvse; +using namespace chip::app::Clusters::EnergyEvse::Attributes; + +using chip::app::LogEvent; +using chip::Protocols::InteractionModel::Status; + +namespace chip { +namespace app { +namespace Clusters { +namespace DeviceEnergyManagement { + +/** + * @brief Helper function to get current timestamp in Epoch format + * + * @param[out] chipEpoch reference to hold return timestamp. Set to 0 if an error occurs. + */ +CHIP_ERROR GetEpochTS(uint32_t & chipEpoch) +{ + chipEpoch = 0; + + System::Clock::Milliseconds64 cTMs; + CHIP_ERROR err = System::SystemClock().GetClock_RealTimeMS(cTMs); + + /* If the GetClock_RealTimeMS returns CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE, then + * This platform cannot ever report real time ! + * This should not be certifiable since getting time is a Mandatory + * feature of EVSE Cluster + */ + VerifyOrDie(err != CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Unable to get current time - err:%" CHIP_ERROR_FORMAT, err.Format()); + return err; + } + + auto unixEpoch = std::chrono::duration_cast(cTMs).count(); + if (!UnixEpochToChipEpochTime(unixEpoch, chipEpoch)) + { + ChipLogError(Zcl, "Unable to convert Unix Epoch time to Matter Epoch Time"); + return CHIP_ERROR_INCORRECT_STATE; + } + + return CHIP_NO_ERROR; +} + +/** + * @brief Helper function to get current timestamp and work out the day of week + * + * NOTE that the time_t is converted using localtime to provide the timestamp + * in local time. If this is not supported on some platforms an alternative + * implementation may be required. + * + * @param unixEpoch (as time_t) + * + * @return bitmap value for day of week as defined by EnergyEvse::TargetDayOfWeekBitmap. Note + * only one bit will be set for the day of the week. + */ +BitMask GetLocalDayOfWeekFromUnixEpoch(time_t unixEpoch) +{ + // Define a timezone structure and initialize it to the local timezone + // This will capture any daylight saving time changes + struct tm local_time; + localtime_r(&unixEpoch, &local_time); + + // Get the day of the week (0 = Sunday, 1 = Monday, ..., 6 = Saturday) + uint8_t dayOfWeek = static_cast(local_time.tm_wday); + + // Calculate the bitmap value based on the day of the week. Note that the value in bitmap + // maps directly to the definition in EnergyEvse::TargetDayOfWeekBitmap. + uint8_t bitmap = static_cast(1 << dayOfWeek); + + return bitmap; +} +/** + * @brief Helper function to get current timestamp and work out the day of week based on localtime + * + * @return bitmap value for day of week as defined by EnergyEvse::TargetDayOfWeekBitmap. Note + * only one bit will be set for the current day. + */ +CHIP_ERROR GetLocalDayOfWeekNow(BitMask & dayOfWeekMap) +{ + System::Clock::Milliseconds64 cTMs; + CHIP_ERROR err = chip::System::SystemClock().GetClock_RealTimeMS(cTMs); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Uable to get current time. error=%" CHIP_ERROR_FORMAT, err.Format()); + return err; + } + time_t unixEpoch = std::chrono::duration_cast(cTMs).count(); + dayOfWeekMap = GetLocalDayOfWeekFromUnixEpoch(unixEpoch); + + return CHIP_NO_ERROR; +} + +/** + * @brief Helper function to get current timestamp and work out the current number of minutes + * past midnight based on localtime + * + * @param reference to hold the number of minutes past midnight + */ +CHIP_ERROR GetMinutesPastMidnight(uint16_t & minutesPastMidnight) +{ + System::Clock::Milliseconds64 cTMs; + CHIP_ERROR err = System::SystemClock().GetClock_RealTimeMS(cTMs); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "EVSE: unable to get current time to check user schedules error=%" CHIP_ERROR_FORMAT, err.Format()); + return err; + } + time_t unixEpoch = std::chrono::duration_cast(cTMs).count(); + + // Define a timezone structure and initialize it to the local timezone + // This will capture any daylight saving time changes + struct tm local_time; + localtime_r(&unixEpoch, &local_time); + + minutesPastMidnight = static_cast((local_time.tm_hour * 60) + local_time.tm_min); + + return err; +} + +} // namespace DeviceEnergyManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/energy-management-app/energy-management-common/src/FakeReadings.cpp b/examples/energy-management-app/energy-management-common/src/FakeReadings.cpp new file mode 100644 index 00000000000000..151e82be8436e9 --- /dev/null +++ b/examples/energy-management-app/energy-management-common/src/FakeReadings.cpp @@ -0,0 +1,176 @@ +/* + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "FakeReadings.h" + +using namespace chip; +using namespace chip::app; +using namespace chip::app::DataModel; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::EnergyEvse; +using namespace chip::app::Clusters::ElectricalPowerMeasurement; +using namespace chip::app::Clusters::ElectricalEnergyMeasurement; +using namespace chip::app::Clusters::ElectricalEnergyMeasurement::Structs; +using namespace chip::app::Clusters::PowerSource; +using namespace chip::app::Clusters::PowerSource::Attributes; + +using Protocols::InteractionModel::Status; + +FakeReadings::FakeReadings() {} + +FakeReadings::~FakeReadings() {} + +/* static */ +FakeReadings & FakeReadings::GetInstance() +{ + static FakeReadings sInstance; + + return sInstance; +} + +/** + * @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 max randomness of the + * random power values around the mean power of the load + * @param[in] aVoltage_mV - the nominal voltage measurement + * @param[in] aVoltageRandomness_mV This is used to define the max randomness of the + * random voltage values + * @param[in] aCurrent_mA - the nominal current measurement + * @param[in] aCurrentRandomness_mA This is used to define the max randomness of the + * random current values + * @param[in] aInterval_s - the callback interval in seconds + * @param[in] bReset - boolean: true will reset the energy values to 0 + */ +void FakeReadings::StartFakeReadings(EndpointId aEndpointId, int64_t aPower_mW, uint32_t aPowerRandomness_mW, int64_t aVoltage_mV, + uint32_t aVoltageRandomness_mV, int64_t aCurrent_mA, uint32_t aCurrentRandomness_mA, + uint8_t aInterval_s, bool bReset) +{ + bEnabled = true; + mEndpointId = aEndpointId; + mPower_mW = aPower_mW; + mPowerRandomness_mW = aPowerRandomness_mW; + mVoltage_mV = aVoltage_mV; + mVoltageRandomness_mV = aVoltageRandomness_mV; + mCurrent_mA = aCurrent_mA; + mCurrentRandomness_mA = aCurrentRandomness_mA; + mInterval_s = aInterval_s; + + if (bReset) + { + mTotalEnergyImported = 0; + mTotalEnergyExported = 0; + } + + // Call update function to kick off regular readings + FakeReadingsUpdate(); +} + +/** + * @brief Stops any active updates to the fake load data callbacks + */ +void FakeReadings::StopFakeReadings() +{ + bEnabled = false; +} + +/** + * @brief Sends fake meter data into the cluster and restarts the timer + */ +void FakeReadings::FakeReadingsUpdate() +{ + /* Check to see if the fake Load is still running - don't send updates if the timer was already cancelled */ + if (!bEnabled) + { + return; + } + + // Update readings + // 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 +/- mPowerRandomness_mW + int64_t power = (static_cast(rand()) % (2 * mPowerRandomness_mW)) - mPowerRandomness_mW; + power += mPower_mW; // add in the base power + + int64_t voltage = (static_cast(rand()) % (2 * mVoltageRandomness_mV)) - mVoltageRandomness_mV; + voltage += mVoltage_mV; // add in the base voltage + + /* Note: whilst we could compute a current from the power and voltage, + * there will always be some random error from the sensor + * that measures it. To keep this simple and to avoid doing divides in integer + * format etc use the same approach here too. + * This is meant more as an example to show how to use the APIs, not + * to be a real representation of laws of physics. + */ + int64_t current = (static_cast(rand()) % (2 * mCurrentRandomness_mA)) - mCurrentRandomness_mA; + current += mCurrent_mA; // add in the base current + + GetEvseManufacturer()->SendPowerReading(mEndpointId, power, voltage, current); + + // update the energy meter - we'll assume that the power has been constant during the previous interval + if (mPower_mW > 0) + { + // Positive power - means power is imported + mPeriodicEnergyImported = ((power * mInterval_s) / 3600); + mPeriodicEnergyExported = 0; + mTotalEnergyImported += mPeriodicEnergyImported; + } + else + { + // Negative power - means power is exported, but the exported energy is reported positive + mPeriodicEnergyImported = 0; + mPeriodicEnergyExported = ((-power * mInterval_s) / 3600); + mTotalEnergyExported += mPeriodicEnergyExported; + } + + GetEvseManufacturer()->SendPeriodicEnergyReading(mEndpointId, mPeriodicEnergyImported, mPeriodicEnergyExported); + + GetEvseManufacturer()->SendCumulativeEnergyReading(mEndpointId, mTotalEnergyImported, mTotalEnergyExported); + + // start/restart the timer + DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds32(mInterval_s), FakeReadingsTimerExpiry, this); +} + +/** + * @brief Timer expiry callback to handle fake load + */ +void FakeReadings::FakeReadingsTimerExpiry(System::Layer * systemLayer, void * manufacturer) +{ + FakeReadings * mn = reinterpret_cast(manufacturer); + + mn->FakeReadingsUpdate(); +} diff --git a/examples/energy-management-app/esp32/main/CMakeLists.txt b/examples/energy-management-app/esp32/main/CMakeLists.txt index a9a8c4100ae2de..15c1f99a6982aa 100644 --- a/examples/energy-management-app/esp32/main/CMakeLists.txt +++ b/examples/energy-management-app/esp32/main/CMakeLists.txt @@ -20,6 +20,7 @@ set(PRIV_INCLUDE_DIRS_LIST "${APP_GEN_DIR}" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/providers" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/energy-management-app/energy-management-common/include" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/energy-management-app/esp32/main/include" "${CMAKE_CURRENT_LIST_DIR}/include" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32" ) diff --git a/examples/energy-management-app/esp32/main/include/CHIPProjectConfig.h b/examples/energy-management-app/esp32/main/include/CHIPProjectConfig.h index 559895608a08d0..b70a285ccfda0b 100644 --- a/examples/energy-management-app/esp32/main/include/CHIPProjectConfig.h +++ b/examples/energy-management-app/esp32/main/include/CHIPProjectConfig.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/examples/energy-management-app/esp32/main/main.cpp b/examples/energy-management-app/esp32/main/main.cpp index e61e6cb00cca9b..136817a7744095 100644 --- a/examples/energy-management-app/esp32/main/main.cpp +++ b/examples/energy-management-app/esp32/main/main.cpp @@ -111,6 +111,26 @@ chip::Credentials::DeviceAttestationCredentialsProvider * get_dac_provider(void) } // namespace +namespace chip { +namespace app { +namespace Clusters { +namespace DeviceEnergyManagement { + +// Keep track of the parsed featureMap option +static chip::BitMask sFeatureMap(Feature::kPowerAdjustment, Feature::kPowerForecastReporting, + Feature::kStateForecastReporting, Feature::kStartTimeAdjustment, Feature::kPausable, + Feature::kForecastAdjustment, Feature::kConstraintBasedAdjustment); + +chip::BitMask GetFeatureMapFromCmdLine() +{ + return sFeatureMap; +} + +} // namespace DeviceEnergyManagement +} // namespace Clusters +} // namespace app +} // namespace chip + void ApplicationInit() { ESP_LOGD(TAG, "Energy Management App: ApplicationInit()"); diff --git a/examples/energy-management-app/esp32/sdkconfig.optimize.defaults b/examples/energy-management-app/esp32/sdkconfig.optimize.defaults index 87248eebfcdfa7..85a5c0cbd4615c 100644 --- a/examples/energy-management-app/esp32/sdkconfig.optimize.defaults +++ b/examples/energy-management-app/esp32/sdkconfig.optimize.defaults @@ -1,5 +1,5 @@ # -# Copyright (c) 2023 Project CHIP Authors +# Copyright (c) 2023-2024 Project CHIP Authors # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/examples/energy-management-app/linux/BUILD.gn b/examples/energy-management-app/linux/BUILD.gn index 4730c2c8ea82f3..c3564c345a1182 100644 --- a/examples/energy-management-app/linux/BUILD.gn +++ b/examples/energy-management-app/linux/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Project CHIP Authors +# Copyright (c) 2023-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. @@ -36,13 +36,18 @@ config("includes") { executable("chip-energy-management-app") { sources = [ + "${chip_root}/examples/energy-management-app/energy-management-common/src/DEMTestEventTriggers.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseEventTriggers.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseMain.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyReportingEventTriggers.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/FakeReadings.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/PowerTopologyDelegate.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/device-energy-management-mode.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/energy-evse-mode.cpp", diff --git a/examples/energy-management-app/linux/README.md b/examples/energy-management-app/linux/README.md index f91d341915ccd8..ab35600709de47 100644 --- a/examples/energy-management-app/linux/README.md +++ b/examples/energy-management-app/linux/README.md @@ -163,6 +163,14 @@ There are several test scripts provided for EVSE (in - `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) +- `TC_EEVSE_2_6`: This validates EVSE Forecast Adjustment with State Forecast + Reporting feature functionality +- `TC_EEVSE_2_7`: This validates EVSE Constraints-based Adjustment with Power + Forecast Reporting feature functionality +- `TC_EEVSE_2_8`: This validates EVSE Constraints-based Adjustment with State + Forecast Reporting feature functionality +- `TC_EEVSE_2_9`: This validates EVSE Power or State Forecast Reporting + feature functionality 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 @@ -183,6 +191,39 @@ chosen enable key is using the `--enable-key` command line option. From the top-level of the connectedhomeip repo type: +Start the chip-energy-management-app: + +```bash +rm -f evse.bin; out/debug/chip-energy-management-app --enable-key 000102030405060708090a0b0c0d0e0f --KVS evse.bin --featureSet $featureSet +``` + +where the \$featureSet depends on the test being run: + +``` +TC_DEM_2_2.py: 0x01 // PA +TC_DEM_2_3.py: 0x3b // STA, PAU, FA, CON + (PFR | SFR) +TC_DEM_2_4.py: 0x3b // STA, PAU, FA, CON + (PFR | SFR) +TC_DEM_2_5.py: 0x3b // STA, PAU, FA, CON + PFR +TC_DEM_2_6.py: 0x3d // STA, PAU, FA, CON + SFR +TC_DEM_2_7.py: 0x3b // STA, PAU, FA, CON + PFR +TC_DEM_2_8.py: 0x3d // STA, PAU, FA, CON + SFR +TC_DEM_2_9.py: 0x3f // STA, PAU, FA, CON + PFR + SFR +``` + +where + +``` +PA - DEM.S.F00(PowerAdjustment) +PFR - DEM.S.F01(PowerForecastReporting) +SFR - DEM.S.F02(StateForecastReporting) +STA - DEM.S.F03(StartTimeAdjustment) +PAU - DEM.S.F04(Pausable) +FA - DEM.S.F05(ForecastAdjustment) +CON -DEM.S.F06(ConstraintBasedAdjustment) +``` + +Then run the test: + ```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 ``` @@ -191,6 +232,12 @@ From the top-level of the connectedhomeip repo type: cluster is on endpoint 1. The `--hex-arg enableKey:` value must match the `--enable-key ` used on chip-energy-management-app args. +The chip-energy-management-app will need to be stopped before running each test +script as each test commissions the chip-energy-management-app in the first +step. That is also why the evse.bin is deleted before running +chip-energy-management-app as this is where the app stores the matter persistent +data (e.g. fabric info). + ## CHIP-REPL Interaction - See chip-repl documentation in diff --git a/examples/energy-management-app/linux/args.gni b/examples/energy-management-app/linux/args.gni index e1000e6cb3fa27..1f196e6122910c 100644 --- a/examples/energy-management-app/linux/args.gni +++ b/examples/energy-management-app/linux/args.gni @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Project CHIP Authors +# Copyright (c) 2023-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. @@ -31,3 +31,4 @@ matter_enable_tracing_support = true chip_enable_read_client = false chip_enable_energy_evse_trigger = true chip_enable_energy_reporting_trigger = true +chip_enable_device_energy_management_trigger = true diff --git a/examples/energy-management-app/linux/main.cpp b/examples/energy-management-app/linux/main.cpp index 7973c16025d640..2d056e56a9b149 100644 --- a/examples/energy-management-app/linux/main.cpp +++ b/examples/energy-management-app/linux/main.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,6 +18,71 @@ #include #include +#include +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::DeviceEnergyManagement; +using namespace chip::app::Clusters::DeviceEnergyManagement::Attributes; + +// Parse a hex (prefixed by 0x) or decimal (no-prefix) string +static uint32_t ParseNumber(const char * pString); + +// Parses the --featureMap option +static bool FeatureMapOptionHandler(const char * aProgram, chip::ArgParser::OptionSet * aOptions, int aIdentifier, + const char * aName, const char * aValue); + +constexpr uint16_t kOptionFeatureMap = 'f'; + +// Define the chip::ArgParser command line structures for extending the command line to support the +// -f/--featureMap option +static chip::ArgParser::OptionDef sFeatureMapOptionDefs[] = { + { "featureSet", chip::ArgParser::kArgumentRequired, kOptionFeatureMap }, { NULL } +}; + +static chip::ArgParser::OptionSet sCmdLineOptions = { + FeatureMapOptionHandler, // handler function + sFeatureMapOptionDefs, // array of option definitions + "GENERAL OPTIONS", // help group + "-f, --featureSet " // option help text +}; + +namespace chip { +namespace app { +namespace Clusters { +namespace DeviceEnergyManagement { + +// Keep track of the parsed featureMap option +static chip::BitMask sFeatureMap(Feature::kPowerAdjustment, Feature::kPowerForecastReporting, + Feature::kStateForecastReporting, Feature::kStartTimeAdjustment, Feature::kPausable, + Feature::kForecastAdjustment, Feature::kConstraintBasedAdjustment); + +chip::BitMask GetFeatureMapFromCmdLine() +{ + return sFeatureMap; +} + +} // namespace DeviceEnergyManagement +} // namespace Clusters +} // namespace app +} // namespace chip + +static uint32_t ParseNumber(const char * pString) +{ + uint32_t num = 0; + if (strlen(pString) > 2 && pString[0] == '0' && pString[1] == 'x') + { + num = (uint32_t) strtoul(&pString[2], NULL, 16); + } + else + { + num = (uint32_t) strtoul(pString, NULL, 10); + } + + return num; +} void ApplicationInit() { @@ -31,9 +96,29 @@ void ApplicationShutdown() EvseApplicationShutdown(); } +static bool FeatureMapOptionHandler(const char * aProgram, chip::ArgParser::OptionSet * aOptions, int aIdentifier, + const char * aName, const char * aValue) +{ + bool retval = true; + + switch (aIdentifier) + { + case kOptionFeatureMap: + sFeatureMap = BitMask(ParseNumber(aValue)); + ChipLogDetail(Support, "Using FeatureMap 0x%04x", sFeatureMap.Raw()); + break; + default: + ChipLogError(Support, "%s: INTERNAL ERROR: Unhandled option: %s\n", aProgram, aName); + retval = false; + break; + } + + return (retval); +} + int main(int argc, char * argv[]) { - if (ChipLinuxAppInit(argc, argv) != 0) + if (ChipLinuxAppInit(argc, argv, &sCmdLineOptions) != 0) { return -1; } diff --git a/examples/platform/linux/AppMain.cpp b/examples/platform/linux/AppMain.cpp index b92d76e613e618..37bf2495f08736 100644 --- a/examples/platform/linux/AppMain.cpp +++ b/examples/platform/linux/AppMain.cpp @@ -90,6 +90,9 @@ #if CHIP_DEVICE_CONFIG_ENABLE_ENERGY_REPORTING_TRIGGER #include #endif +#if CHIP_DEVICE_CONFIG_ENABLE_DEVICE_ENERGY_MANAGEMENT_TRIGGER +#include +#endif #include #include @@ -553,6 +556,10 @@ void ChipLinuxAppMainLoop(AppMainLoopImplementation * impl) static EnergyReportingTestEventTriggerHandler sEnergyReportingTestEventTriggerHandler; sTestEventTriggerDelegate.AddHandler(&sEnergyReportingTestEventTriggerHandler); #endif +#if CHIP_DEVICE_CONFIG_ENABLE_DEVICE_ENERGY_MANAGEMENT_TRIGGER + static DeviceEnergyManagementTestEventTriggerHandler sDeviceEnergyManagementTestEventTriggerHandler; + sTestEventTriggerDelegate.AddHandler(&sDeviceEnergyManagementTestEventTriggerHandler); +#endif initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; diff --git a/examples/platform/linux/BUILD.gn b/examples/platform/linux/BUILD.gn index 4641eae6446e04..cd79d2b42e3273 100644 --- a/examples/platform/linux/BUILD.gn +++ b/examples/platform/linux/BUILD.gn @@ -24,6 +24,7 @@ declare_args() { chip_enable_boolean_state_configuration_trigger = false chip_enable_energy_evse_trigger = false chip_enable_energy_reporting_trigger = false + chip_enable_device_energy_management_trigger = false } config("app-main-config") { @@ -52,6 +53,10 @@ source_set("energy-reporting-test-event-trigger") { sources = [ "${chip_root}/src/app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerHandler.h" ] } +source_set("device-energy-management-test-event-trigger") { + sources = [ "${chip_root}/src/app/clusters/device-energy-management-server/DeviceEnergyManagementTestEventTriggerHandler.h" ] +} + source_set("app-main") { defines = [ "ENABLE_TRACING=${matter_enable_tracing_support}" ] sources = [ @@ -75,6 +80,7 @@ source_set("app-main") { public_deps = [ ":boolean-state-configuration-test-event-trigger", + ":device-energy-management-test-event-trigger", ":energy-evse-test-event-trigger", ":energy-reporting-test-event-trigger", ":smco-test-event-trigger", @@ -117,6 +123,7 @@ source_set("app-main") { "CHIP_DEVICE_CONFIG_ENABLE_BOOLEAN_STATE_CONFIGURATION_TRIGGER=${chip_enable_boolean_state_configuration_trigger}", "CHIP_DEVICE_CONFIG_ENABLE_ENERGY_EVSE_TRIGGER=${chip_enable_energy_evse_trigger}", "CHIP_DEVICE_CONFIG_ENABLE_ENERGY_REPORTING_TRIGGER=${chip_enable_energy_reporting_trigger}", + "CHIP_DEVICE_CONFIG_ENABLE_DEVICE_ENERGY_MANAGEMENT_TRIGGER=${chip_enable_device_energy_management_trigger}", ] public_configs = [ ":app-main-config" ] diff --git a/examples/shell/shell_common/BUILD.gn b/examples/shell/shell_common/BUILD.gn index cb098a64598a1b..0e318ccb31a5ca 100644 --- a/examples/shell/shell_common/BUILD.gn +++ b/examples/shell/shell_common/BUILD.gn @@ -72,14 +72,17 @@ static_library("shell_common") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp", ] include_dirs = [ "${chip_root}/examples/all-clusters-app/all-clusters-common/include", "${chip_root}/examples/energy-management-app/energy-management-common/include", + "${chip_root}/src/app/clusters/device-energy-management-server", ] public_deps += diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index b436f46cb7cb1f..da75e616aa1510 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -386,6 +386,12 @@ template("chip_data_model") { "${_app_root}/clusters/${cluster}/${cluster}.h", "${_app_root}/clusters/${cluster}/EnergyReportingTestEventTriggerHandler.h", ] + } else if (cluster == "device-energy-management-server") { + sources += [ + "${_app_root}/clusters/${cluster}/${cluster}.cpp", + "${_app_root}/clusters/${cluster}/${cluster}.h", + "${_app_root}/clusters/${cluster}/DeviceEnergyManagementTestEventTriggerHandler.h", + ] } else if (cluster == "thread-network-diagnostics-server") { sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp", diff --git a/src/app/clusters/device-energy-management-server/device-energy-management-server.cpp b/src/app/clusters/device-energy-management-server/device-energy-management-server.cpp index dc6e2a44de618a..fa913bc900a065 100644 --- a/src/app/clusters/device-energy-management-server/device-energy-management-server.cpp +++ b/src/app/clusters/device-energy-management-server/device-energy-management-server.cpp @@ -582,7 +582,7 @@ void Instance::HandlePauseRequest(HandlerContext & ctx, const Commands::PauseReq if (!forecast.Value().slots[activeSlotNumber].slotIsPausable.Value()) { ChipLogError(Zcl, "DEM: activeSlotNumber %d is NOT pausable.", activeSlotNumber); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Failure); return; } @@ -607,6 +607,7 @@ void Instance::HandlePauseRequest(HandlerContext & ctx, const Commands::PauseReq if (status != Status::Success) { ChipLogError(Zcl, "DEM: PauseRequest(%ld) FAILURE", static_cast(duration)); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); return; } } @@ -623,7 +624,7 @@ void Instance::HandleResumeRequest(HandlerContext & ctx, const Commands::ResumeR if (ESAStateEnum::kPaused != mDelegate.GetESAState()) { ChipLogError(Zcl, "DEM: ESAState not Paused."); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Failure); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidInState); return; } @@ -678,7 +679,7 @@ void Instance::HandleModifyForecastRequest(HandlerContext & ctx, const Commands: if (slotAdjustment.slotIndex > forecast.Value().slots.size()) { ChipLogError(Zcl, "DEM: Bad slot index %d", slotAdjustment.slotIndex); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Failure); return; } @@ -726,6 +727,7 @@ void Instance::HandleModifyForecastRequest(HandlerContext & ctx, const Commands: if (status != Status::Success) { ChipLogError(Zcl, "DEM: ModifyForecastRequest FAILURE"); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Failure); return; } } diff --git a/src/app/clusters/device-energy-management-server/device-energy-management-server.h b/src/app/clusters/device-energy-management-server/device-energy-management-server.h index b0d27b0b99edd7..c58e5c5a73743a 100644 --- a/src/app/clusters/device-energy-management-server/device-energy-management-server.h +++ b/src/app/clusters/device-energy-management-server/device-energy-management-server.h @@ -45,6 +45,9 @@ class Delegate * @brief Delegate should implement a handler to begin to adjust client power * consumption/generation to the level requested. * + * Note callers must call GetPowerAdjustmentCapability and ensure the return value is not null + * before calling PowerAdjustRequest. + * * @param power Milli-Watts the ESA SHALL use during the adjustment period. * @param duration The duration that the ESA SHALL maintain the requested power for. * @return Success if the adjustment is accepted; otherwise the command SHALL be rejected with appropriate error. @@ -160,25 +163,42 @@ class Delegate // ------------------------------------------------------------------ // Get attribute methods - virtual ESATypeEnum GetESAType() = 0; - virtual bool GetESACanGenerate() = 0; - virtual ESAStateEnum GetESAState() = 0; - virtual int64_t GetAbsMinPower() = 0; - virtual int64_t GetAbsMaxPower() = 0; - virtual DataModel::Nullable GetPowerAdjustmentCapability() = 0; - virtual DataModel::Nullable GetForecast() = 0; - virtual OptOutStateEnum GetOptOutState() = 0; + virtual ESATypeEnum GetESAType() = 0; + virtual bool GetESACanGenerate() = 0; + virtual ESAStateEnum GetESAState() = 0; + virtual int64_t GetAbsMinPower() = 0; + virtual int64_t GetAbsMaxPower() = 0; + virtual OptOutStateEnum GetOptOutState() = 0; + + /** + * @brief Returns the current PowerAdjustCapability object + * + * The reference returned from GetPowerAdjustmentCapability() is only valid until the next Matter event + * is processed. Callers must not hold on to that reference for any asynchronous processing. + * + * Once another Matter event has had a chance to run, the memory associated with the + * PowerAdjustCapabilityStruct is likely to change or be re-allocated, so would become invalid. + * + * @return The current PowerAdjustCapability object + */ + virtual const DataModel::Nullable & GetPowerAdjustmentCapability() = 0; + + /** + * @brief Returns the current Forecast object + * + * The reference returned from GetForecast() is only valid until the next Matter event + * is processed. Callers must not hold on to that reference for any asynchronous processing. + * + * Once another Matter event has had a chance to run, the memory associated with the + * ForecastStruct is likely to change or be re-allocated, so would become invalid. + * + * @return The current Forecast object + */ + virtual const DataModel::Nullable & GetForecast() = 0; // ------------------------------------------------------------------ // Set attribute methods - virtual CHIP_ERROR SetESAType(ESATypeEnum) = 0; - virtual CHIP_ERROR SetESACanGenerate(bool) = 0; - virtual CHIP_ERROR SetESAState(ESAStateEnum) = 0; - virtual CHIP_ERROR SetAbsMinPower(int64_t) = 0; - virtual CHIP_ERROR SetAbsMaxPower(int64_t) = 0; - virtual CHIP_ERROR SetPowerAdjustmentCapability(DataModel::Nullable) = 0; - virtual CHIP_ERROR SetForecast(DataModel::Nullable) = 0; - virtual CHIP_ERROR SetOptOutState(OptOutStateEnum) = 0; + virtual CHIP_ERROR SetESAState(ESAStateEnum) = 0; protected: EndpointId mEndpointId = 0; diff --git a/src/python_testing/TC_DEM_2_2.py b/src/python_testing/TC_DEM_2_2.py new file mode 100644 index 00000000000000..dc81cbcc0aa9d2 --- /dev/null +++ b/src/python_testing/TC_DEM_2_2.py @@ -0,0 +1,367 @@ +# +# 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. +# pylint: disable=invalid-name + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${ENERGY_MANAGEMENT_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json --enable-key 000102030405060708090a0b0c0d0e0f --featureSet 0x01 +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --hex-arg enableKey:000102030405060708090a0b0c0d0e0f --endpoint 1 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# === END CI TEST ARGUMENTS === + +"""Define Matter test case TC_DEM_2_2.""" + + +import datetime +import logging +import sys +import time + +import chip.clusters as Clusters +from chip.interaction_model import Status +from matter_testing_support import EventChangeCallback, MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts +from TC_DEMTestBase import DEMTestBase + +logger = logging.getLogger(__name__) + + +class TC_DEM_2_2(MatterBaseTest, DEMTestBase): + """Implementation of test case TC_DEM_2_2.""" + + def desc_TC_DEM_2_2(self) -> str: + """Return a description of this test.""" + return "4.1.3. [TC-DEM-2.2] Power Adjustment feature functionality with DUT as Server" + + def pics_TC_DEM_2_2(self): + """Return the PICS definitions associated with this test.""" + pics = [ + "DEM.S.F00", # Depends on Feature 00 (PowerAdjustment) + ] + return pics + + def steps_TC_DEM_2_2(self) -> list[TestStep]: + """Execute the test steps.""" + steps = [ + TestStep("1", "Commissioning, already done", + is_commissioning=True), + TestStep("2", "TH reads TestEventTriggersEnabled attribute from General Diagnostics Cluster.", + "Verify that TestEventTriggersEnabled attribute has a value of 1 (True)"), + TestStep("3", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Power Adjustment Test Event", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("3a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("3b", "TH reads PowerAdjustmentCapability attribute.", + "Value has to include Cause=NoAdjustment. Note value for later. Determine the OverallMaxPower and OverallMaxDuration as the largest MaxPower and MaxDuration of the PowerAdjustStructs returned, and similarly the OverallMinPower and OverallMinDuration as the smallest of the MinPower and MinDuration values."), + TestStep("3c", "TH reads OptOutState attribute.", + "Verify value is 0x00 (NoOptOut)"), + TestStep("4", "TH sends PowerAdjustRequest with Power=PowerAdjustmentCapability[0].MaxPower, Duration=PowerAdjustmentCapability[0].MinDuration, Cause=LocalOptimization.", + "Verify DUT responds with status SUCCESS(0x00) and Event DEM.S.E00(PowerAdjustStart) sent"), + TestStep("4a", "TH reads ESAState attribute.", + "Verify value is 0x04 (PowerAdjustActive)"), + TestStep("4b", "TH reads PowerAdjustmentCapability attribute.", + "Value has to include Cause=LocalOptimizationAdjustment."), + TestStep("5", "TH sends CancelPowerAdjustRequest.", + "Verify DUT responds with status SUCCESS(0x00) and Event DEM.S.E01(PowerAdjustEnd) sent with Cause=Cancelled"), + TestStep("5a", "TH reads PowerAdjustmentCapability attribute.", + "Value has to include Cause=NoAdjustment."), + TestStep("5b", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("6", "TH sends CancelPowerAdjustRequest.", + "Verify DUT responds with status INVALID_IN_STATE(0xcb)"), + TestStep("7", "TH sends PowerAdjustRequest with Power=OverallMaxPower+1 Duration=OverallMinDuration Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("8", "TH sends PowerAdjustRequest with Power=OverallMinPower Duration=OverallMaxDuration+1 Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("9", "TH sends PowerAdjustRequest with Power=OverallMinPower-1 Duration=OverallMaxDuration Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("10", "TH sends PowerAdjustRequest with Power=OverallMaxPower Duration=OverallMinDuration-1 Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("11", "TH sends PowerAdjustRequest with Power=PowerAdjustmentCapability[0].MaxPower, Duration=PowerAdjustmentCapability[0].MinDuration, Cause=LocalOptimization.", + "Verify DUT responds with status SUCCESS(0x00) and event DEM.S.E00(PowerAdjustStart) sent"), + TestStep("11a", "TH reads PowerAdjustmentCapability attribute.", + "Value has to include Cause=LocalOptimizationAdjustment."), + TestStep("12", "TH sends PowerAdjustRequest with Power=PowerAdjustmentCapability[0].MaxPower, Duration=PowerAdjustmentCapability[0].MinDuration, Cause=GridOptimization.", + "Verify DUT responds with status SUCCESS(0x00) and no event sent"), + TestStep("12a", "TH reads ESAState attribute.", + "Verify value is 0x04 (PowerAdjustActive)"), + TestStep("12b", "TH reads PowerAdjustmentCapability attribute.", + "Value has to include Cause=GridOptimizationAdjustment."), + TestStep("13", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Local Optimization Test Event.", + "Verify DUT responds with status SUCCESS(0x00) and no event sent"), + TestStep("13a", "TH reads ESAState attribute.", + "Verify value is 0x04 (PowerAdjustActive)"), + TestStep("13b", "TH reads OptOutState attribute.", + "Verify value is 0x02 (LocalOptOut)"), + TestStep("14", "TH sends PowerAdjustRequest with Power=PowerAdjustmentCapability[0].MaxPower, Duration=PowerAdjustmentCapability[0].MinDuration, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("15", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Grid Optimization Test Event.", + "Verify DUT responds with status SUCCESS(0x00) and event DEM.S.E01(PowerAdjustEnd) sent with Cause=UserOptOut, Duration= approx time from step 11 to step 15, EnergyUse= a valid value"), + TestStep("15a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("15b", "TH reads OptOutState attribute.", + "Verify value is 0x03 (OptOut)"), + TestStep("15c", "TH reads PowerAdjustmentCapability attribute.", + "Value has to include Cause=NoAdjustment."), + TestStep("16", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Test Event Clear", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("16a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("16b", "TH reads OptOutState attribute.", + "Verify value is 0x00 (NoOptOut)"), + TestStep("17", "TH sends PowerAdjustRequest with Power=PowerAdjustmentCapability[0].MaxPower, Duration=PowerAdjustmentCapability[0].MinDuration, Cause=LocalOptimization.", + "Verify DUT responds with status SUCCESS(0x00) and event DEM.S.E00(PowerAdjustStart) sent"), + TestStep("17a", "TH reads ESAState attribute.", + "Verify value is 0x04 (PowerAdjustActive)"), + TestStep("17b", "TH reads PowerAdjustmentCapability attribute.", + "Value has to include Cause=LocalOptimizationAdjustment."), + TestStep("18", "Wait 10 seconds.", + "Event DEM.S.E01(PowerAdjustEnd) sent with Cause=NormalCompletion, Duration=10s, EnergyUse= a valid value"), + TestStep("18a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("18b", "TH reads PowerAdjustmentCapability attribute.", + "Value has to include Cause=NoAdjustment."), + TestStep("19", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Power Adjustment Test Event Clear", + "Verify DUT responds with status SUCCESS(0x00)"), + ] + + return steps + + @async_test_body + async def test_TC_DEM_2_2(self): + # pylint: disable=too-many-locals, too-many-statements + """Run the test steps.""" + min_duration = 10 + max_duration = 60 + + self.step("1") + # Commission DUT - already done + + # Subscribe to Events and when they are sent push them to a queue for checking later + events_callback = EventChangeCallback(Clusters.DeviceEnergyManagement) + await events_callback.start(self.default_controller, + self.dut_node_id, + self.matter_test_config.endpoint) + + self.step("2") + await self.check_test_event_triggers_enabled() + + self.step("3") + await self.send_test_event_trigger_power_adjustment() + + self.step("3a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("3b") + powerAdjustCapabilityStruct = await self.read_dem_attribute_expect_success(attribute="PowerAdjustmentCapability") + asserts.assert_greater_equal(len(powerAdjustCapabilityStruct.powerAdjustCapability), 1) + logging.info(powerAdjustCapabilityStruct) + asserts.assert_equal(powerAdjustCapabilityStruct.cause, + Clusters.DeviceEnergyManagement.Enums.PowerAdjustReasonEnum.kNoAdjustment) + + # we should expect powerAdjustCapabilityStruct to have multiple entries with different max powers, min powers, max and min durations + min_power = sys.maxsize + max_power = 0 + min_duration = sys.maxsize + max_duration = 0 + + for entry in powerAdjustCapabilityStruct.powerAdjustCapability: + min_power = min(min_power, entry.minPower) + max_power = max(max_power, entry.maxPower) + min_duration = min(min_duration, entry.minDuration) + max_duration = max(max_duration, entry.maxDuration) + + result = f"min_power {min_power} max_power {max_power} min_duration {min_duration} max_duration {max_duration}" + logging.info(result) + + self.step("3c") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kNoOptOut) + + self.step("4") + await self.send_power_adjustment_command(power=max_power, + duration=powerAdjustCapabilityStruct.powerAdjustCapability[0].minDuration, + cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization) + + event_data = events_callback.wait_for_event_report(Clusters.DeviceEnergyManagement.Events.PowerAdjustStart) + + self.step("4a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kPowerAdjustActive) + + self.step("4b") + powerAdjustCapabilityStruct = await self.read_dem_attribute_expect_success(attribute="PowerAdjustmentCapability") + asserts.assert_greater_equal(len(powerAdjustCapabilityStruct.powerAdjustCapability), 1) + asserts.assert_equal(powerAdjustCapabilityStruct.cause, + Clusters.DeviceEnergyManagement.Enums.PowerAdjustReasonEnum.kLocalOptimizationAdjustment) + + self.step("5") + await self.send_cancel_power_adjustment_command() + event_data = events_callback.wait_for_event_report(Clusters.DeviceEnergyManagement.Events.PowerAdjustEnd) + asserts.assert_equal(event_data.cause, Clusters.DeviceEnergyManagement.Enums.CauseEnum.kCancelled) + + self.step("5a") + powerAdjustCapabilityStruct = await self.read_dem_attribute_expect_success(attribute="PowerAdjustmentCapability") + asserts.assert_greater_equal(len(powerAdjustCapabilityStruct.powerAdjustCapability), 1) + asserts.assert_equal(powerAdjustCapabilityStruct.cause, + Clusters.DeviceEnergyManagement.Enums.PowerAdjustReasonEnum.kNoAdjustment) + + self.step("5b") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("6") + await self.send_cancel_power_adjustment_command(expected_status=Status.InvalidInState) + + self.step("7") + await self.send_power_adjustment_command(power=max_power + 1, + duration=min_duration, + cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, + expected_status=Status.ConstraintError) + + self.step("8") + await self.send_power_adjustment_command(power=min_power, + duration=max_duration + 1, + cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, + expected_status=Status.ConstraintError) + + self.step("9") + await self.send_power_adjustment_command(power=min_power - 1, + duration=max_duration, + cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, + expected_status=Status.ConstraintError) + + self.step("10") + await self.send_power_adjustment_command(power=max_power, + duration=min_duration - 1, + cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, + expected_status=Status.ConstraintError) + + self.step("11") + start = datetime.datetime.now() + await self.send_power_adjustment_command(power=powerAdjustCapabilityStruct.powerAdjustCapability[0].maxPower, + duration=min_duration, + cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization) + + event_data = events_callback.wait_for_event_report(Clusters.DeviceEnergyManagement.Events.PowerAdjustStart) + + self.step("11a") + powerAdjustCapabilityStruct = await self.read_dem_attribute_expect_success(attribute="PowerAdjustmentCapability") + asserts.assert_greater_equal(len(powerAdjustCapabilityStruct.powerAdjustCapability), 1) + asserts.assert_equal(powerAdjustCapabilityStruct.cause, + Clusters.DeviceEnergyManagement.Enums.PowerAdjustReasonEnum.kLocalOptimizationAdjustment) + + self.step("12") + await self.send_power_adjustment_command(power=powerAdjustCapabilityStruct.powerAdjustCapability[0].maxPower, + duration=min_duration, + cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization) + + # Wait 5 seconds for an event not to be reported + events_callback.wait_for_event_expect_no_report(5) + + self.step("12a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kPowerAdjustActive) + + self.step("12b") + powerAdjustCapabilityStruct = await self.read_dem_attribute_expect_success(attribute="PowerAdjustmentCapability") + asserts.assert_greater_equal(len(powerAdjustCapabilityStruct.powerAdjustCapability), 1) + asserts.assert_equal(powerAdjustCapabilityStruct.cause, + Clusters.DeviceEnergyManagement.Enums.PowerAdjustReasonEnum.kGridOptimizationAdjustment) + + self.step("13") + await self.send_test_event_trigger_user_opt_out_local() + + self.step("13a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kPowerAdjustActive) + + self.step("13b") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kLocalOptOut) + + self.step("14") + await self.send_power_adjustment_command(power=max_power, + duration=max_duration, + cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, + expected_status=Status.ConstraintError) + + self.step("15") + await self.send_test_event_trigger_user_opt_out_grid() + event_data = events_callback.wait_for_event_report(Clusters.DeviceEnergyManagement.Events.PowerAdjustEnd) + asserts.assert_equal(event_data.cause, Clusters.DeviceEnergyManagement.Enums.CauseEnum.kUserOptOut) + + # Allow 3s error margin as the CI build system can run out of CPU time + elapsed = datetime.datetime.now() - start + asserts.assert_less_equal(abs(elapsed.seconds - event_data.duration), 3) + asserts.assert_greater_equal(event_data.energyUse, 0) + + self.step("15a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("15b") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kOptOut) + + self.step("15c") + powerAdjustCapabilityStruct = await self.read_dem_attribute_expect_success(attribute="PowerAdjustmentCapability") + asserts.assert_equal(powerAdjustCapabilityStruct.cause, + Clusters.DeviceEnergyManagement.Enums.PowerAdjustReasonEnum.kNoAdjustment) + + self.step("16") + await self.send_test_event_trigger_user_opt_out_clear_all() + + self.step("16a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("16b") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kNoOptOut) + + self.step("17") + await self.send_power_adjustment_command(power=powerAdjustCapabilityStruct.powerAdjustCapability[0].maxPower, + duration=powerAdjustCapabilityStruct.powerAdjustCapability[0].minDuration, + cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, + expected_status=Status.Success) + event_data = events_callback.wait_for_event_report(Clusters.DeviceEnergyManagement.Events.PowerAdjustStart) + + self.step("17a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kPowerAdjustActive) + + self.step("17b") + powerAdjustCapabilityStruct = await self.read_dem_attribute_expect_success(attribute="PowerAdjustmentCapability") + asserts.assert_equal(powerAdjustCapabilityStruct.cause, + Clusters.DeviceEnergyManagement.Enums.PowerAdjustReasonEnum.kLocalOptimizationAdjustment) + + self.step("18") + time.sleep(10) + + event_data = events_callback.wait_for_event_report(Clusters.DeviceEnergyManagement.Events.PowerAdjustEnd) + asserts.assert_equal(event_data.duration, 10) + asserts.assert_equal(event_data.cause, Clusters.DeviceEnergyManagement.Enums.CauseEnum.kNormalCompletion) + asserts.assert_greater(event_data.energyUse, 0) + + self.step("18a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("18b") + powerAdjustCapabilityStruct = await self.read_dem_attribute_expect_success(attribute="PowerAdjustmentCapability") + asserts.assert_equal(powerAdjustCapabilityStruct.cause, + Clusters.DeviceEnergyManagement.Enums.PowerAdjustReasonEnum.kNoAdjustment) + + self.step("19") + await self.send_test_event_trigger_power_adjustment_clear() + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index 11b1dfb01c0760..0fe12e777bc866 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -256,11 +256,11 @@ def __call__(self, res: EventReadResult, transaction: SubscriptionTransaction): f'Got subscription report for event on cluster {self._expected_cluster}: {res.Data}') self._q.put(res) - def wait_for_event_report(self, expected_event: ClusterObjects.ClusterEvent, timeout: int = 10): - """This function allows a test script to block waiting for the specific event to arrive with a timeout. - It returns the event data so that the values can be checked.""" + def wait_for_event_report(self, expected_event: ClusterObjects.ClusterEvent, timeoutS: int = 10): + """This function allows a test script to block waiting for the specific event to arrive with a timeout + (specified in seconds). It returns the event data so that the values can be checked.""" try: - res = self._q.get(block=True, timeout=timeout) + res = self._q.get(block=True, timeout=timeoutS) except queue.Empty: asserts.fail("Failed to receive a report for the event {}".format(expected_event)) @@ -268,6 +268,16 @@ def wait_for_event_report(self, expected_event: ClusterObjects.ClusterEvent, tim asserts.assert_equal(res.Header.EventId, expected_event.event_id, "Expected event ID not found in event report") return res.Data + def wait_for_event_expect_no_report(self, timeoutS: int = 10): + """This function succceeds/returns if an event does not arrive within the timeout specified in seconds. + If an event does arrive, an assert is called.""" + try: + res = self._q.get(block=True, timeout=timeoutS) + except queue.Empty: + return + + asserts.fail(f"Event reported when not expected {res}") + @property def event_queue(self) -> queue.Queue: return self._q From 20c251ba0c2fe6560b580b39964a6f74bbce4f29 Mon Sep 17 00:00:00 2001 From: PeterC1965 <101805108+PeterC1965@users.noreply.github.com> Date: Fri, 26 Jul 2024 02:00:03 +0100 Subject: [PATCH 20/49] Add water heater mode to sdk and all-clusters-app (#34351) * Add water heater mode cluster * Get all-clusters-app building with water-heater-mode * Restyled by clang-format * Added call to WaterHeaterMode::Shutdown() to fix IntrusiveList not being freed at exit. * Added missing #include "water-heater-mode.h" to main-common.cpp * Regenerated files * Update following review comments from Andrei Litvin * Fix compilation error * Restyled by clang-format * Update after review comment from Boris * WaterHeaterMode moved to future section * Update examples/all-clusters-app/all-clusters-common/include/water-heater-mode.h Co-authored-by: Andrei Litvin * Address final set of review comments from Andrei --------- Co-authored-by: Restyled.io Co-authored-by: jamesharrow <93921463+jamesharrow@users.noreply.github.com> Co-authored-by: James Harrow Co-authored-by: Andrei Litvin --- .../all-clusters-app.matter | 66 ++++++ .../all-clusters-common/all-clusters-app.zap | 188 ++++++++++++++++++ .../include/water-heater-mode.h | 73 +++++++ .../src/water-heater-mode.cpp | 105 ++++++++++ examples/all-clusters-app/linux/BUILD.gn | 1 + .../all-clusters-app/linux/main-common.cpp | 2 + src/app/common/templates/config-data.yaml | 2 + src/app/util/util.cpp | 1 + src/app/zap_cluster_list.json | 2 + .../data_model/controller-clusters.zap | 60 ++++++ .../python/chip/clusters/Objects.py | 10 +- .../app-common/zap-generated/callback.h | 6 - .../zap-generated/cluster-enums-check.h | 14 -- .../app-common/zap-generated/cluster-enums.h | 10 +- 14 files changed, 510 insertions(+), 30 deletions(-) create mode 100644 examples/all-clusters-app/all-clusters-common/include/water-heater-mode.h create mode 100644 examples/all-clusters-app/all-clusters-common/src/water-heater-mode.cpp diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter index b8855227a52db1..4313908d4fcdcb 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter @@ -4582,6 +4582,56 @@ cluster EnergyEvseMode = 157 { command ChangeToMode(ChangeToModeRequest): ChangeToModeResponse = 0; } +/** Attributes and commands for selecting a mode from a list of supported options. */ +cluster WaterHeaterMode = 158 { + revision 1; + + enum ModeTag : enum16 { + kOff = 16384; + kManual = 16385; + kTimed = 16386; + } + + bitmap Feature : bitmap32 { + kOnOff = 0x1; + } + + struct ModeTagStruct { + optional vendor_id mfgCode = 0; + enum16 value = 1; + } + + struct ModeOptionStruct { + char_string<64> label = 0; + int8u mode = 1; + ModeTagStruct modeTags[] = 2; + } + + readonly attribute ModeOptionStruct supportedModes[] = 0; + readonly attribute int8u currentMode = 1; + attribute optional nullable int8u startUpMode = 2; + attribute optional nullable int8u onMode = 3; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct ChangeToModeRequest { + int8u newMode = 0; + } + + response struct ChangeToModeResponse = 1 { + enum8 status = 0; + optional char_string statusText = 1; + } + + /** This command is used to change device modes. + On receipt of this command the device SHALL respond with a ChangeToModeResponse command. */ + command ChangeToMode(ChangeToModeRequest): ChangeToModeResponse = 0; +} + /** Attributes and commands for selecting a mode from a list of supported options. */ provisional cluster DeviceEnergyManagementMode = 159 { revision 1; @@ -8479,6 +8529,22 @@ endpoint 1 { handle command ChangeToModeResponse; } + server cluster WaterHeaterMode { + callback attribute supportedModes; + callback attribute currentMode; + ram attribute startUpMode; + ram attribute onMode; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + callback attribute featureMap; + ram attribute clusterRevision default = 1; + + handle command ChangeToMode; + handle command ChangeToModeResponse; + } + server cluster DeviceEnergyManagementMode { callback attribute supportedModes; callback attribute currentMode; diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap index 6491378c9e83d7..3c9dcb094f8eb4 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap @@ -14745,6 +14745,194 @@ } ] }, + { + "name": "Water Heater Mode", + "code": 158, + "mfgCode": null, + "define": "WATER_HEATER_MODE_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "ChangeToMode", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "ChangeToModeResponse", + "code": 1, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "SupportedModes", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "CurrentMode", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "StartUpMode", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "OnMode", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EventList", + "code": 65530, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, { "name": "Device Energy Management Mode", "code": 159, diff --git a/examples/all-clusters-app/all-clusters-common/include/water-heater-mode.h b/examples/all-clusters-app/all-clusters-common/include/water-heater-mode.h new file mode 100644 index 00000000000000..9f3515094ed255 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/include/water-heater-mode.h @@ -0,0 +1,73 @@ +/* + * + * 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. + */ + +#pragma once + +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { + +namespace WaterHeaterMode { + +constexpr uint8_t kModeOff = 0; +constexpr uint8_t kModeManual = 1; +constexpr uint8_t kModeTimed = 2; + +/// This is an application level delegate to handle WaterHeaterMode commands according to the specific business logic. +class ExampleWaterHeaterModeDelegate : public ModeBase::Delegate +{ +private: + using ModeTagStructType = detail::Structs::ModeTagStruct::Type; + ModeTagStructType modeTagsOff[1] = { { .value = to_underlying(ModeTag::kOff) } }; + ModeTagStructType modeTagsManual[1] = { { .value = to_underlying(ModeTag::kManual) } }; + ModeTagStructType modeTagsTimed[1] = { { .value = to_underlying(ModeTag::kTimed) } }; + + const detail::Structs::ModeOptionStruct::Type kModeOptions[3] = { + detail::Structs::ModeOptionStruct::Type{ + .label = "Off"_span, .mode = kModeOff, .modeTags = DataModel::List(modeTagsOff) }, + detail::Structs::ModeOptionStruct::Type{ + .label = "Manual"_span, .mode = kModeManual, .modeTags = DataModel::List(modeTagsManual) }, + detail::Structs::ModeOptionStruct::Type{ + .label = "Timed"_span, .mode = kModeTimed, .modeTags = DataModel::List(modeTagsTimed) } + }; + + CHIP_ERROR Init() override; + void HandleChangeToMode(uint8_t mode, ModeBase::Commands::ChangeToModeResponse::Type & response) override; + + CHIP_ERROR GetModeLabelByIndex(uint8_t modeIndex, MutableCharSpan & label) override; + CHIP_ERROR GetModeValueByIndex(uint8_t modeIndex, uint8_t & value) override; + CHIP_ERROR GetModeTagsByIndex(uint8_t modeIndex, DataModel::List & tags) override; + +public: + ~ExampleWaterHeaterModeDelegate() override = default; +}; + +ModeBase::Instance * Instance(); + +void Shutdown(); + +} // namespace WaterHeaterMode + +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/all-clusters-app/all-clusters-common/src/water-heater-mode.cpp b/examples/all-clusters-app/all-clusters-common/src/water-heater-mode.cpp new file mode 100644 index 00000000000000..9c4121fb70d668 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/src/water-heater-mode.cpp @@ -0,0 +1,105 @@ +/* + * + * 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 + +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::WaterHeaterMode; +using chip::Protocols::InteractionModel::Status; +template +using List = chip::app::DataModel::List; +using ModeTagStructType = chip::app::Clusters::detail::Structs::ModeTagStruct::Type; + +static ExampleWaterHeaterModeDelegate * gWaterHeaterModeDelegate = nullptr; +static ModeBase::Instance * gWaterHeaterModeInstance = nullptr; + +CHIP_ERROR ExampleWaterHeaterModeDelegate::Init() +{ + return CHIP_NO_ERROR; +} + +// todo refactor code by making a parent class for all ModeInstance classes to reduce flash usage. +void ExampleWaterHeaterModeDelegate::HandleChangeToMode(uint8_t NewMode, ModeBase::Commands::ChangeToModeResponse::Type & response) +{ + response.status = to_underlying(ModeBase::StatusCode::kSuccess); +} + +CHIP_ERROR ExampleWaterHeaterModeDelegate::GetModeLabelByIndex(uint8_t modeIndex, chip::MutableCharSpan & label) +{ + if (modeIndex >= ArraySize(kModeOptions)) + { + return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; + } + return chip::CopyCharSpanToMutableCharSpan(kModeOptions[modeIndex].label, label); +} + +CHIP_ERROR ExampleWaterHeaterModeDelegate::GetModeValueByIndex(uint8_t modeIndex, uint8_t & value) +{ + if (modeIndex >= ArraySize(kModeOptions)) + { + return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; + } + value = kModeOptions[modeIndex].mode; + return CHIP_NO_ERROR; +} + +CHIP_ERROR ExampleWaterHeaterModeDelegate::GetModeTagsByIndex(uint8_t modeIndex, List & tags) +{ + if (modeIndex >= ArraySize(kModeOptions)) + { + return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; + } + + if (tags.size() < kModeOptions[modeIndex].modeTags.size()) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + std::copy(kModeOptions[modeIndex].modeTags.begin(), kModeOptions[modeIndex].modeTags.end(), tags.begin()); + tags.reduce_size(kModeOptions[modeIndex].modeTags.size()); + + return CHIP_NO_ERROR; +} + +ModeBase::Instance * WaterHeaterMode::Instance() +{ + return gWaterHeaterModeInstance; +} + +void WaterHeaterMode::Shutdown() +{ + if (gWaterHeaterModeInstance != nullptr) + { + delete gWaterHeaterModeInstance; + gWaterHeaterModeInstance = nullptr; + } + if (gWaterHeaterModeDelegate != nullptr) + { + delete gWaterHeaterModeDelegate; + gWaterHeaterModeDelegate = nullptr; + } +} + +void emberAfWaterHeaterModeClusterInitCallback(chip::EndpointId endpointId) +{ + VerifyOrDie(gWaterHeaterModeDelegate == nullptr && gWaterHeaterModeInstance == nullptr); + gWaterHeaterModeDelegate = new WaterHeaterMode::ExampleWaterHeaterModeDelegate; + gWaterHeaterModeInstance = + new ModeBase::Instance(gWaterHeaterModeDelegate, endpointId, WaterHeaterMode::Id, chip::to_underlying(Feature::kOnOff)); + gWaterHeaterModeInstance->Init(); +} diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index e71574ce4db5ac..ca1149b25e7891 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -57,6 +57,7 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-modes-manager.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/tcc-mode.cpp", + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/water-heater-mode.cpp", "${chip_root}/examples/all-clusters-app/linux/diagnostic-logs-provider-delegate-impl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", diff --git a/examples/all-clusters-app/linux/main-common.cpp b/examples/all-clusters-app/linux/main-common.cpp index 73af031ecbdb5d..1d1799ece3581b 100644 --- a/examples/all-clusters-app/linux/main-common.cpp +++ b/examples/all-clusters-app/linux/main-common.cpp @@ -37,6 +37,7 @@ #include "rvc-modes.h" #include "rvc-operational-state-delegate-impl.h" #include "tcc-mode.h" +#include "water-heater-mode.h" #include #include #include @@ -259,6 +260,7 @@ void ApplicationShutdown() Clusters::DeviceEnergyManagementMode::Shutdown(); Clusters::EnergyEvseMode::Shutdown(); + Clusters::WaterHeaterMode::Shutdown(); if (sChipNamedPipeCommands.Stop() != CHIP_NO_ERROR) { diff --git a/src/app/common/templates/config-data.yaml b/src/app/common/templates/config-data.yaml index fd060149c8ff20..a3ae2dc621fd03 100644 --- a/src/app/common/templates/config-data.yaml +++ b/src/app/common/templates/config-data.yaml @@ -13,6 +13,7 @@ EnumsNotUsedAsTypeInXML: - "RvcOperationalState::OperationalStateEnum" - "RvcOperationalState::ErrorStateEnum" - "EnergyEvseMode::ModeTag" + - "WaterHeaterMode::ModeTag" - "DeviceEnergyManagementMode::ModeTag" CommandHandlerInterfaceOnlyClusters: @@ -42,6 +43,7 @@ CommandHandlerInterfaceOnlyClusters: - Electrical Energy Measurement - Wi-Fi Network Management - Thread Network Directory + - Water Heater Mode # We need a more configurable way of deciding which clusters have which init functions.... # See https://github.com/project-chip/connectedhomeip/issues/4369 diff --git a/src/app/util/util.cpp b/src/app/util/util.cpp index b485e7c5dfb4cb..6a23c48a43f6bf 100644 --- a/src/app/util/util.cpp +++ b/src/app/util/util.cpp @@ -139,6 +139,7 @@ void MatterEnergyEvseModePluginServerInitCallback() {} void MatterPowerTopologyPluginServerInitCallback() {} void MatterElectricalEnergyMeasurementPluginServerInitCallback() {} void MatterElectricalPowerMeasurementPluginServerInitCallback() {} +void MatterWaterHeaterModePluginServerInitCallback() {} bool emberAfContainsAttribute(chip::EndpointId endpoint, chip::ClusterId clusterId, chip::AttributeId attributeId) { diff --git a/src/app/zap_cluster_list.json b/src/app/zap_cluster_list.json index 00bcb55adb90a7..c17a59678697a8 100644 --- a/src/app/zap_cluster_list.json +++ b/src/app/zap_cluster_list.json @@ -131,6 +131,7 @@ "WAKE_ON_LAN_CLUSTER": [], "LAUNDRY_WASHER_CONTROLS_CLUSTER": [], "LAUNDRY_DRYER_CONTROLS_CLUSTER": [], + "WATER_HEATER_MODE_CLUSTER": [], "WIFI_NETWORK_DIAGNOSTICS_CLUSTER": [], "WINDOW_COVERING_CLUSTER": [], "ZLL_COMMISSIONING_CLUSTER": [] @@ -314,6 +315,7 @@ "WIFI_NETWORK_DIAGNOSTICS_CLUSTER": ["wifi-network-diagnostics-server"], "WIFI_NETWORK_MANAGEMENT_CLUSTER": ["wifi-network-management-server"], "WINDOW_COVERING_CLUSTER": ["window-covering-server"], + "WATER_HEATER_MODE_CLUSTER": ["mode-base-server"], "ZLL_COMMISSIONING_CLUSTER": [] } } diff --git a/src/controller/data_model/controller-clusters.zap b/src/controller/data_model/controller-clusters.zap index 97d960bb7f4d30..71f77c8d6159b5 100644 --- a/src/controller/data_model/controller-clusters.zap +++ b/src/controller/data_model/controller-clusters.zap @@ -3394,6 +3394,66 @@ } ] }, + { + "name": "Water Heater Mode", + "code": 158, + "mfgCode": null, + "define": "WATER_HEATER_MODE_CLUSTER", + "side": "client", + "enabled": 1, + "commands": [ + { + "name": "ChangeToMode", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "ChangeToModeResponse", + "code": 1, + "mfgCode": null, + "source": "server", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "client", + "type": "bitmap32", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "client", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, { "name": "Device Energy Management Mode", "code": 159, diff --git a/src/controller/python/chip/clusters/Objects.py b/src/controller/python/chip/clusters/Objects.py index 13c6fe3d9f4dab..654a6d292a6910 100644 --- a/src/controller/python/chip/clusters/Objects.py +++ b/src/controller/python/chip/clusters/Objects.py @@ -26894,11 +26894,11 @@ class ModeTag(MatterIntEnum): kOff = 0x4000 kManual = 0x4001 kTimed = 0x4002 - # All received enum values that are not listed above will be mapped - # to kUnknownEnumValue. This is a helper enum value that should only - # be used by code to process how it handles receiving and unknown - # enum value. This specific should never be transmitted. - kUnknownEnumValue = 0, + # kUnknownEnumValue intentionally not defined. This enum never goes + # through DataModel::Decode, likely because it is a part of a derived + # cluster. As a result having kUnknownEnumValue in this enum is error + # prone, and was removed. See + # src/app/common/templates/config-data.yaml. class Bitmaps: class Feature(IntFlag): diff --git a/zzz_generated/app-common/app-common/zap-generated/callback.h b/zzz_generated/app-common/app-common/zap-generated/callback.h index bb2d5a3f3dd501..03f0b7bbef238e 100644 --- a/zzz_generated/app-common/app-common/zap-generated/callback.h +++ b/zzz_generated/app-common/app-common/zap-generated/callback.h @@ -6071,12 +6071,6 @@ bool emberAfMessagesClusterPresentMessagesRequestCallback( bool emberAfMessagesClusterCancelMessagesRequestCallback( chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, const chip::app::Clusters::Messages::Commands::CancelMessagesRequest::DecodableType & commandData); -/** - * @brief Water Heater Mode Cluster ChangeToMode Command callback (from client) - */ -bool emberAfWaterHeaterModeClusterChangeToModeCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::WaterHeaterMode::Commands::ChangeToMode::DecodableType & commandData); /** * @brief Door Lock Cluster LockDoor Command callback (from client) */ diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h b/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h index 13951c0ff2bc00..0304b683406b8b 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h @@ -1895,20 +1895,6 @@ static auto __attribute__((unused)) EnsureKnownEnumValue(EnergyPreference::Energ } } -static auto __attribute__((unused)) EnsureKnownEnumValue(WaterHeaterMode::ModeTag val) -{ - using EnumType = WaterHeaterMode::ModeTag; - switch (val) - { - case EnumType::kOff: - case EnumType::kManual: - case EnumType::kTimed: - return val; - default: - return EnumType::kUnknownEnumValue; - } -} - static auto __attribute__((unused)) EnsureKnownEnumValue(DoorLock::AlarmCodeEnum val) { using EnumType = DoorLock::AlarmCodeEnum; diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h index fd0f8d24d26dc6..612637d3af1776 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h @@ -2823,11 +2823,11 @@ enum class ModeTag : uint16_t kOff = 0x4000, kManual = 0x4001, kTimed = 0x4002, - // All received enum values that are not listed above will be mapped - // to kUnknownEnumValue. This is a helper enum value that should only - // be used by code to process how it handles receiving and unknown - // enum value. This specific should never be transmitted. - kUnknownEnumValue = 0, + // kUnknownEnumValue intentionally not defined. This enum never goes + // through DataModel::Decode, likely because it is a part of a derived + // cluster. As a result having kUnknownEnumValue in this enum is error + // prone, and was removed. See + // src/app/common/templates/config-data.yaml. }; // Bitmap for Feature From 10d653ddf42665df5a8bda8cb9ef61bcc29584dd Mon Sep 17 00:00:00 2001 From: lpbeliveau-silabs <112982107+lpbeliveau-silabs@users.noreply.github.com> Date: Thu, 25 Jul 2024 21:52:54 -0400 Subject: [PATCH 21/49] [OO] Scenes Integration Tests (#34447) * Added test for scenes integration in onoff cluster * Made onoff and scenes behaviors independant in scenes interaction * Added the test to the CI --- .../clusters/level-control/level-control.cpp | 9 +- .../clusters/on-off-server/on-off-server.cpp | 15 +- .../suites/certification/Test_TC_OO_2_7.yaml | 269 ++++++++++++++++++ src/app/tests/suites/ciTests.json | 7 +- 4 files changed, 280 insertions(+), 20 deletions(-) create mode 100644 src/app/tests/suites/certification/Test_TC_OO_2_7.yaml diff --git a/src/app/clusters/level-control/level-control.cpp b/src/app/clusters/level-control/level-control.cpp index 814436c39e179a..738a7b10ed0ffb 100644 --- a/src/app/clusters/level-control/level-control.cpp +++ b/src/app/clusters/level-control/level-control.cpp @@ -256,12 +256,9 @@ class DefaultLevelControlSceneHandler : public scenes::DefaultSceneHandlerImpl if (!NumericAttributeTraits::IsNullValue(level)) { - CommandId command = LevelControlHasFeature(endpoint, LevelControl::Feature::kOnOff) ? Commands::MoveToLevelWithOnOff::Id - : Commands::MoveToLevel::Id; - - moveToLevelHandler(endpoint, command, level, DataModel::MakeNullable(static_cast(timeMs / 100)), - chip::Optional>(), chip::Optional>(), - INVALID_STORED_LEVEL); + moveToLevelHandler( + endpoint, Commands::MoveToLevel::Id, level, DataModel::MakeNullable(static_cast(timeMs / 100)), + chip::Optional>(1), chip::Optional>(1), INVALID_STORED_LEVEL); } return CHIP_NO_ERROR; diff --git a/src/app/clusters/on-off-server/on-off-server.cpp b/src/app/clusters/on-off-server/on-off-server.cpp index 726da2d29f7c4d..98dfaf9da02c3e 100644 --- a/src/app/clusters/on-off-server/on-off-server.cpp +++ b/src/app/clusters/on-off-server/on-off-server.cpp @@ -213,19 +213,8 @@ class DefaultOnOffSceneHandler : public scenes::DefaultSceneHandlerImpl return err; } - // This handler assumes it is being used with the default handler for the level control. Therefore if the level control - // cluster with on off feature is present on the endpoint and the level control handler is registered, it assumes this - // handler will take action on the on-off state. This assumes the level control attributes were also saved in the scene. - // This is to prevent a behavior where the on off state is set by this handler, and then the level control handler or vice - // versa. -#ifdef MATTER_DM_PLUGIN_LEVEL_CONTROL - if (!(LevelControlWithOnOffFeaturePresent(endpoint) && - ScenesManagement::ScenesServer::Instance().IsHandlerRegistered(endpoint, LevelControlServer::GetSceneHandler()))) -#endif - { - VerifyOrReturnError(mTransitionTimeInterface.sceneEventControl(endpoint) != nullptr, CHIP_ERROR_INVALID_ARGUMENT); - OnOffServer::Instance().scheduleTimerCallbackMs(mTransitionTimeInterface.sceneEventControl(endpoint), timeMs); - } + VerifyOrReturnError(mTransitionTimeInterface.sceneEventControl(endpoint) != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + OnOffServer::Instance().scheduleTimerCallbackMs(mTransitionTimeInterface.sceneEventControl(endpoint), timeMs); return CHIP_NO_ERROR; } diff --git a/src/app/tests/suites/certification/Test_TC_OO_2_7.yaml b/src/app/tests/suites/certification/Test_TC_OO_2_7.yaml new file mode 100644 index 00000000000000..19183ba79aff8d --- /dev/null +++ b/src/app/tests/suites/certification/Test_TC_OO_2_7.yaml @@ -0,0 +1,269 @@ +# 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. + +name: 4.2.4. [TC-OO-2.7] Scenes Management Cluster Interaction (DUT as Server) + +PICS: + - OO.S + - S.S + +config: + nodeId: 0x12344321 + cluster: "Scenes Management" + endpoint: 1 + G1: + type: group_id + defaultValue: 0x0001 + +tests: + - label: "Wait for the commissioned device to be retrieved" + cluster: "DelayCommands" + command: "WaitForCommissionee" + arguments: + values: + - name: "nodeId" + value: nodeId + + - label: + "Step 0a :TH sends KeySetWrite command in the GroupKeyManagement + cluster to DUT using a key that is pre-installed on the TH. + GroupKeySet fields are as follows:" + cluster: "Group Key Management" + endpoint: 0 + command: "KeySetWrite" + arguments: + values: + - name: "GroupKeySet" + value: + { + GroupKeySetID: 0x01a1, + GroupKeySecurityPolicy: 0, + EpochKey0: "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf", + EpochStartTime0: 1110000, + EpochKey1: "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf", + EpochStartTime1: 1110001, + EpochKey2: "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", + EpochStartTime2: 1110002, + } + + - label: + "Step 0b: TH binds GroupIds 0x0001 and 0x0002 with GroupKeySetID + 0x01a1 in the GroupKeyMap attribute list on GroupKeyManagement cluster + by writing the GroupKeyMap attribute with two entries as follows:" + cluster: "Group Key Management" + endpoint: 0 + command: "writeAttribute" + attribute: "GroupKeyMap" + arguments: + value: [{ FabricIndex: 1, GroupId: G1, GroupKeySetID: 0x01a1 }] + + - label: "Step 0c: TH sends a RemoveAllGroups command to DUT." + cluster: "Groups" + endpoint: endpoint + command: "RemoveAllGroups" + + - label: + "Step 1a: TH sends a AddGroup command to DUT with the GroupID field + set to G1." + cluster: "Groups" + command: "AddGroup" + arguments: + values: + - name: "GroupID" + value: G1 + - name: "GroupName" + value: "Group1" + response: + values: + - name: "Status" + value: 0 + - name: "GroupID" + value: G1 + + - label: + "Step 1b: TH sends a RemoveAllScenes command to DUT with the GroupID + field set to G1." + command: "RemoveAllScenes" + arguments: + values: + - name: "GroupID" + value: G1 + response: + values: + - name: "Status" + value: 0x00 + - name: "GroupID" + value: G1 + - label: + "Step 1c: TH sends a GetSceneMembership command to DUT with the + GroupID field set to G1." + command: "GetSceneMembership" + arguments: + values: + - name: "GroupID" + value: G1 + response: + values: + - name: "Status" + value: 0x00 + - name: "GroupID" + value: G1 + - name: "SceneList" + value: [] + + - label: "Step 2a: TH sends Off command to DUT" + cluster: "On/Off" + command: "Off" + + - label: "Wait 1000ms" + cluster: "DelayCommands" + command: "WaitForMs" + arguments: + values: + - name: "ms" + value: 1000 + + - label: "Step 2b: after a few seconds, TH reads OnOff attribute from DUT" + cluster: "On/Off" + command: "readAttribute" + attribute: "OnOff" + response: + value: 0 + + - label: + "Step 2b: TH sends a StoreScene command to DUT with the GroupID field + set to G1 and the SceneID field set to 0x01." + command: "StoreScene" + arguments: + values: + - name: "GroupID" + value: G1 + - name: "SceneID" + value: 0x01 + response: + values: + - name: "Status" + value: 0x00 + - name: "GroupID" + value: G1 + - name: "SceneID" + value: 0x01 + + - label: "Step 3: TH sends a AddScene command to DUT with the GroupID field + set to G1, the SceneID field set to 0x02, the TransitionTime field set + to 1000 (1s) and the ExtensionFieldSets set to: '[{ ClusterID: 0x0006, + AttributeValueList: [{ AttributeID: 0x0000, ValueUnsigned8: 0x01 }]}]' + " + command: "AddScene" + arguments: + values: + - name: "GroupID" + value: G1 + - name: "SceneID" + value: 0x02 + - name: "TransitionTime" + value: 1000 + - name: "SceneName" + value: "Scene1" + - name: "ExtensionFieldSets" + value: + [ + { + ClusterID: 0x0006, + AttributeValueList: + [{ AttributeID: 0x0000, ValueUnsigned8: 0x01 }], + }, + ] + response: + values: + - name: "Status" + value: 0x00 + - name: "GroupID" + value: G1 + - name: "SceneID" + value: 0x02 + + - label: + "Step 4a: TH sends a RecallScene command to DUT with the GroupID field + set to G1 and the SceneID field set to 0x02." + PICS: S.S.C05.Rsp + command: "RecallScene" + arguments: + values: + - name: "GroupID" + value: G1 + - name: "SceneID" + value: 0x02 + + - label: "Wait 2000ms" + cluster: "DelayCommands" + command: "WaitForMs" + arguments: + values: + - name: "ms" + value: 2000 + + - label: "Step 4b: after a few seconds, TH reads OnOff attribute from DUT" + cluster: "On/Off" + command: "readAttribute" + attribute: "OnOff" + response: + value: 1 + + - label: + "Step 5a: TH sends a RecallScene command to DUT with the GroupID field + set to G1 and the SceneID field set to 0x01." + PICS: S.S.C05.Rsp + command: "RecallScene" + arguments: + values: + - name: "GroupID" + value: G1 + - name: "SceneID" + value: 0x01 + + - label: "Wait 1000ms" + cluster: "DelayCommands" + command: "WaitForMs" + arguments: + values: + - name: "ms" + value: 1000 + + - label: "Step 5b: after a few seconds, TH reads OnOff attribute from DUT" + cluster: "On/Off" + command: "readAttribute" + attribute: "OnOff" + response: + value: 0 + + - label: + "Cleanup: TH sends a RemoveAllScenes command to DUT with the GroupID + field set to G1." + command: "RemoveAllScenes" + arguments: + values: + - name: "GroupID" + value: G1 + response: + values: + - name: "Status" + value: 0x00 + - name: "GroupID" + value: G1 + + - label: "Cleanup: TH sends a RemoveAllGroups command to DUT." + cluster: "Groups" + endpoint: endpoint + command: "RemoveAllGroups" diff --git a/src/app/tests/suites/ciTests.json b/src/app/tests/suites/ciTests.json index a77ecb9a3b2838..44cfcd1648a70d 100644 --- a/src/app/tests/suites/ciTests.json +++ b/src/app/tests/suites/ciTests.json @@ -144,7 +144,12 @@ "ModeSelect": [], "MultipleFabrics": [], "OTASoftwareUpdate": ["OTA_SuccessfulTransfer"], - "OnOff": ["Test_TC_OO_2_1", "Test_TC_OO_2_2", "Test_TC_OO_2_4"], + "OnOff": [ + "Test_TC_OO_2_1", + "Test_TC_OO_2_2", + "Test_TC_OO_2_4", + "Test_TC_OO_2_7" + ], "PowerSource": ["Test_TC_PS_2_1"], "PowerTopology": ["Test_TC_PWRTL_1_1"], "PressureMeasurement": ["Test_TC_PRS_2_1", "Test_TC_PRS_2_2"], From f53e2f9c4e4abf70af7b7c81f9ea41455d542168 Mon Sep 17 00:00:00 2001 From: lpbeliveau-silabs <112982107+lpbeliveau-silabs@users.noreply.github.com> Date: Thu, 25 Jul 2024 22:06:21 -0400 Subject: [PATCH 22/49] [LC] LC Scenes Integration Tests (#34448) * Added test for scenes integration to Level control cluster * Removed interaction between OnOff and Level control from the test * Added new test to test plan * Update src/app/tests/suites/certification/Test_TC_LVL_9_1.yaml Co-authored-by: C Freeman * Added cleanup --------- Co-authored-by: C Freeman --- .../suites/certification/Test_TC_LVL_9_1.yaml | 264 ++++++++++++++++++ src/app/tests/suites/ciTests.json | 3 +- 2 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 src/app/tests/suites/certification/Test_TC_LVL_9_1.yaml diff --git a/src/app/tests/suites/certification/Test_TC_LVL_9_1.yaml b/src/app/tests/suites/certification/Test_TC_LVL_9_1.yaml new file mode 100644 index 00000000000000..36da12403f5e9d --- /dev/null +++ b/src/app/tests/suites/certification/Test_TC_LVL_9_1.yaml @@ -0,0 +1,264 @@ +# 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. + +name: 4.2.4. [TC-LVL-9.1] Scenes Management Cluster Interaction (DUT as Server) + +PICS: + - LVL.S + - S.S + +config: + nodeId: 0x12344321 + cluster: "Scenes Management" + endpoint: 1 + G1: + type: group_id + defaultValue: 0x0001 + +tests: + - label: "Wait for the commissioned device to be retrieved" + cluster: "DelayCommands" + command: "WaitForCommissionee" + arguments: + values: + - name: "nodeId" + value: nodeId + + - label: + "Step 0a :TH sends KeySetWrite command in the GroupKeyManagement + cluster to DUT using a key that is pre-installed on the TH. + GroupKeySet fields are as follows:" + cluster: "Group Key Management" + endpoint: 0 + command: "KeySetWrite" + arguments: + values: + - name: "GroupKeySet" + value: + { + GroupKeySetID: 0x01a1, + GroupKeySecurityPolicy: 0, + EpochKey0: "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf", + EpochStartTime0: 1110000, + EpochKey1: "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf", + EpochStartTime1: 1110001, + EpochKey2: "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", + EpochStartTime2: 1110002, + } + + - label: + "Step 0b: TH binds GroupIds 0x0001 and 0x0002 with GroupKeySetID + 0x01a1 in the GroupKeyMap attribute list on GroupKeyManagement cluster + by writing the GroupKeyMap attribute with two entries as follows:" + cluster: "Group Key Management" + endpoint: 0 + command: "writeAttribute" + attribute: "GroupKeyMap" + arguments: + value: [{ FabricIndex: 1, GroupId: G1, GroupKeySetID: 0x01a1 }] + + - label: "Step 0c: TH sends a RemoveAllGroups command to DUT." + cluster: "Groups" + endpoint: endpoint + command: "RemoveAllGroups" + + - label: + "Step 1a: TH sends a AddGroup command to DUT with the GroupID field + set to G1." + cluster: "Groups" + command: "AddGroup" + arguments: + values: + - name: "GroupID" + value: G1 + - name: "GroupName" + value: "Group1" + response: + values: + - name: "Status" + value: 0 + - name: "GroupID" + value: G1 + + - label: + "Step 1b: TH sends a RemoveAllScenes command to DUT with the GroupID + field set to G1." + command: "RemoveAllScenes" + arguments: + values: + - name: "GroupID" + value: G1 + response: + values: + - name: "Status" + value: 0x00 + - name: "GroupID" + value: G1 + - label: + "Step 1c: TH sends a GetSceneMembership command to DUT with the + GroupID field set to G1." + command: "GetSceneMembership" + arguments: + values: + - name: "GroupID" + value: G1 + response: + values: + - name: "Status" + value: 0x00 + - name: "GroupID" + value: G1 + - name: "SceneList" + value: [] + + - label: + "Step 2a: TH sends a MoveToLevel command to DUT, with Level =0 and + TransitionTime =0 (immediate)" + cluster: "Level Control" + command: "MoveToLevel" + arguments: + values: + - name: "Level" + value: 0 + - name: "TransitionTime" + value: 0 + - name: "OptionsMask" + value: 1 + - name: "OptionsOverride" + value: 1 + + - label: "Step 2b: TH reads the MinLevel attribute from the DUT" + cluster: "Level Control" + command: "readAttribute" + attribute: "MinLevel" + response: + saveAs: MinLevelValue + constraints: + type: int8u + + - label: "Step 2c: TH reads the CurrentLevel attribute from DUT" + cluster: "Level Control" + command: "readAttribute" + attribute: "CurrentLevel" + response: + value: MinLevelValue + + - label: + "Step 3: TH sends a StoreScene command to DUT with the GroupID field + set to G1 and the SceneID field set to 0x01." + command: "StoreScene" + arguments: + values: + - name: "GroupID" + value: G1 + - name: "SceneID" + value: 0x01 + response: + values: + - name: "Status" + value: 0x00 + - name: "GroupID" + value: G1 + - name: "SceneID" + value: 0x01 + + - label: "Step 4: TH sends a AddScene command to DUT with the GroupID field + set to G1, the SceneID field set to 0x02, the TransitionTime field set + to 0 and the ExtensionFieldSets set to: '[{ ClusterID: 0x0008, + AttributeValueList: [{ AttributeID: 0x0000, ValueUnsigned8: 0x64 }]}]' + " + command: "AddScene" + arguments: + values: + - name: "GroupID" + value: G1 + - name: "SceneID" + value: 0x02 + - name: "TransitionTime" + value: 0 + - name: "SceneName" + value: "Scene1" + - name: "ExtensionFieldSets" + value: + [ + { + ClusterID: 0x0008, + AttributeValueList: + [{ AttributeID: 0x0000, ValueUnsigned8: 0x64 }], + }, + ] + response: + values: + - name: "Status" + value: 0x00 + - name: "GroupID" + value: G1 + - name: "SceneID" + value: 0x02 + + - label: + "Step 5a: TH sends a RecallScene command to DUT with the GroupID field + set to G1 and the SceneID field set to 0x02." + command: "RecallScene" + arguments: + values: + - name: "GroupID" + value: G1 + - name: "SceneID" + value: 0x02 + + - label: "Step 5b: TH reads the CurrentLevel attribute from DUT" + cluster: "Level Control" + command: "readAttribute" + attribute: "CurrentLevel" + response: + value: 0x64 + + - label: + "Step 6a: TH sends a RecallScene command to DUT with the GroupID field + set to G1 and the SceneID field set to 0x01." + command: "RecallScene" + arguments: + values: + - name: "GroupID" + value: G1 + - name: "SceneID" + value: 0x01 + + - label: "Step 6c: TH reads the CurrentLevel attribute from DUT" + cluster: "Level Control" + command: "readAttribute" + attribute: "CurrentLevel" + response: + value: MinLevelValue + + - label: + "Cleanup: TH sends a RemoveAllScenes command to DUT with the GroupID + field set to G1." + command: "RemoveAllScenes" + arguments: + values: + - name: "GroupID" + value: G1 + response: + values: + - name: "Status" + value: 0x00 + - name: "GroupID" + value: G1 + + - label: "Cleanup: TH sends a RemoveAllGroups command to DUT." + cluster: "Groups" + endpoint: endpoint + command: "RemoveAllGroups" diff --git a/src/app/tests/suites/ciTests.json b/src/app/tests/suites/ciTests.json index 44cfcd1648a70d..a96ef4aebc8fff 100644 --- a/src/app/tests/suites/ciTests.json +++ b/src/app/tests/suites/ciTests.json @@ -83,7 +83,8 @@ "Test_TC_LVL_4_1", "Test_TC_LVL_5_1", "Test_TC_LVL_6_1", - "Test_TC_LVL_7_1" + "Test_TC_LVL_7_1", + "Test_TC_LVL_9_1" ], "LocalizationConfiguration": [], "TimeFormatLocalization": ["Test_TC_LTIME_1_2", "Test_TC_LTIME_3_1"], From 942112753d5c3e5fa83bb6ee97c4da8b3a02624a Mon Sep 17 00:00:00 2001 From: Terence Hampson Date: Thu, 25 Jul 2024 22:22:24 -0400 Subject: [PATCH 23/49] Add XML definition for Ecosystem Information Cluster (#34291) --- .github/workflows/tests.yaml | 1 + docs/zap_clusters.md | 1 + scripts/rules.matterlint | 1 + src/app/zap-templates/zcl/data-model/all.xml | 1 + .../data-model/chip/descriptor-cluster.xml | 1 + .../chip/ecosystem-information-cluster.xml | 60 ++ .../zcl/data-model/chip/global-structs.xml | 1 + .../chip/semantic-tag-namespace-enums.xml | 1 + .../zcl/zcl-with-test-extensions.json | 1 + src/app/zap-templates/zcl/zcl.json | 1 + src/app/zap_cluster_list.json | 2 + .../data_model/controller-clusters.matter | 142 +++ .../chip/devicecontroller/ChipClusters.java | 296 ++++++ .../chip/devicecontroller/ChipStructs.java | 379 +++++++ .../devicecontroller/ClusterIDMapping.java | 106 ++ .../devicecontroller/ClusterInfoMapping.java | 156 +++ .../devicecontroller/ClusterReadMapping.java | 104 ++ .../devicecontroller/ClusterWriteMapping.java | 2 + .../chip/devicecontroller/cluster/files.gni | 4 + ...ystemInformationClusterDeviceTypeStruct.kt | 56 + ...InformationClusterEcosystemDeviceStruct.kt | 142 +++ ...formationClusterEcosystemLocationStruct.kt | 82 ++ ...temInformationClusterHomeLocationStruct.kt | 84 ++ .../clusters/EcosystemInformationCluster.kt | 969 ++++++++++++++++++ .../java/matter/controller/cluster/files.gni | 5 + ...ystemInformationClusterDeviceTypeStruct.kt | 56 + ...InformationClusterEcosystemDeviceStruct.kt | 142 +++ ...formationClusterEcosystemLocationStruct.kt | 82 ++ ...temInformationClusterHomeLocationStruct.kt | 84 ++ .../CHIPAttributeTLVValueDecoder.cpp | 455 ++++++++ .../CHIPEventTLVValueDecoder.cpp | 10 + .../python/chip/clusters/CHIPClusters.py | 64 ++ .../python/chip/clusters/Objects.py | 349 +++++++ .../MTRAttributeSpecifiedCheck.mm | 39 + .../MTRAttributeTLVValueDecoder.mm | 165 +++ .../CHIP/zap-generated/MTRBaseClusters.h | 177 ++++ .../CHIP/zap-generated/MTRBaseClusters.mm | 328 ++++++ .../CHIP/zap-generated/MTRClusterConstants.h | 12 + .../CHIP/zap-generated/MTRClusterNames.mm | 49 + .../CHIP/zap-generated/MTRClusters.h | 42 + .../CHIP/zap-generated/MTRClusters.mm | 49 + .../zap-generated/MTRCommandTimedCheck.mm | 12 + .../zap-generated/MTREventTLVValueDecoder.mm | 15 + .../CHIP/zap-generated/MTRStructsObjc.h | 33 + .../CHIP/zap-generated/MTRStructsObjc.mm | 147 +++ .../zap-generated/attributes/Accessors.cpp | 189 ++++ .../zap-generated/attributes/Accessors.h | 29 + .../app-common/zap-generated/callback.h | 43 + .../zap-generated/cluster-enums-check.h | 210 ++-- .../app-common/zap-generated/cluster-enums.h | 215 ++-- .../zap-generated/cluster-objects.cpp | 398 +++++-- .../zap-generated/cluster-objects.h | 300 +++++- .../app-common/zap-generated/ids/Attributes.h | 42 + .../app-common/zap-generated/ids/Clusters.h | 3 + .../zap-generated/cluster/Commands.h | 86 ++ .../cluster/ComplexArgumentParser.cpp | 270 +++-- .../cluster/ComplexArgumentParser.h | 32 +- .../cluster/logging/DataModelLogger.cpp | 287 ++++-- .../cluster/logging/DataModelLogger.h | 19 +- .../zap-generated/cluster/Commands.h | 849 +++++++++++++++ 60 files changed, 7391 insertions(+), 489 deletions(-) create mode 100644 src/app/zap-templates/zcl/data-model/chip/ecosystem-information-cluster.xml create mode 100644 src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterDeviceTypeStruct.kt create mode 100644 src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterEcosystemDeviceStruct.kt create mode 100644 src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt create mode 100644 src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt create mode 100644 src/controller/java/generated/java/matter/controller/cluster/clusters/EcosystemInformationCluster.kt create mode 100644 src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterDeviceTypeStruct.kt create mode 100644 src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterEcosystemDeviceStruct.kt create mode 100644 src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt create mode 100644 src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 182a3bb3a9fadd..554ee752ff2a08 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -127,6 +127,7 @@ jobs: src/app/zap-templates/zcl/data-model/chip/microwave-oven-mode-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/microwave-oven-control-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/door-lock-cluster.xml \ + src/app/zap-templates/zcl/data-model/chip/ecosystem-information-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/energy-evse-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/energy-evse-mode-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/ethernet-network-diagnostics-cluster.xml \ diff --git a/docs/zap_clusters.md b/docs/zap_clusters.md index ac9c8f04e7f86f..93e37131626379 100644 --- a/docs/zap_clusters.md +++ b/docs/zap_clusters.md @@ -132,6 +132,7 @@ Generally regenerate using one of: | 1294 | 0x50E | AccountLogin | | 1295 | 0x50F | ContentControl | | 1296 | 0x510 | ContentAppObserver | +| 1872 | 0x750 | EcosystemInformation | | 1873 | 0x751 | CommissionerControl | | 2820 | 0xB04 | ElectricalMeasurement | | 4294048773 | 0xFFF1FC05 | UnitTesting | diff --git a/scripts/rules.matterlint b/scripts/rules.matterlint index f44681febd112d..43b9b5887582b4 100644 --- a/scripts/rules.matterlint +++ b/scripts/rules.matterlint @@ -34,6 +34,7 @@ load "../src/app/zap-templates/zcl/data-model/chip/measurement-and-sensing.xml"; load "../src/app/zap-templates/zcl/data-model/chip/microwave-oven-mode-cluster.xml"; load "../src/app/zap-templates/zcl/data-model/chip/microwave-oven-control-cluster.xml"; load "../src/app/zap-templates/zcl/data-model/chip/door-lock-cluster.xml"; +load "../src/app/zap-templates/zcl/data-model/chip/ecosystem-information-cluster.xml"; load "../src/app/zap-templates/zcl/data-model/chip/energy-evse-cluster.xml"; load "../src/app/zap-templates/zcl/data-model/chip/energy-evse-mode-cluster.xml"; load "../src/app/zap-templates/zcl/data-model/chip/ethernet-network-diagnostics-cluster.xml"; diff --git a/src/app/zap-templates/zcl/data-model/all.xml b/src/app/zap-templates/zcl/data-model/all.xml index 6003a7cf4a3f1a..2a2f73ccfc1709 100644 --- a/src/app/zap-templates/zcl/data-model/all.xml +++ b/src/app/zap-templates/zcl/data-model/all.xml @@ -34,6 +34,7 @@ + diff --git a/src/app/zap-templates/zcl/data-model/chip/descriptor-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/descriptor-cluster.xml index 639b965cfc9bf9..d63dbeb05ca11a 100644 --- a/src/app/zap-templates/zcl/data-model/chip/descriptor-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/descriptor-cluster.xml @@ -19,6 +19,7 @@ limitations under the License. + diff --git a/src/app/zap-templates/zcl/data-model/chip/ecosystem-information-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/ecosystem-information-cluster.xml new file mode 100644 index 00000000000000..9a34cd3d02b20e --- /dev/null +++ b/src/app/zap-templates/zcl/data-model/chip/ecosystem-information-cluster.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + General + Ecosystem Information + 0x0750 + ECOSYSTEM_INFORMATION_CLUSTER + Provides extended device information for all the logical devices represented by a Bridged Node. + true + true + + + + RemovedOn + + + + DeviceDirectory + + + + LocationDirectory + + + + diff --git a/src/app/zap-templates/zcl/data-model/chip/global-structs.xml b/src/app/zap-templates/zcl/data-model/chip/global-structs.xml index c68c4aa1d000d7..777630ec32b06d 100644 --- a/src/app/zap-templates/zcl/data-model/chip/global-structs.xml +++ b/src/app/zap-templates/zcl/data-model/chip/global-structs.xml @@ -25,6 +25,7 @@ TODO: Make these structures global rather than defining them for each cluster. + diff --git a/src/app/zap-templates/zcl/data-model/chip/semantic-tag-namespace-enums.xml b/src/app/zap-templates/zcl/data-model/chip/semantic-tag-namespace-enums.xml index a1572523bcb0b3..8e6d9237e29d90 100644 --- a/src/app/zap-templates/zcl/data-model/chip/semantic-tag-namespace-enums.xml +++ b/src/app/zap-templates/zcl/data-model/chip/semantic-tag-namespace-enums.xml @@ -44,6 +44,7 @@ TODO: Make these namespace enums global rather than defining them for each clust + diff --git a/src/app/zap-templates/zcl/zcl-with-test-extensions.json b/src/app/zap-templates/zcl/zcl-with-test-extensions.json index 513144d3d32ad9..b06fc9131f3eee 100644 --- a/src/app/zap-templates/zcl/zcl-with-test-extensions.json +++ b/src/app/zap-templates/zcl/zcl-with-test-extensions.json @@ -48,6 +48,7 @@ "microwave-oven-mode-cluster.xml", "microwave-oven-control-cluster.xml", "door-lock-cluster.xml", + "ecosystem-information-cluster.xml", "energy-preference-cluster.xml", "electrical-energy-measurement-cluster.xml", "electrical-measurement-cluster.xml", diff --git a/src/app/zap-templates/zcl/zcl.json b/src/app/zap-templates/zcl/zcl.json index b497281e585578..921b2022f40f8a 100644 --- a/src/app/zap-templates/zcl/zcl.json +++ b/src/app/zap-templates/zcl/zcl.json @@ -46,6 +46,7 @@ "microwave-oven-mode-cluster.xml", "door-lock-cluster.xml", "drlc-cluster.xml", + "ecosystem-information-cluster.xml", "electrical-energy-measurement-cluster.xml", "electrical-measurement-cluster.xml", "electrical-power-measurement-cluster.xml", diff --git a/src/app/zap_cluster_list.json b/src/app/zap_cluster_list.json index c17a59678697a8..285bd6e6398b73 100644 --- a/src/app/zap_cluster_list.json +++ b/src/app/zap_cluster_list.json @@ -37,6 +37,7 @@ "DISHWASHER_MODE_CLUSTER": [], "MICROWAVE_OVEN_MODE_CLUSTER": [], "DOOR_LOCK_CLUSTER": [], + "ECOSYSTEM_INFORMATION_CLUSTER": [], "ELECTRICAL_ENERGY_MEASUREMENT_CLUSTER": [], "ELECTRICAL_MEASUREMENT_CLUSTER": [], "ELECTRICAL_POWER_MEASUREMENT_CLUSTER": [], @@ -185,6 +186,7 @@ "DISHWASHER_MODE_CLUSTER": ["mode-base-server"], "MICROWAVE_OVEN_MODE_CLUSTER": ["mode-base-server"], "DOOR_LOCK_CLUSTER": ["door-lock-server"], + "ECOSYSTEM_INFORMATION_CLUSTER": [], "ELECTRICAL_ENERGY_MEASUREMENT_CLUSTER": [ "electrical-energy-measurement-server" ], diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter index 39a4d56bfd69b8..a1ed710504df8a 100644 --- a/src/controller/data_model/controller-clusters.matter +++ b/src/controller/data_model/controller-clusters.matter @@ -9245,6 +9245,148 @@ provisional cluster ContentAppObserver = 1296 { command ContentAppMessage(ContentAppMessageRequest): ContentAppMessageResponse = 0; } +/** Provides extended device information for all the logical devices represented by a Bridged Node. */ +provisional cluster EcosystemInformation = 1872 { + revision 1; + + enum AreaTypeTag : enum8 { + kAisle = 0; + kAttic = 1; + kBackDoor = 2; + kBackYard = 3; + kBalcony = 4; + kBallroom = 5; + kBathroom = 6; + kBedroom = 7; + kBorder = 8; + kBoxroom = 9; + kBreakfastRoom = 10; + kCarport = 11; + kCellar = 12; + kCloakroom = 13; + kCloset = 14; + kConservatory = 15; + kCorridor = 16; + kCraftRoom = 17; + kCupboard = 18; + kDeck = 19; + kDen = 20; + kDining = 21; + kDrawingRoom = 22; + kDressingRoom = 23; + kDriveway = 24; + kElevator = 25; + kEnsuite = 26; + kEntrance = 27; + kEntryway = 28; + kFamilyRoom = 29; + kFoyer = 30; + kFrontDoor = 31; + kFrontYard = 32; + kGameRoom = 33; + kGarage = 34; + kGarageDoor = 35; + kGarden = 36; + kGardenDoor = 37; + kGuestBathroom = 38; + kGuestBedroom = 39; + kGuestRestroom = 40; + kGuestRoom = 41; + kGym = 42; + kHallway = 43; + kHearthRoom = 44; + kKidsRoom = 45; + kKidsBedroom = 46; + kKitchen = 47; + kLarder = 48; + kLaundryRoom = 49; + kLawn = 50; + kLibrary = 51; + kLivingRoom = 52; + kLounge = 53; + kMediaTVRoom = 54; + kMudRoom = 55; + kMusicRoom = 56; + kNursery = 57; + kOffice = 58; + kOutdoorKitchen = 59; + kOutside = 60; + kPantry = 61; + kParkingLot = 62; + kParlor = 63; + kPatio = 64; + kPlayRoom = 65; + kPoolRoom = 66; + kPorch = 67; + kPrimaryBathroom = 68; + kPrimaryBedroom = 69; + kRamp = 70; + kReceptionRoom = 71; + kRecreationRoom = 72; + kRestroom = 73; + kRoof = 74; + kSauna = 75; + kScullery = 76; + kSewingRoom = 77; + kShed = 78; + kSideDoor = 79; + kSideYard = 80; + kSittingRoom = 81; + kSnug = 82; + kSpa = 83; + kStaircase = 84; + kSteamRoom = 85; + kStorageRoom = 86; + kStudio = 87; + kStudy = 88; + kSunRoom = 89; + kSwimmingPool = 90; + kTerrace = 91; + kUtilityRoom = 92; + kWard = 93; + kWorkshop = 94; + } + + struct HomeLocationStruct { + char_string<128> locationName = 0; + nullable int16s floorNumber = 1; + nullable AreaTypeTag areaType = 2; + } + + fabric_scoped struct EcosystemLocationStruct { + fabric_sensitive char_string<64> uniqueLocationID = 0; + fabric_sensitive HomeLocationStruct locationDescriptor = 1; + fabric_sensitive epoch_us locationDescriptorLastEdit = 2; + fabric_idx fabricIndex = 254; + } + + struct DeviceTypeStruct { + devtype_id deviceType = 0; + int16u revision = 1; + } + + fabric_scoped struct EcosystemDeviceStruct { + optional fabric_sensitive char_string<64> deviceName = 0; + optional fabric_sensitive epoch_us deviceNameLastEdit = 1; + fabric_sensitive endpoint_no bridgedEndpoint = 2; + fabric_sensitive endpoint_no originalEndpoint = 3; + fabric_sensitive DeviceTypeStruct deviceTypes[] = 4; + fabric_sensitive char_string uniqueLocationIDs[] = 5; + fabric_sensitive epoch_us uniqueLocationIDsLastEdit = 6; + fabric_idx fabricIndex = 254; + } + + readonly attribute access(read: manage) optional nullable epoch_us removedOn = 0; + readonly attribute access(read: manage) EcosystemDeviceStruct deviceDirectory[] = 1; + readonly attribute access(read: manage) EcosystemLocationStruct locationDirectory[] = 2; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; +} + /** Supports the ability for clients to request the commissioning of themselves or other nodes onto a fabric which the cluster server can commission onto. */ provisional cluster CommissionerControl = 1873 { revision 1; diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java index bbdc1dea185456..e11a47a23a8c7c 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java @@ -60522,6 +60522,302 @@ public void onSuccess(byte[] tlv) { } } + public static class EcosystemInformationCluster extends BaseChipCluster { + public static final long CLUSTER_ID = 1872L; + + private static final long REMOVED_ON_ATTRIBUTE_ID = 0L; + private static final long DEVICE_DIRECTORY_ATTRIBUTE_ID = 1L; + private static final long LOCATION_DIRECTORY_ATTRIBUTE_ID = 2L; + private static final long GENERATED_COMMAND_LIST_ATTRIBUTE_ID = 65528L; + private static final long ACCEPTED_COMMAND_LIST_ATTRIBUTE_ID = 65529L; + private static final long EVENT_LIST_ATTRIBUTE_ID = 65530L; + private static final long ATTRIBUTE_LIST_ATTRIBUTE_ID = 65531L; + private static final long FEATURE_MAP_ATTRIBUTE_ID = 65532L; + private static final long CLUSTER_REVISION_ATTRIBUTE_ID = 65533L; + + public EcosystemInformationCluster(long devicePtr, int endpointId) { + super(devicePtr, endpointId, CLUSTER_ID); + } + + @Override + @Deprecated + public long initWithDevice(long devicePtr, int endpointId) { + return 0L; + } + + public interface RemovedOnAttributeCallback extends BaseAttributeCallback { + void onSuccess(@Nullable Long value); + } + + public interface DeviceDirectoryAttributeCallback extends BaseAttributeCallback { + void onSuccess(List value); + } + + public interface LocationDirectoryAttributeCallback extends BaseAttributeCallback { + void onSuccess(List value); + } + + public interface GeneratedCommandListAttributeCallback extends BaseAttributeCallback { + void onSuccess(List value); + } + + public interface AcceptedCommandListAttributeCallback extends BaseAttributeCallback { + void onSuccess(List value); + } + + public interface EventListAttributeCallback extends BaseAttributeCallback { + void onSuccess(List value); + } + + public interface AttributeListAttributeCallback extends BaseAttributeCallback { + void onSuccess(List value); + } + + public void readRemovedOnAttribute( + RemovedOnAttributeCallback callback) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, REMOVED_ON_ATTRIBUTE_ID); + + readAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + @Nullable Long value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, REMOVED_ON_ATTRIBUTE_ID, true); + } + + public void subscribeRemovedOnAttribute( + RemovedOnAttributeCallback callback, int minInterval, int maxInterval) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, REMOVED_ON_ATTRIBUTE_ID); + + subscribeAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + @Nullable Long value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, REMOVED_ON_ATTRIBUTE_ID, minInterval, maxInterval); + } + + public void readDeviceDirectoryAttribute( + DeviceDirectoryAttributeCallback callback) { + readDeviceDirectoryAttributeWithFabricFilter(callback, true); + } + + public void readDeviceDirectoryAttributeWithFabricFilter( + DeviceDirectoryAttributeCallback callback, boolean isFabricFiltered) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, DEVICE_DIRECTORY_ATTRIBUTE_ID); + + readAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, DEVICE_DIRECTORY_ATTRIBUTE_ID, isFabricFiltered); + } + + public void subscribeDeviceDirectoryAttribute( + DeviceDirectoryAttributeCallback callback, int minInterval, int maxInterval) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, DEVICE_DIRECTORY_ATTRIBUTE_ID); + + subscribeAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, DEVICE_DIRECTORY_ATTRIBUTE_ID, minInterval, maxInterval); + } + + public void readLocationDirectoryAttribute( + LocationDirectoryAttributeCallback callback) { + readLocationDirectoryAttributeWithFabricFilter(callback, true); + } + + public void readLocationDirectoryAttributeWithFabricFilter( + LocationDirectoryAttributeCallback callback, boolean isFabricFiltered) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, LOCATION_DIRECTORY_ATTRIBUTE_ID); + + readAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, LOCATION_DIRECTORY_ATTRIBUTE_ID, isFabricFiltered); + } + + public void subscribeLocationDirectoryAttribute( + LocationDirectoryAttributeCallback callback, int minInterval, int maxInterval) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, LOCATION_DIRECTORY_ATTRIBUTE_ID); + + subscribeAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, LOCATION_DIRECTORY_ATTRIBUTE_ID, minInterval, maxInterval); + } + + public void readGeneratedCommandListAttribute( + GeneratedCommandListAttributeCallback callback) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, GENERATED_COMMAND_LIST_ATTRIBUTE_ID); + + readAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, GENERATED_COMMAND_LIST_ATTRIBUTE_ID, true); + } + + public void subscribeGeneratedCommandListAttribute( + GeneratedCommandListAttributeCallback callback, int minInterval, int maxInterval) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, GENERATED_COMMAND_LIST_ATTRIBUTE_ID); + + subscribeAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, GENERATED_COMMAND_LIST_ATTRIBUTE_ID, minInterval, maxInterval); + } + + public void readAcceptedCommandListAttribute( + AcceptedCommandListAttributeCallback callback) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, ACCEPTED_COMMAND_LIST_ATTRIBUTE_ID); + + readAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, ACCEPTED_COMMAND_LIST_ATTRIBUTE_ID, true); + } + + public void subscribeAcceptedCommandListAttribute( + AcceptedCommandListAttributeCallback callback, int minInterval, int maxInterval) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, ACCEPTED_COMMAND_LIST_ATTRIBUTE_ID); + + subscribeAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, ACCEPTED_COMMAND_LIST_ATTRIBUTE_ID, minInterval, maxInterval); + } + + public void readEventListAttribute( + EventListAttributeCallback callback) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, EVENT_LIST_ATTRIBUTE_ID); + + readAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, EVENT_LIST_ATTRIBUTE_ID, true); + } + + public void subscribeEventListAttribute( + EventListAttributeCallback callback, int minInterval, int maxInterval) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, EVENT_LIST_ATTRIBUTE_ID); + + subscribeAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, EVENT_LIST_ATTRIBUTE_ID, minInterval, maxInterval); + } + + public void readAttributeListAttribute( + AttributeListAttributeCallback callback) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, ATTRIBUTE_LIST_ATTRIBUTE_ID); + + readAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, ATTRIBUTE_LIST_ATTRIBUTE_ID, true); + } + + public void subscribeAttributeListAttribute( + AttributeListAttributeCallback callback, int minInterval, int maxInterval) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, ATTRIBUTE_LIST_ATTRIBUTE_ID); + + subscribeAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, ATTRIBUTE_LIST_ATTRIBUTE_ID, minInterval, maxInterval); + } + + public void readFeatureMapAttribute( + LongAttributeCallback callback) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, FEATURE_MAP_ATTRIBUTE_ID); + + readAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + Long value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, FEATURE_MAP_ATTRIBUTE_ID, true); + } + + public void subscribeFeatureMapAttribute( + LongAttributeCallback callback, int minInterval, int maxInterval) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, FEATURE_MAP_ATTRIBUTE_ID); + + subscribeAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + Long value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, FEATURE_MAP_ATTRIBUTE_ID, minInterval, maxInterval); + } + + public void readClusterRevisionAttribute( + IntegerAttributeCallback callback) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, CLUSTER_REVISION_ATTRIBUTE_ID); + + readAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + Integer value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, CLUSTER_REVISION_ATTRIBUTE_ID, true); + } + + public void subscribeClusterRevisionAttribute( + IntegerAttributeCallback callback, int minInterval, int maxInterval) { + ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, CLUSTER_REVISION_ATTRIBUTE_ID); + + subscribeAttribute(new ReportCallbackImpl(callback, path) { + @Override + public void onSuccess(byte[] tlv) { + Integer value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv); + callback.onSuccess(value); + } + }, CLUSTER_REVISION_ATTRIBUTE_ID, minInterval, maxInterval); + } + } + public static class CommissionerControlCluster extends BaseChipCluster { public static final long CLUSTER_ID = 1873L; diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java b/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java index c62c3062fa3bc4..075e3dbd45ed0e 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java @@ -12283,6 +12283,385 @@ public String toString() { return output.toString(); } } +public static class EcosystemInformationClusterHomeLocationStruct { + public String locationName; + public @Nullable Integer floorNumber; + public @Nullable Integer areaType; + private static final long LOCATION_NAME_ID = 0L; + private static final long FLOOR_NUMBER_ID = 1L; + private static final long AREA_TYPE_ID = 2L; + + public EcosystemInformationClusterHomeLocationStruct( + String locationName, + @Nullable Integer floorNumber, + @Nullable Integer areaType + ) { + this.locationName = locationName; + this.floorNumber = floorNumber; + this.areaType = areaType; + } + + public StructType encodeTlv() { + ArrayList values = new ArrayList<>(); + values.add(new StructElement(LOCATION_NAME_ID, new StringType(locationName))); + values.add(new StructElement(FLOOR_NUMBER_ID, floorNumber != null ? new IntType(floorNumber) : new NullType())); + values.add(new StructElement(AREA_TYPE_ID, areaType != null ? new UIntType(areaType) : new NullType())); + + return new StructType(values); + } + + public static EcosystemInformationClusterHomeLocationStruct decodeTlv(BaseTLVType tlvValue) { + if (tlvValue == null || tlvValue.type() != TLVType.Struct) { + return null; + } + String locationName = null; + @Nullable Integer floorNumber = null; + @Nullable Integer areaType = null; + for (StructElement element: ((StructType)tlvValue).value()) { + if (element.contextTagNum() == LOCATION_NAME_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.String) { + StringType castingValue = element.value(StringType.class); + locationName = castingValue.value(String.class); + } + } else if (element.contextTagNum() == FLOOR_NUMBER_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.Int) { + IntType castingValue = element.value(IntType.class); + floorNumber = castingValue.value(Integer.class); + } + } else if (element.contextTagNum() == AREA_TYPE_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + areaType = castingValue.value(Integer.class); + } + } + } + return new EcosystemInformationClusterHomeLocationStruct( + locationName, + floorNumber, + areaType + ); + } + + @Override + public String toString() { + StringBuilder output = new StringBuilder(); + output.append("EcosystemInformationClusterHomeLocationStruct {\n"); + output.append("\tlocationName: "); + output.append(locationName); + output.append("\n"); + output.append("\tfloorNumber: "); + output.append(floorNumber); + output.append("\n"); + output.append("\tareaType: "); + output.append(areaType); + output.append("\n"); + output.append("}\n"); + return output.toString(); + } +} +public static class EcosystemInformationClusterEcosystemLocationStruct { + public String uniqueLocationID; + public ChipStructs.EcosystemInformationClusterHomeLocationStruct locationDescriptor; + public Long locationDescriptorLastEdit; + public Integer fabricIndex; + private static final long UNIQUE_LOCATION_I_D_ID = 0L; + private static final long LOCATION_DESCRIPTOR_ID = 1L; + private static final long LOCATION_DESCRIPTOR_LAST_EDIT_ID = 2L; + private static final long FABRIC_INDEX_ID = 254L; + + public EcosystemInformationClusterEcosystemLocationStruct( + String uniqueLocationID, + ChipStructs.EcosystemInformationClusterHomeLocationStruct locationDescriptor, + Long locationDescriptorLastEdit, + Integer fabricIndex + ) { + this.uniqueLocationID = uniqueLocationID; + this.locationDescriptor = locationDescriptor; + this.locationDescriptorLastEdit = locationDescriptorLastEdit; + this.fabricIndex = fabricIndex; + } + + public StructType encodeTlv() { + ArrayList values = new ArrayList<>(); + values.add(new StructElement(UNIQUE_LOCATION_I_D_ID, new StringType(uniqueLocationID))); + values.add(new StructElement(LOCATION_DESCRIPTOR_ID, locationDescriptor.encodeTlv())); + values.add(new StructElement(LOCATION_DESCRIPTOR_LAST_EDIT_ID, new UIntType(locationDescriptorLastEdit))); + values.add(new StructElement(FABRIC_INDEX_ID, new UIntType(fabricIndex))); + + return new StructType(values); + } + + public static EcosystemInformationClusterEcosystemLocationStruct decodeTlv(BaseTLVType tlvValue) { + if (tlvValue == null || tlvValue.type() != TLVType.Struct) { + return null; + } + String uniqueLocationID = null; + ChipStructs.EcosystemInformationClusterHomeLocationStruct locationDescriptor = null; + Long locationDescriptorLastEdit = null; + Integer fabricIndex = null; + for (StructElement element: ((StructType)tlvValue).value()) { + if (element.contextTagNum() == UNIQUE_LOCATION_I_D_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.String) { + StringType castingValue = element.value(StringType.class); + uniqueLocationID = castingValue.value(String.class); + } + } else if (element.contextTagNum() == LOCATION_DESCRIPTOR_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.Struct) { + StructType castingValue = element.value(StructType.class); + locationDescriptor = ChipStructs.EcosystemInformationClusterHomeLocationStruct.decodeTlv(castingValue); + } + } else if (element.contextTagNum() == LOCATION_DESCRIPTOR_LAST_EDIT_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + locationDescriptorLastEdit = castingValue.value(Long.class); + } + } else if (element.contextTagNum() == FABRIC_INDEX_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + fabricIndex = castingValue.value(Integer.class); + } + } + } + return new EcosystemInformationClusterEcosystemLocationStruct( + uniqueLocationID, + locationDescriptor, + locationDescriptorLastEdit, + fabricIndex + ); + } + + @Override + public String toString() { + StringBuilder output = new StringBuilder(); + output.append("EcosystemInformationClusterEcosystemLocationStruct {\n"); + output.append("\tuniqueLocationID: "); + output.append(uniqueLocationID); + output.append("\n"); + output.append("\tlocationDescriptor: "); + output.append(locationDescriptor); + output.append("\n"); + output.append("\tlocationDescriptorLastEdit: "); + output.append(locationDescriptorLastEdit); + output.append("\n"); + output.append("\tfabricIndex: "); + output.append(fabricIndex); + output.append("\n"); + output.append("}\n"); + return output.toString(); + } +} +public static class EcosystemInformationClusterDeviceTypeStruct { + public Long deviceType; + public Integer revision; + private static final long DEVICE_TYPE_ID = 0L; + private static final long REVISION_ID = 1L; + + public EcosystemInformationClusterDeviceTypeStruct( + Long deviceType, + Integer revision + ) { + this.deviceType = deviceType; + this.revision = revision; + } + + public StructType encodeTlv() { + ArrayList values = new ArrayList<>(); + values.add(new StructElement(DEVICE_TYPE_ID, new UIntType(deviceType))); + values.add(new StructElement(REVISION_ID, new UIntType(revision))); + + return new StructType(values); + } + + public static EcosystemInformationClusterDeviceTypeStruct decodeTlv(BaseTLVType tlvValue) { + if (tlvValue == null || tlvValue.type() != TLVType.Struct) { + return null; + } + Long deviceType = null; + Integer revision = null; + for (StructElement element: ((StructType)tlvValue).value()) { + if (element.contextTagNum() == DEVICE_TYPE_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + deviceType = castingValue.value(Long.class); + } + } else if (element.contextTagNum() == REVISION_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + revision = castingValue.value(Integer.class); + } + } + } + return new EcosystemInformationClusterDeviceTypeStruct( + deviceType, + revision + ); + } + + @Override + public String toString() { + StringBuilder output = new StringBuilder(); + output.append("EcosystemInformationClusterDeviceTypeStruct {\n"); + output.append("\tdeviceType: "); + output.append(deviceType); + output.append("\n"); + output.append("\trevision: "); + output.append(revision); + output.append("\n"); + output.append("}\n"); + return output.toString(); + } +} +public static class EcosystemInformationClusterEcosystemDeviceStruct { + public Optional deviceName; + public Optional deviceNameLastEdit; + public Integer bridgedEndpoint; + public Integer originalEndpoint; + public ArrayList deviceTypes; + public ArrayList uniqueLocationIDs; + public Long uniqueLocationIDsLastEdit; + public Integer fabricIndex; + private static final long DEVICE_NAME_ID = 0L; + private static final long DEVICE_NAME_LAST_EDIT_ID = 1L; + private static final long BRIDGED_ENDPOINT_ID = 2L; + private static final long ORIGINAL_ENDPOINT_ID = 3L; + private static final long DEVICE_TYPES_ID = 4L; + private static final long UNIQUE_LOCATION_I_DS_ID = 5L; + private static final long UNIQUE_LOCATION_I_DS_LAST_EDIT_ID = 6L; + private static final long FABRIC_INDEX_ID = 254L; + + public EcosystemInformationClusterEcosystemDeviceStruct( + Optional deviceName, + Optional deviceNameLastEdit, + Integer bridgedEndpoint, + Integer originalEndpoint, + ArrayList deviceTypes, + ArrayList uniqueLocationIDs, + Long uniqueLocationIDsLastEdit, + Integer fabricIndex + ) { + this.deviceName = deviceName; + this.deviceNameLastEdit = deviceNameLastEdit; + this.bridgedEndpoint = bridgedEndpoint; + this.originalEndpoint = originalEndpoint; + this.deviceTypes = deviceTypes; + this.uniqueLocationIDs = uniqueLocationIDs; + this.uniqueLocationIDsLastEdit = uniqueLocationIDsLastEdit; + this.fabricIndex = fabricIndex; + } + + public StructType encodeTlv() { + ArrayList values = new ArrayList<>(); + values.add(new StructElement(DEVICE_NAME_ID, deviceName.map((nonOptionaldeviceName) -> new StringType(nonOptionaldeviceName)).orElse(new EmptyType()))); + values.add(new StructElement(DEVICE_NAME_LAST_EDIT_ID, deviceNameLastEdit.map((nonOptionaldeviceNameLastEdit) -> new UIntType(nonOptionaldeviceNameLastEdit)).orElse(new EmptyType()))); + values.add(new StructElement(BRIDGED_ENDPOINT_ID, new UIntType(bridgedEndpoint))); + values.add(new StructElement(ORIGINAL_ENDPOINT_ID, new UIntType(originalEndpoint))); + values.add(new StructElement(DEVICE_TYPES_ID, ArrayType.generateArrayType(deviceTypes, (elementdeviceTypes) -> elementdeviceTypes.encodeTlv()))); + values.add(new StructElement(UNIQUE_LOCATION_I_DS_ID, ArrayType.generateArrayType(uniqueLocationIDs, (elementuniqueLocationIDs) -> new StringType(elementuniqueLocationIDs)))); + values.add(new StructElement(UNIQUE_LOCATION_I_DS_LAST_EDIT_ID, new UIntType(uniqueLocationIDsLastEdit))); + values.add(new StructElement(FABRIC_INDEX_ID, new UIntType(fabricIndex))); + + return new StructType(values); + } + + public static EcosystemInformationClusterEcosystemDeviceStruct decodeTlv(BaseTLVType tlvValue) { + if (tlvValue == null || tlvValue.type() != TLVType.Struct) { + return null; + } + Optional deviceName = Optional.empty(); + Optional deviceNameLastEdit = Optional.empty(); + Integer bridgedEndpoint = null; + Integer originalEndpoint = null; + ArrayList deviceTypes = null; + ArrayList uniqueLocationIDs = null; + Long uniqueLocationIDsLastEdit = null; + Integer fabricIndex = null; + for (StructElement element: ((StructType)tlvValue).value()) { + if (element.contextTagNum() == DEVICE_NAME_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.String) { + StringType castingValue = element.value(StringType.class); + deviceName = Optional.of(castingValue.value(String.class)); + } + } else if (element.contextTagNum() == DEVICE_NAME_LAST_EDIT_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + deviceNameLastEdit = Optional.of(castingValue.value(Long.class)); + } + } else if (element.contextTagNum() == BRIDGED_ENDPOINT_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + bridgedEndpoint = castingValue.value(Integer.class); + } + } else if (element.contextTagNum() == ORIGINAL_ENDPOINT_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + originalEndpoint = castingValue.value(Integer.class); + } + } else if (element.contextTagNum() == DEVICE_TYPES_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.Array) { + ArrayType castingValue = element.value(ArrayType.class); + deviceTypes = castingValue.map((elementcastingValue) -> ChipStructs.EcosystemInformationClusterDeviceTypeStruct.decodeTlv(elementcastingValue)); + } + } else if (element.contextTagNum() == UNIQUE_LOCATION_I_DS_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.Array) { + ArrayType castingValue = element.value(ArrayType.class); + uniqueLocationIDs = castingValue.map((elementcastingValue) -> elementcastingValue.value(String.class)); + } + } else if (element.contextTagNum() == UNIQUE_LOCATION_I_DS_LAST_EDIT_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + uniqueLocationIDsLastEdit = castingValue.value(Long.class); + } + } else if (element.contextTagNum() == FABRIC_INDEX_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + fabricIndex = castingValue.value(Integer.class); + } + } + } + return new EcosystemInformationClusterEcosystemDeviceStruct( + deviceName, + deviceNameLastEdit, + bridgedEndpoint, + originalEndpoint, + deviceTypes, + uniqueLocationIDs, + uniqueLocationIDsLastEdit, + fabricIndex + ); + } + + @Override + public String toString() { + StringBuilder output = new StringBuilder(); + output.append("EcosystemInformationClusterEcosystemDeviceStruct {\n"); + output.append("\tdeviceName: "); + output.append(deviceName); + output.append("\n"); + output.append("\tdeviceNameLastEdit: "); + output.append(deviceNameLastEdit); + output.append("\n"); + output.append("\tbridgedEndpoint: "); + output.append(bridgedEndpoint); + output.append("\n"); + output.append("\toriginalEndpoint: "); + output.append(originalEndpoint); + output.append("\n"); + output.append("\tdeviceTypes: "); + output.append(deviceTypes); + output.append("\n"); + output.append("\tuniqueLocationIDs: "); + output.append(uniqueLocationIDs); + output.append("\n"); + output.append("\tuniqueLocationIDsLastEdit: "); + output.append(uniqueLocationIDsLastEdit); + output.append("\n"); + output.append("\tfabricIndex: "); + output.append(fabricIndex); + output.append("\n"); + output.append("}\n"); + return output.toString(); + } +} public static class UnitTestingClusterSimpleStruct { public Integer a; public Boolean b; diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java index 72972b2554c102..ab20bdcaa301e9 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java @@ -388,6 +388,9 @@ public static BaseCluster getCluster(long clusterId) { if (clusterId == ContentAppObserver.ID) { return new ContentAppObserver(); } + if (clusterId == EcosystemInformation.ID) { + return new EcosystemInformation(); + } if (clusterId == CommissionerControl.ID) { return new CommissionerControl(); } @@ -17166,6 +17169,109 @@ public long getCommandID(String name) throws IllegalArgumentException { return Command.valueOf(name).getID(); } } + public static class EcosystemInformation implements BaseCluster { + public static final long ID = 1872L; + public long getID() { + return ID; + } + + public enum Attribute { + RemovedOn(0L), + DeviceDirectory(1L), + LocationDirectory(2L), + GeneratedCommandList(65528L), + AcceptedCommandList(65529L), + EventList(65530L), + AttributeList(65531L), + FeatureMap(65532L), + ClusterRevision(65533L),; + private final long id; + Attribute(long id) { + this.id = id; + } + + public long getID() { + return id; + } + + public static Attribute value(long id) throws NoSuchFieldError { + for (Attribute attribute : Attribute.values()) { + if (attribute.getID() == id) { + return attribute; + } + } + throw new NoSuchFieldError(); + } + } + + public enum Event {; + private final long id; + Event(long id) { + this.id = id; + } + + public long getID() { + return id; + } + + public static Event value(long id) throws NoSuchFieldError { + for (Event event : Event.values()) { + if (event.getID() == id) { + return event; + } + } + throw new NoSuchFieldError(); + } + } + + public enum Command {; + private final long id; + Command(long id) { + this.id = id; + } + + public long getID() { + return id; + } + + public static Command value(long id) throws NoSuchFieldError { + for (Command command : Command.values()) { + if (command.getID() == id) { + return command; + } + } + throw new NoSuchFieldError(); + } + }@Override + public String getAttributeName(long id) throws NoSuchFieldError { + return Attribute.value(id).toString(); + } + + @Override + public String getEventName(long id) throws NoSuchFieldError { + return Event.value(id).toString(); + } + + @Override + public String getCommandName(long id) throws NoSuchFieldError { + return Command.value(id).toString(); + } + + @Override + public long getAttributeID(String name) throws IllegalArgumentException { + return Attribute.valueOf(name).getID(); + } + + @Override + public long getEventID(String name) throws IllegalArgumentException { + return Event.valueOf(name).getID(); + } + + @Override + public long getCommandID(String name) throws IllegalArgumentException { + return Command.valueOf(name).getID(); + } + } public static class CommissionerControl implements BaseCluster { public static final long ID = 1873L; public long getID() { diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java index 98a276983b8670..ca3d74f1ac3797 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java @@ -20089,6 +20089,153 @@ public void onError(Exception ex) { } } + public static class DelegatedEcosystemInformationClusterRemovedOnAttributeCallback implements ChipClusters.EcosystemInformationCluster.RemovedOnAttributeCallback, DelegatedClusterCallback { + private ClusterCommandCallback callback; + @Override + public void setCallbackDelegate(ClusterCommandCallback callback) { + this.callback = callback; + } + + @Override + public void onSuccess(@Nullable Long value) { + Map responseValues = new LinkedHashMap<>(); + CommandResponseInfo commandResponseInfo = new CommandResponseInfo("value", "Long"); + responseValues.put(commandResponseInfo, value); + callback.onSuccess(responseValues); + } + + @Override + public void onError(Exception ex) { + callback.onFailure(ex); + } + } + + public static class DelegatedEcosystemInformationClusterDeviceDirectoryAttributeCallback implements ChipClusters.EcosystemInformationCluster.DeviceDirectoryAttributeCallback, DelegatedClusterCallback { + private ClusterCommandCallback callback; + @Override + public void setCallbackDelegate(ClusterCommandCallback callback) { + this.callback = callback; + } + + @Override + public void onSuccess(List valueList) { + Map responseValues = new LinkedHashMap<>(); + CommandResponseInfo commandResponseInfo = new CommandResponseInfo("valueList", "List"); + responseValues.put(commandResponseInfo, valueList); + callback.onSuccess(responseValues); + } + + @Override + public void onError(Exception ex) { + callback.onFailure(ex); + } + } + + public static class DelegatedEcosystemInformationClusterLocationDirectoryAttributeCallback implements ChipClusters.EcosystemInformationCluster.LocationDirectoryAttributeCallback, DelegatedClusterCallback { + private ClusterCommandCallback callback; + @Override + public void setCallbackDelegate(ClusterCommandCallback callback) { + this.callback = callback; + } + + @Override + public void onSuccess(List valueList) { + Map responseValues = new LinkedHashMap<>(); + CommandResponseInfo commandResponseInfo = new CommandResponseInfo("valueList", "List"); + responseValues.put(commandResponseInfo, valueList); + callback.onSuccess(responseValues); + } + + @Override + public void onError(Exception ex) { + callback.onFailure(ex); + } + } + + public static class DelegatedEcosystemInformationClusterGeneratedCommandListAttributeCallback implements ChipClusters.EcosystemInformationCluster.GeneratedCommandListAttributeCallback, DelegatedClusterCallback { + private ClusterCommandCallback callback; + @Override + public void setCallbackDelegate(ClusterCommandCallback callback) { + this.callback = callback; + } + + @Override + public void onSuccess(List valueList) { + Map responseValues = new LinkedHashMap<>(); + CommandResponseInfo commandResponseInfo = new CommandResponseInfo("valueList", "List"); + responseValues.put(commandResponseInfo, valueList); + callback.onSuccess(responseValues); + } + + @Override + public void onError(Exception ex) { + callback.onFailure(ex); + } + } + + public static class DelegatedEcosystemInformationClusterAcceptedCommandListAttributeCallback implements ChipClusters.EcosystemInformationCluster.AcceptedCommandListAttributeCallback, DelegatedClusterCallback { + private ClusterCommandCallback callback; + @Override + public void setCallbackDelegate(ClusterCommandCallback callback) { + this.callback = callback; + } + + @Override + public void onSuccess(List valueList) { + Map responseValues = new LinkedHashMap<>(); + CommandResponseInfo commandResponseInfo = new CommandResponseInfo("valueList", "List"); + responseValues.put(commandResponseInfo, valueList); + callback.onSuccess(responseValues); + } + + @Override + public void onError(Exception ex) { + callback.onFailure(ex); + } + } + + public static class DelegatedEcosystemInformationClusterEventListAttributeCallback implements ChipClusters.EcosystemInformationCluster.EventListAttributeCallback, DelegatedClusterCallback { + private ClusterCommandCallback callback; + @Override + public void setCallbackDelegate(ClusterCommandCallback callback) { + this.callback = callback; + } + + @Override + public void onSuccess(List valueList) { + Map responseValues = new LinkedHashMap<>(); + CommandResponseInfo commandResponseInfo = new CommandResponseInfo("valueList", "List"); + responseValues.put(commandResponseInfo, valueList); + callback.onSuccess(responseValues); + } + + @Override + public void onError(Exception ex) { + callback.onFailure(ex); + } + } + + public static class DelegatedEcosystemInformationClusterAttributeListAttributeCallback implements ChipClusters.EcosystemInformationCluster.AttributeListAttributeCallback, DelegatedClusterCallback { + private ClusterCommandCallback callback; + @Override + public void setCallbackDelegate(ClusterCommandCallback callback) { + this.callback = callback; + } + + @Override + public void onSuccess(List valueList) { + Map responseValues = new LinkedHashMap<>(); + CommandResponseInfo commandResponseInfo = new CommandResponseInfo("valueList", "List"); + responseValues.put(commandResponseInfo, valueList); + callback.onSuccess(responseValues); + } + + @Override + public void onError(Exception ex) { + callback.onFailure(ex); + } + } + public static class DelegatedCommissionerControlClusterReverseOpenCommissioningWindowCallback implements ChipClusters.CommissionerControlCluster.ReverseOpenCommissioningWindowCallback, DelegatedClusterCallback { private ClusterCommandCallback callback; @@ -22308,6 +22455,10 @@ public Map initializeClusterMap() { (ptr, endpointId) -> new ChipClusters.ContentAppObserverCluster(ptr, endpointId), new HashMap<>()); clusterMap.put("contentAppObserver", contentAppObserverClusterInfo); + ClusterInfo ecosystemInformationClusterInfo = new ClusterInfo( + (ptr, endpointId) -> new ChipClusters.EcosystemInformationCluster(ptr, endpointId), new HashMap<>()); + clusterMap.put("ecosystemInformation", ecosystemInformationClusterInfo); + ClusterInfo commissionerControlClusterInfo = new ClusterInfo( (ptr, endpointId) -> new ChipClusters.CommissionerControlCluster(ptr, endpointId), new HashMap<>()); clusterMap.put("commissionerControl", commissionerControlClusterInfo); @@ -22452,6 +22603,7 @@ public void combineCommand(Map destination, Map> getCommandMap() { commandMap.put("contentAppObserver", contentAppObserverClusterInteractionInfoMap); + Map ecosystemInformationClusterInteractionInfoMap = new LinkedHashMap<>(); + + commandMap.put("ecosystemInformation", ecosystemInformationClusterInteractionInfoMap); + Map commissionerControlClusterInteractionInfoMap = new LinkedHashMap<>(); Map commissionerControlrequestCommissioningApprovalCommandParams = new LinkedHashMap(); diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterReadMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterReadMapping.java index 04c849e6c0eb9c..45d0e3037631fb 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterReadMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterReadMapping.java @@ -18600,6 +18600,109 @@ private static Map readContentAppObserverInteractionInf return result; } + private static Map readEcosystemInformationInteractionInfo() { + Map result = new LinkedHashMap<>();Map readEcosystemInformationRemovedOnCommandParams = new LinkedHashMap(); + InteractionInfo readEcosystemInformationRemovedOnAttributeInteractionInfo = new InteractionInfo( + (cluster, callback, commandArguments) -> { + ((ChipClusters.EcosystemInformationCluster) cluster).readRemovedOnAttribute( + (ChipClusters.EcosystemInformationCluster.RemovedOnAttributeCallback) callback + ); + }, + () -> new ClusterInfoMapping.DelegatedEcosystemInformationClusterRemovedOnAttributeCallback(), + readEcosystemInformationRemovedOnCommandParams + ); + result.put("readRemovedOnAttribute", readEcosystemInformationRemovedOnAttributeInteractionInfo); + Map readEcosystemInformationDeviceDirectoryCommandParams = new LinkedHashMap(); + InteractionInfo readEcosystemInformationDeviceDirectoryAttributeInteractionInfo = new InteractionInfo( + (cluster, callback, commandArguments) -> { + ((ChipClusters.EcosystemInformationCluster) cluster).readDeviceDirectoryAttribute( + (ChipClusters.EcosystemInformationCluster.DeviceDirectoryAttributeCallback) callback + ); + }, + () -> new ClusterInfoMapping.DelegatedEcosystemInformationClusterDeviceDirectoryAttributeCallback(), + readEcosystemInformationDeviceDirectoryCommandParams + ); + result.put("readDeviceDirectoryAttribute", readEcosystemInformationDeviceDirectoryAttributeInteractionInfo); + Map readEcosystemInformationLocationDirectoryCommandParams = new LinkedHashMap(); + InteractionInfo readEcosystemInformationLocationDirectoryAttributeInteractionInfo = new InteractionInfo( + (cluster, callback, commandArguments) -> { + ((ChipClusters.EcosystemInformationCluster) cluster).readLocationDirectoryAttribute( + (ChipClusters.EcosystemInformationCluster.LocationDirectoryAttributeCallback) callback + ); + }, + () -> new ClusterInfoMapping.DelegatedEcosystemInformationClusterLocationDirectoryAttributeCallback(), + readEcosystemInformationLocationDirectoryCommandParams + ); + result.put("readLocationDirectoryAttribute", readEcosystemInformationLocationDirectoryAttributeInteractionInfo); + Map readEcosystemInformationGeneratedCommandListCommandParams = new LinkedHashMap(); + InteractionInfo readEcosystemInformationGeneratedCommandListAttributeInteractionInfo = new InteractionInfo( + (cluster, callback, commandArguments) -> { + ((ChipClusters.EcosystemInformationCluster) cluster).readGeneratedCommandListAttribute( + (ChipClusters.EcosystemInformationCluster.GeneratedCommandListAttributeCallback) callback + ); + }, + () -> new ClusterInfoMapping.DelegatedEcosystemInformationClusterGeneratedCommandListAttributeCallback(), + readEcosystemInformationGeneratedCommandListCommandParams + ); + result.put("readGeneratedCommandListAttribute", readEcosystemInformationGeneratedCommandListAttributeInteractionInfo); + Map readEcosystemInformationAcceptedCommandListCommandParams = new LinkedHashMap(); + InteractionInfo readEcosystemInformationAcceptedCommandListAttributeInteractionInfo = new InteractionInfo( + (cluster, callback, commandArguments) -> { + ((ChipClusters.EcosystemInformationCluster) cluster).readAcceptedCommandListAttribute( + (ChipClusters.EcosystemInformationCluster.AcceptedCommandListAttributeCallback) callback + ); + }, + () -> new ClusterInfoMapping.DelegatedEcosystemInformationClusterAcceptedCommandListAttributeCallback(), + readEcosystemInformationAcceptedCommandListCommandParams + ); + result.put("readAcceptedCommandListAttribute", readEcosystemInformationAcceptedCommandListAttributeInteractionInfo); + Map readEcosystemInformationEventListCommandParams = new LinkedHashMap(); + InteractionInfo readEcosystemInformationEventListAttributeInteractionInfo = new InteractionInfo( + (cluster, callback, commandArguments) -> { + ((ChipClusters.EcosystemInformationCluster) cluster).readEventListAttribute( + (ChipClusters.EcosystemInformationCluster.EventListAttributeCallback) callback + ); + }, + () -> new ClusterInfoMapping.DelegatedEcosystemInformationClusterEventListAttributeCallback(), + readEcosystemInformationEventListCommandParams + ); + result.put("readEventListAttribute", readEcosystemInformationEventListAttributeInteractionInfo); + Map readEcosystemInformationAttributeListCommandParams = new LinkedHashMap(); + InteractionInfo readEcosystemInformationAttributeListAttributeInteractionInfo = new InteractionInfo( + (cluster, callback, commandArguments) -> { + ((ChipClusters.EcosystemInformationCluster) cluster).readAttributeListAttribute( + (ChipClusters.EcosystemInformationCluster.AttributeListAttributeCallback) callback + ); + }, + () -> new ClusterInfoMapping.DelegatedEcosystemInformationClusterAttributeListAttributeCallback(), + readEcosystemInformationAttributeListCommandParams + ); + result.put("readAttributeListAttribute", readEcosystemInformationAttributeListAttributeInteractionInfo); + Map readEcosystemInformationFeatureMapCommandParams = new LinkedHashMap(); + InteractionInfo readEcosystemInformationFeatureMapAttributeInteractionInfo = new InteractionInfo( + (cluster, callback, commandArguments) -> { + ((ChipClusters.EcosystemInformationCluster) cluster).readFeatureMapAttribute( + (ChipClusters.LongAttributeCallback) callback + ); + }, + () -> new ClusterInfoMapping.DelegatedLongAttributeCallback(), + readEcosystemInformationFeatureMapCommandParams + ); + result.put("readFeatureMapAttribute", readEcosystemInformationFeatureMapAttributeInteractionInfo); + Map readEcosystemInformationClusterRevisionCommandParams = new LinkedHashMap(); + InteractionInfo readEcosystemInformationClusterRevisionAttributeInteractionInfo = new InteractionInfo( + (cluster, callback, commandArguments) -> { + ((ChipClusters.EcosystemInformationCluster) cluster).readClusterRevisionAttribute( + (ChipClusters.IntegerAttributeCallback) callback + ); + }, + () -> new ClusterInfoMapping.DelegatedIntegerAttributeCallback(), + readEcosystemInformationClusterRevisionCommandParams + ); + result.put("readClusterRevisionAttribute", readEcosystemInformationClusterRevisionAttributeInteractionInfo); + + return result; + } private static Map readCommissionerControlInteractionInfo() { Map result = new LinkedHashMap<>();Map readCommissionerControlSupportedDeviceCategoriesCommandParams = new LinkedHashMap(); InteractionInfo readCommissionerControlSupportedDeviceCategoriesAttributeInteractionInfo = new InteractionInfo( @@ -21395,6 +21498,7 @@ public Map> getReadAttributeMap() { put("accountLogin", readAccountLoginInteractionInfo()); put("contentControl", readContentControlInteractionInfo()); put("contentAppObserver", readContentAppObserverInteractionInfo()); + put("ecosystemInformation", readEcosystemInformationInteractionInfo()); put("commissionerControl", readCommissionerControlInteractionInfo()); put("electricalMeasurement", readElectricalMeasurementInteractionInfo()); put("unitTesting", readUnitTestingInteractionInfo()); diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterWriteMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterWriteMapping.java index 28cba2abd728f8..32759bb97af64c 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterWriteMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterWriteMapping.java @@ -3764,6 +3764,8 @@ public Map> getWriteAttributeMap() { writeAttributeMap.put("contentControl", writeContentControlInteractionInfo); Map writeContentAppObserverInteractionInfo = new LinkedHashMap<>(); writeAttributeMap.put("contentAppObserver", writeContentAppObserverInteractionInfo); + Map writeEcosystemInformationInteractionInfo = new LinkedHashMap<>(); + writeAttributeMap.put("ecosystemInformation", writeEcosystemInformationInteractionInfo); Map writeCommissionerControlInteractionInfo = new LinkedHashMap<>(); writeAttributeMap.put("commissionerControl", writeCommissionerControlInteractionInfo); Map writeElectricalMeasurementInteractionInfo = new LinkedHashMap<>(); diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni b/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni index 90ce4a42ae206f..ea8a94605f8ca8 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni @@ -56,6 +56,10 @@ structs_sources = [ "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/DishwasherModeClusterModeOptionStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/DishwasherModeClusterModeTagStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/DoorLockClusterCredentialStruct.kt", + "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterDeviceTypeStruct.kt", + "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterEcosystemDeviceStruct.kt", + "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt", + "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ElectricalEnergyMeasurementClusterCumulativeEnergyResetStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ElectricalEnergyMeasurementClusterEnergyMeasurementStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ElectricalEnergyMeasurementClusterMeasurementAccuracyRangeStruct.kt", diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterDeviceTypeStruct.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterDeviceTypeStruct.kt new file mode 100644 index 00000000000000..a5974dea0c190e --- /dev/null +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterDeviceTypeStruct.kt @@ -0,0 +1,56 @@ +/* + * + * 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. + */ +package chip.devicecontroller.cluster.structs + +import chip.devicecontroller.cluster.* +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class EcosystemInformationClusterDeviceTypeStruct(val deviceType: ULong, val revision: UInt) { + override fun toString(): String = buildString { + append("EcosystemInformationClusterDeviceTypeStruct {\n") + append("\tdeviceType : $deviceType\n") + append("\trevision : $revision\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + put(ContextSpecificTag(TAG_DEVICE_TYPE), deviceType) + put(ContextSpecificTag(TAG_REVISION), revision) + endStructure() + } + } + + companion object { + private const val TAG_DEVICE_TYPE = 0 + private const val TAG_REVISION = 1 + + fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): EcosystemInformationClusterDeviceTypeStruct { + tlvReader.enterStructure(tlvTag) + val deviceType = tlvReader.getULong(ContextSpecificTag(TAG_DEVICE_TYPE)) + val revision = tlvReader.getUInt(ContextSpecificTag(TAG_REVISION)) + + tlvReader.exitContainer() + + return EcosystemInformationClusterDeviceTypeStruct(deviceType, revision) + } + } +} diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterEcosystemDeviceStruct.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterEcosystemDeviceStruct.kt new file mode 100644 index 00000000000000..c603107ebf7449 --- /dev/null +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterEcosystemDeviceStruct.kt @@ -0,0 +1,142 @@ +/* + * + * 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. + */ +package chip.devicecontroller.cluster.structs + +import chip.devicecontroller.cluster.* +import java.util.Optional +import matter.tlv.AnonymousTag +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class EcosystemInformationClusterEcosystemDeviceStruct( + val deviceName: Optional, + val deviceNameLastEdit: Optional, + val bridgedEndpoint: UInt, + val originalEndpoint: UInt, + val deviceTypes: List, + val uniqueLocationIDs: List, + val uniqueLocationIDsLastEdit: ULong, + val fabricIndex: UInt, +) { + override fun toString(): String = buildString { + append("EcosystemInformationClusterEcosystemDeviceStruct {\n") + append("\tdeviceName : $deviceName\n") + append("\tdeviceNameLastEdit : $deviceNameLastEdit\n") + append("\tbridgedEndpoint : $bridgedEndpoint\n") + append("\toriginalEndpoint : $originalEndpoint\n") + append("\tdeviceTypes : $deviceTypes\n") + append("\tuniqueLocationIDs : $uniqueLocationIDs\n") + append("\tuniqueLocationIDsLastEdit : $uniqueLocationIDsLastEdit\n") + append("\tfabricIndex : $fabricIndex\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + if (deviceName.isPresent) { + val optdeviceName = deviceName.get() + put(ContextSpecificTag(TAG_DEVICE_NAME), optdeviceName) + } + if (deviceNameLastEdit.isPresent) { + val optdeviceNameLastEdit = deviceNameLastEdit.get() + put(ContextSpecificTag(TAG_DEVICE_NAME_LAST_EDIT), optdeviceNameLastEdit) + } + put(ContextSpecificTag(TAG_BRIDGED_ENDPOINT), bridgedEndpoint) + put(ContextSpecificTag(TAG_ORIGINAL_ENDPOINT), originalEndpoint) + startArray(ContextSpecificTag(TAG_DEVICE_TYPES)) + for (item in deviceTypes.iterator()) { + item.toTlv(AnonymousTag, this) + } + endArray() + startArray(ContextSpecificTag(TAG_UNIQUE_LOCATION_I_DS)) + for (item in uniqueLocationIDs.iterator()) { + put(AnonymousTag, item) + } + endArray() + put(ContextSpecificTag(TAG_UNIQUE_LOCATION_I_DS_LAST_EDIT), uniqueLocationIDsLastEdit) + put(ContextSpecificTag(TAG_FABRIC_INDEX), fabricIndex) + endStructure() + } + } + + companion object { + private const val TAG_DEVICE_NAME = 0 + private const val TAG_DEVICE_NAME_LAST_EDIT = 1 + private const val TAG_BRIDGED_ENDPOINT = 2 + private const val TAG_ORIGINAL_ENDPOINT = 3 + private const val TAG_DEVICE_TYPES = 4 + private const val TAG_UNIQUE_LOCATION_I_DS = 5 + private const val TAG_UNIQUE_LOCATION_I_DS_LAST_EDIT = 6 + private const val TAG_FABRIC_INDEX = 254 + + fun fromTlv( + tlvTag: Tag, + tlvReader: TlvReader, + ): EcosystemInformationClusterEcosystemDeviceStruct { + tlvReader.enterStructure(tlvTag) + val deviceName = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_DEVICE_NAME))) { + Optional.of(tlvReader.getString(ContextSpecificTag(TAG_DEVICE_NAME))) + } else { + Optional.empty() + } + val deviceNameLastEdit = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_DEVICE_NAME_LAST_EDIT))) { + Optional.of(tlvReader.getULong(ContextSpecificTag(TAG_DEVICE_NAME_LAST_EDIT))) + } else { + Optional.empty() + } + val bridgedEndpoint = tlvReader.getUInt(ContextSpecificTag(TAG_BRIDGED_ENDPOINT)) + val originalEndpoint = tlvReader.getUInt(ContextSpecificTag(TAG_ORIGINAL_ENDPOINT)) + val deviceTypes = + buildList { + tlvReader.enterArray(ContextSpecificTag(TAG_DEVICE_TYPES)) + while (!tlvReader.isEndOfContainer()) { + add(EcosystemInformationClusterDeviceTypeStruct.fromTlv(AnonymousTag, tlvReader)) + } + tlvReader.exitContainer() + } + val uniqueLocationIDs = + buildList { + tlvReader.enterArray(ContextSpecificTag(TAG_UNIQUE_LOCATION_I_DS)) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getString(AnonymousTag)) + } + tlvReader.exitContainer() + } + val uniqueLocationIDsLastEdit = + tlvReader.getULong(ContextSpecificTag(TAG_UNIQUE_LOCATION_I_DS_LAST_EDIT)) + val fabricIndex = tlvReader.getUInt(ContextSpecificTag(TAG_FABRIC_INDEX)) + + tlvReader.exitContainer() + + return EcosystemInformationClusterEcosystemDeviceStruct( + deviceName, + deviceNameLastEdit, + bridgedEndpoint, + originalEndpoint, + deviceTypes, + uniqueLocationIDs, + uniqueLocationIDsLastEdit, + fabricIndex, + ) + } + } +} diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt new file mode 100644 index 00000000000000..c9db75b3082344 --- /dev/null +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt @@ -0,0 +1,82 @@ +/* + * + * 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. + */ +package chip.devicecontroller.cluster.structs + +import chip.devicecontroller.cluster.* +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class EcosystemInformationClusterEcosystemLocationStruct( + val uniqueLocationID: String, + val locationDescriptor: EcosystemInformationClusterHomeLocationStruct, + val locationDescriptorLastEdit: ULong, + val fabricIndex: UInt, +) { + override fun toString(): String = buildString { + append("EcosystemInformationClusterEcosystemLocationStruct {\n") + append("\tuniqueLocationID : $uniqueLocationID\n") + append("\tlocationDescriptor : $locationDescriptor\n") + append("\tlocationDescriptorLastEdit : $locationDescriptorLastEdit\n") + append("\tfabricIndex : $fabricIndex\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + put(ContextSpecificTag(TAG_UNIQUE_LOCATION_I_D), uniqueLocationID) + locationDescriptor.toTlv(ContextSpecificTag(TAG_LOCATION_DESCRIPTOR), this) + put(ContextSpecificTag(TAG_LOCATION_DESCRIPTOR_LAST_EDIT), locationDescriptorLastEdit) + put(ContextSpecificTag(TAG_FABRIC_INDEX), fabricIndex) + endStructure() + } + } + + companion object { + private const val TAG_UNIQUE_LOCATION_I_D = 0 + private const val TAG_LOCATION_DESCRIPTOR = 1 + private const val TAG_LOCATION_DESCRIPTOR_LAST_EDIT = 2 + private const val TAG_FABRIC_INDEX = 254 + + fun fromTlv( + tlvTag: Tag, + tlvReader: TlvReader, + ): EcosystemInformationClusterEcosystemLocationStruct { + tlvReader.enterStructure(tlvTag) + val uniqueLocationID = tlvReader.getString(ContextSpecificTag(TAG_UNIQUE_LOCATION_I_D)) + val locationDescriptor = + EcosystemInformationClusterHomeLocationStruct.fromTlv( + ContextSpecificTag(TAG_LOCATION_DESCRIPTOR), + tlvReader, + ) + val locationDescriptorLastEdit = + tlvReader.getULong(ContextSpecificTag(TAG_LOCATION_DESCRIPTOR_LAST_EDIT)) + val fabricIndex = tlvReader.getUInt(ContextSpecificTag(TAG_FABRIC_INDEX)) + + tlvReader.exitContainer() + + return EcosystemInformationClusterEcosystemLocationStruct( + uniqueLocationID, + locationDescriptor, + locationDescriptorLastEdit, + fabricIndex, + ) + } + } +} diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt new file mode 100644 index 00000000000000..727c2276191f81 --- /dev/null +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt @@ -0,0 +1,84 @@ +/* + * + * 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. + */ +package chip.devicecontroller.cluster.structs + +import chip.devicecontroller.cluster.* +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class EcosystemInformationClusterHomeLocationStruct( + val locationName: String, + val floorNumber: Int?, + val areaType: UInt?, +) { + override fun toString(): String = buildString { + append("EcosystemInformationClusterHomeLocationStruct {\n") + append("\tlocationName : $locationName\n") + append("\tfloorNumber : $floorNumber\n") + append("\tareaType : $areaType\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + put(ContextSpecificTag(TAG_LOCATION_NAME), locationName) + if (floorNumber != null) { + put(ContextSpecificTag(TAG_FLOOR_NUMBER), floorNumber) + } else { + putNull(ContextSpecificTag(TAG_FLOOR_NUMBER)) + } + if (areaType != null) { + put(ContextSpecificTag(TAG_AREA_TYPE), areaType) + } else { + putNull(ContextSpecificTag(TAG_AREA_TYPE)) + } + endStructure() + } + } + + companion object { + private const val TAG_LOCATION_NAME = 0 + private const val TAG_FLOOR_NUMBER = 1 + private const val TAG_AREA_TYPE = 2 + + fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): EcosystemInformationClusterHomeLocationStruct { + tlvReader.enterStructure(tlvTag) + val locationName = tlvReader.getString(ContextSpecificTag(TAG_LOCATION_NAME)) + val floorNumber = + if (!tlvReader.isNull()) { + tlvReader.getInt(ContextSpecificTag(TAG_FLOOR_NUMBER)) + } else { + tlvReader.getNull(ContextSpecificTag(TAG_FLOOR_NUMBER)) + null + } + val areaType = + if (!tlvReader.isNull()) { + tlvReader.getUInt(ContextSpecificTag(TAG_AREA_TYPE)) + } else { + tlvReader.getNull(ContextSpecificTag(TAG_AREA_TYPE)) + null + } + + tlvReader.exitContainer() + + return EcosystemInformationClusterHomeLocationStruct(locationName, floorNumber, areaType) + } + } +} diff --git a/src/controller/java/generated/java/matter/controller/cluster/clusters/EcosystemInformationCluster.kt b/src/controller/java/generated/java/matter/controller/cluster/clusters/EcosystemInformationCluster.kt new file mode 100644 index 00000000000000..5dfa4a7f8067ef --- /dev/null +++ b/src/controller/java/generated/java/matter/controller/cluster/clusters/EcosystemInformationCluster.kt @@ -0,0 +1,969 @@ +/* + * + * 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. + */ + +package matter.controller.cluster.clusters + +import java.time.Duration +import java.util.logging.Level +import java.util.logging.Logger +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.transform +import matter.controller.MatterController +import matter.controller.ReadData +import matter.controller.ReadRequest +import matter.controller.SubscribeRequest +import matter.controller.SubscriptionState +import matter.controller.UIntSubscriptionState +import matter.controller.UShortSubscriptionState +import matter.controller.cluster.structs.* +import matter.controller.model.AttributePath +import matter.tlv.AnonymousTag +import matter.tlv.TlvReader + +class EcosystemInformationCluster( + private val controller: MatterController, + private val endpointId: UShort, +) { + class RemovedOnAttribute(val value: ULong?) + + sealed class RemovedOnAttributeSubscriptionState { + data class Success(val value: ULong?) : RemovedOnAttributeSubscriptionState() + + data class Error(val exception: Exception) : RemovedOnAttributeSubscriptionState() + + object SubscriptionEstablished : RemovedOnAttributeSubscriptionState() + } + + class DeviceDirectoryAttribute(val value: List) + + sealed class DeviceDirectoryAttributeSubscriptionState { + data class Success(val value: List) : + DeviceDirectoryAttributeSubscriptionState() + + data class Error(val exception: Exception) : DeviceDirectoryAttributeSubscriptionState() + + object SubscriptionEstablished : DeviceDirectoryAttributeSubscriptionState() + } + + class LocationDirectoryAttribute( + val value: List + ) + + sealed class LocationDirectoryAttributeSubscriptionState { + data class Success(val value: List) : + LocationDirectoryAttributeSubscriptionState() + + data class Error(val exception: Exception) : LocationDirectoryAttributeSubscriptionState() + + object SubscriptionEstablished : LocationDirectoryAttributeSubscriptionState() + } + + class GeneratedCommandListAttribute(val value: List) + + sealed class GeneratedCommandListAttributeSubscriptionState { + data class Success(val value: List) : GeneratedCommandListAttributeSubscriptionState() + + data class Error(val exception: Exception) : GeneratedCommandListAttributeSubscriptionState() + + object SubscriptionEstablished : GeneratedCommandListAttributeSubscriptionState() + } + + class AcceptedCommandListAttribute(val value: List) + + sealed class AcceptedCommandListAttributeSubscriptionState { + data class Success(val value: List) : AcceptedCommandListAttributeSubscriptionState() + + data class Error(val exception: Exception) : AcceptedCommandListAttributeSubscriptionState() + + object SubscriptionEstablished : AcceptedCommandListAttributeSubscriptionState() + } + + class EventListAttribute(val value: List) + + sealed class EventListAttributeSubscriptionState { + data class Success(val value: List) : EventListAttributeSubscriptionState() + + data class Error(val exception: Exception) : EventListAttributeSubscriptionState() + + object SubscriptionEstablished : EventListAttributeSubscriptionState() + } + + class AttributeListAttribute(val value: List) + + sealed class AttributeListAttributeSubscriptionState { + data class Success(val value: List) : AttributeListAttributeSubscriptionState() + + data class Error(val exception: Exception) : AttributeListAttributeSubscriptionState() + + object SubscriptionEstablished : AttributeListAttributeSubscriptionState() + } + + suspend fun readRemovedOnAttribute(): RemovedOnAttribute { + val ATTRIBUTE_ID: UInt = 0u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Removedon attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: ULong? = + if (!tlvReader.isNull()) { + if (tlvReader.isNextTag(AnonymousTag)) { + tlvReader.getULong(AnonymousTag) + } else { + null + } + } else { + tlvReader.getNull(AnonymousTag) + null + } + + return RemovedOnAttribute(decodedValue) + } + + suspend fun subscribeRemovedOnAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 0u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + RemovedOnAttributeSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { "Removedon attribute not found in Node State update" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: ULong? = + if (!tlvReader.isNull()) { + if (tlvReader.isNextTag(AnonymousTag)) { + tlvReader.getULong(AnonymousTag) + } else { + null + } + } else { + tlvReader.getNull(AnonymousTag) + null + } + + decodedValue?.let { emit(RemovedOnAttributeSubscriptionState.Success(it)) } + } + SubscriptionState.SubscriptionEstablished -> { + emit(RemovedOnAttributeSubscriptionState.SubscriptionEstablished) + } + } + } + } + + suspend fun readDeviceDirectoryAttribute(): DeviceDirectoryAttribute { + val ATTRIBUTE_ID: UInt = 1u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Devicedirectory attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(EcosystemInformationClusterEcosystemDeviceStruct.fromTlv(AnonymousTag, tlvReader)) + } + tlvReader.exitContainer() + } + + return DeviceDirectoryAttribute(decodedValue) + } + + suspend fun subscribeDeviceDirectoryAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 1u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + DeviceDirectoryAttributeSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { + "Devicedirectory attribute not found in Node State update" + } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add( + EcosystemInformationClusterEcosystemDeviceStruct.fromTlv(AnonymousTag, tlvReader) + ) + } + tlvReader.exitContainer() + } + + emit(DeviceDirectoryAttributeSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(DeviceDirectoryAttributeSubscriptionState.SubscriptionEstablished) + } + } + } + } + + suspend fun readLocationDirectoryAttribute(): LocationDirectoryAttribute { + val ATTRIBUTE_ID: UInt = 2u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Locationdirectory attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(EcosystemInformationClusterEcosystemLocationStruct.fromTlv(AnonymousTag, tlvReader)) + } + tlvReader.exitContainer() + } + + return LocationDirectoryAttribute(decodedValue) + } + + suspend fun subscribeLocationDirectoryAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 2u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + LocationDirectoryAttributeSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { + "Locationdirectory attribute not found in Node State update" + } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add( + EcosystemInformationClusterEcosystemLocationStruct.fromTlv( + AnonymousTag, + tlvReader, + ) + ) + } + tlvReader.exitContainer() + } + + emit(LocationDirectoryAttributeSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(LocationDirectoryAttributeSubscriptionState.SubscriptionEstablished) + } + } + } + } + + suspend fun readGeneratedCommandListAttribute(): GeneratedCommandListAttribute { + val ATTRIBUTE_ID: UInt = 65528u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Generatedcommandlist attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getUInt(AnonymousTag)) + } + tlvReader.exitContainer() + } + + return GeneratedCommandListAttribute(decodedValue) + } + + suspend fun subscribeGeneratedCommandListAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 65528u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + GeneratedCommandListAttributeSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { + "Generatedcommandlist attribute not found in Node State update" + } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getUInt(AnonymousTag)) + } + tlvReader.exitContainer() + } + + emit(GeneratedCommandListAttributeSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(GeneratedCommandListAttributeSubscriptionState.SubscriptionEstablished) + } + } + } + } + + suspend fun readAcceptedCommandListAttribute(): AcceptedCommandListAttribute { + val ATTRIBUTE_ID: UInt = 65529u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Acceptedcommandlist attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getUInt(AnonymousTag)) + } + tlvReader.exitContainer() + } + + return AcceptedCommandListAttribute(decodedValue) + } + + suspend fun subscribeAcceptedCommandListAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 65529u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + AcceptedCommandListAttributeSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { + "Acceptedcommandlist attribute not found in Node State update" + } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getUInt(AnonymousTag)) + } + tlvReader.exitContainer() + } + + emit(AcceptedCommandListAttributeSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(AcceptedCommandListAttributeSubscriptionState.SubscriptionEstablished) + } + } + } + } + + suspend fun readEventListAttribute(): EventListAttribute { + val ATTRIBUTE_ID: UInt = 65530u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Eventlist attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getUInt(AnonymousTag)) + } + tlvReader.exitContainer() + } + + return EventListAttribute(decodedValue) + } + + suspend fun subscribeEventListAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 65530u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + EventListAttributeSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { "Eventlist attribute not found in Node State update" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getUInt(AnonymousTag)) + } + tlvReader.exitContainer() + } + + emit(EventListAttributeSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(EventListAttributeSubscriptionState.SubscriptionEstablished) + } + } + } + } + + suspend fun readAttributeListAttribute(): AttributeListAttribute { + val ATTRIBUTE_ID: UInt = 65531u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Attributelist attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getUInt(AnonymousTag)) + } + tlvReader.exitContainer() + } + + return AttributeListAttribute(decodedValue) + } + + suspend fun subscribeAttributeListAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 65531u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + AttributeListAttributeSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { "Attributelist attribute not found in Node State update" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getUInt(AnonymousTag)) + } + tlvReader.exitContainer() + } + + emit(AttributeListAttributeSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(AttributeListAttributeSubscriptionState.SubscriptionEstablished) + } + } + } + } + + suspend fun readFeatureMapAttribute(): UInt { + val ATTRIBUTE_ID: UInt = 65532u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Featuremap attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: UInt = tlvReader.getUInt(AnonymousTag) + + return decodedValue + } + + suspend fun subscribeFeatureMapAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 65532u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + UIntSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { "Featuremap attribute not found in Node State update" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: UInt = tlvReader.getUInt(AnonymousTag) + + emit(UIntSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(UIntSubscriptionState.SubscriptionEstablished) + } + } + } + } + + suspend fun readClusterRevisionAttribute(): UShort { + val ATTRIBUTE_ID: UInt = 65533u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Clusterrevision attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: UShort = tlvReader.getUShort(AnonymousTag) + + return decodedValue + } + + suspend fun subscribeClusterRevisionAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 65533u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + UShortSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { + "Clusterrevision attribute not found in Node State update" + } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: UShort = tlvReader.getUShort(AnonymousTag) + + emit(UShortSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(UShortSubscriptionState.SubscriptionEstablished) + } + } + } + } + + companion object { + private val logger = Logger.getLogger(EcosystemInformationCluster::class.java.name) + const val CLUSTER_ID: UInt = 1872u + } +} diff --git a/src/controller/java/generated/java/matter/controller/cluster/files.gni b/src/controller/java/generated/java/matter/controller/cluster/files.gni index 14ff82b93897cd..6b02ff0ca05bbb 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/files.gni +++ b/src/controller/java/generated/java/matter/controller/cluster/files.gni @@ -56,6 +56,10 @@ matter_structs_sources = [ "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/DishwasherModeClusterModeOptionStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/DishwasherModeClusterModeTagStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/DoorLockClusterCredentialStruct.kt", + "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterDeviceTypeStruct.kt", + "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterEcosystemDeviceStruct.kt", + "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt", + "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/ElectricalEnergyMeasurementClusterCumulativeEnergyResetStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/ElectricalEnergyMeasurementClusterEnergyMeasurementStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/ElectricalEnergyMeasurementClusterMeasurementAccuracyRangeStruct.kt", @@ -268,6 +272,7 @@ matter_clusters_sources = [ "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/clusters/DishwasherAlarmCluster.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/clusters/DishwasherModeCluster.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/clusters/DoorLockCluster.kt", + "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/clusters/EcosystemInformationCluster.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/clusters/ElectricalEnergyMeasurementCluster.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/clusters/ElectricalMeasurementCluster.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/clusters/ElectricalPowerMeasurementCluster.kt", diff --git a/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterDeviceTypeStruct.kt b/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterDeviceTypeStruct.kt new file mode 100644 index 00000000000000..1ebb7b6abd3f60 --- /dev/null +++ b/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterDeviceTypeStruct.kt @@ -0,0 +1,56 @@ +/* + * + * 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. + */ +package matter.controller.cluster.structs + +import matter.controller.cluster.* +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class EcosystemInformationClusterDeviceTypeStruct(val deviceType: UInt, val revision: UShort) { + override fun toString(): String = buildString { + append("EcosystemInformationClusterDeviceTypeStruct {\n") + append("\tdeviceType : $deviceType\n") + append("\trevision : $revision\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + put(ContextSpecificTag(TAG_DEVICE_TYPE), deviceType) + put(ContextSpecificTag(TAG_REVISION), revision) + endStructure() + } + } + + companion object { + private const val TAG_DEVICE_TYPE = 0 + private const val TAG_REVISION = 1 + + fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): EcosystemInformationClusterDeviceTypeStruct { + tlvReader.enterStructure(tlvTag) + val deviceType = tlvReader.getUInt(ContextSpecificTag(TAG_DEVICE_TYPE)) + val revision = tlvReader.getUShort(ContextSpecificTag(TAG_REVISION)) + + tlvReader.exitContainer() + + return EcosystemInformationClusterDeviceTypeStruct(deviceType, revision) + } + } +} diff --git a/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterEcosystemDeviceStruct.kt b/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterEcosystemDeviceStruct.kt new file mode 100644 index 00000000000000..0f04e02f973a58 --- /dev/null +++ b/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterEcosystemDeviceStruct.kt @@ -0,0 +1,142 @@ +/* + * + * 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. + */ +package matter.controller.cluster.structs + +import java.util.Optional +import matter.controller.cluster.* +import matter.tlv.AnonymousTag +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class EcosystemInformationClusterEcosystemDeviceStruct( + val deviceName: Optional, + val deviceNameLastEdit: Optional, + val bridgedEndpoint: UShort, + val originalEndpoint: UShort, + val deviceTypes: List, + val uniqueLocationIDs: List, + val uniqueLocationIDsLastEdit: ULong, + val fabricIndex: UByte, +) { + override fun toString(): String = buildString { + append("EcosystemInformationClusterEcosystemDeviceStruct {\n") + append("\tdeviceName : $deviceName\n") + append("\tdeviceNameLastEdit : $deviceNameLastEdit\n") + append("\tbridgedEndpoint : $bridgedEndpoint\n") + append("\toriginalEndpoint : $originalEndpoint\n") + append("\tdeviceTypes : $deviceTypes\n") + append("\tuniqueLocationIDs : $uniqueLocationIDs\n") + append("\tuniqueLocationIDsLastEdit : $uniqueLocationIDsLastEdit\n") + append("\tfabricIndex : $fabricIndex\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + if (deviceName.isPresent) { + val optdeviceName = deviceName.get() + put(ContextSpecificTag(TAG_DEVICE_NAME), optdeviceName) + } + if (deviceNameLastEdit.isPresent) { + val optdeviceNameLastEdit = deviceNameLastEdit.get() + put(ContextSpecificTag(TAG_DEVICE_NAME_LAST_EDIT), optdeviceNameLastEdit) + } + put(ContextSpecificTag(TAG_BRIDGED_ENDPOINT), bridgedEndpoint) + put(ContextSpecificTag(TAG_ORIGINAL_ENDPOINT), originalEndpoint) + startArray(ContextSpecificTag(TAG_DEVICE_TYPES)) + for (item in deviceTypes.iterator()) { + item.toTlv(AnonymousTag, this) + } + endArray() + startArray(ContextSpecificTag(TAG_UNIQUE_LOCATION_I_DS)) + for (item in uniqueLocationIDs.iterator()) { + put(AnonymousTag, item) + } + endArray() + put(ContextSpecificTag(TAG_UNIQUE_LOCATION_I_DS_LAST_EDIT), uniqueLocationIDsLastEdit) + put(ContextSpecificTag(TAG_FABRIC_INDEX), fabricIndex) + endStructure() + } + } + + companion object { + private const val TAG_DEVICE_NAME = 0 + private const val TAG_DEVICE_NAME_LAST_EDIT = 1 + private const val TAG_BRIDGED_ENDPOINT = 2 + private const val TAG_ORIGINAL_ENDPOINT = 3 + private const val TAG_DEVICE_TYPES = 4 + private const val TAG_UNIQUE_LOCATION_I_DS = 5 + private const val TAG_UNIQUE_LOCATION_I_DS_LAST_EDIT = 6 + private const val TAG_FABRIC_INDEX = 254 + + fun fromTlv( + tlvTag: Tag, + tlvReader: TlvReader, + ): EcosystemInformationClusterEcosystemDeviceStruct { + tlvReader.enterStructure(tlvTag) + val deviceName = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_DEVICE_NAME))) { + Optional.of(tlvReader.getString(ContextSpecificTag(TAG_DEVICE_NAME))) + } else { + Optional.empty() + } + val deviceNameLastEdit = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_DEVICE_NAME_LAST_EDIT))) { + Optional.of(tlvReader.getULong(ContextSpecificTag(TAG_DEVICE_NAME_LAST_EDIT))) + } else { + Optional.empty() + } + val bridgedEndpoint = tlvReader.getUShort(ContextSpecificTag(TAG_BRIDGED_ENDPOINT)) + val originalEndpoint = tlvReader.getUShort(ContextSpecificTag(TAG_ORIGINAL_ENDPOINT)) + val deviceTypes = + buildList { + tlvReader.enterArray(ContextSpecificTag(TAG_DEVICE_TYPES)) + while (!tlvReader.isEndOfContainer()) { + add(EcosystemInformationClusterDeviceTypeStruct.fromTlv(AnonymousTag, tlvReader)) + } + tlvReader.exitContainer() + } + val uniqueLocationIDs = + buildList { + tlvReader.enterArray(ContextSpecificTag(TAG_UNIQUE_LOCATION_I_DS)) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getString(AnonymousTag)) + } + tlvReader.exitContainer() + } + val uniqueLocationIDsLastEdit = + tlvReader.getULong(ContextSpecificTag(TAG_UNIQUE_LOCATION_I_DS_LAST_EDIT)) + val fabricIndex = tlvReader.getUByte(ContextSpecificTag(TAG_FABRIC_INDEX)) + + tlvReader.exitContainer() + + return EcosystemInformationClusterEcosystemDeviceStruct( + deviceName, + deviceNameLastEdit, + bridgedEndpoint, + originalEndpoint, + deviceTypes, + uniqueLocationIDs, + uniqueLocationIDsLastEdit, + fabricIndex, + ) + } + } +} diff --git a/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt b/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt new file mode 100644 index 00000000000000..0c218223efe1d7 --- /dev/null +++ b/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt @@ -0,0 +1,82 @@ +/* + * + * 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. + */ +package matter.controller.cluster.structs + +import matter.controller.cluster.* +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class EcosystemInformationClusterEcosystemLocationStruct( + val uniqueLocationID: String, + val locationDescriptor: EcosystemInformationClusterHomeLocationStruct, + val locationDescriptorLastEdit: ULong, + val fabricIndex: UByte, +) { + override fun toString(): String = buildString { + append("EcosystemInformationClusterEcosystemLocationStruct {\n") + append("\tuniqueLocationID : $uniqueLocationID\n") + append("\tlocationDescriptor : $locationDescriptor\n") + append("\tlocationDescriptorLastEdit : $locationDescriptorLastEdit\n") + append("\tfabricIndex : $fabricIndex\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + put(ContextSpecificTag(TAG_UNIQUE_LOCATION_I_D), uniqueLocationID) + locationDescriptor.toTlv(ContextSpecificTag(TAG_LOCATION_DESCRIPTOR), this) + put(ContextSpecificTag(TAG_LOCATION_DESCRIPTOR_LAST_EDIT), locationDescriptorLastEdit) + put(ContextSpecificTag(TAG_FABRIC_INDEX), fabricIndex) + endStructure() + } + } + + companion object { + private const val TAG_UNIQUE_LOCATION_I_D = 0 + private const val TAG_LOCATION_DESCRIPTOR = 1 + private const val TAG_LOCATION_DESCRIPTOR_LAST_EDIT = 2 + private const val TAG_FABRIC_INDEX = 254 + + fun fromTlv( + tlvTag: Tag, + tlvReader: TlvReader, + ): EcosystemInformationClusterEcosystemLocationStruct { + tlvReader.enterStructure(tlvTag) + val uniqueLocationID = tlvReader.getString(ContextSpecificTag(TAG_UNIQUE_LOCATION_I_D)) + val locationDescriptor = + EcosystemInformationClusterHomeLocationStruct.fromTlv( + ContextSpecificTag(TAG_LOCATION_DESCRIPTOR), + tlvReader, + ) + val locationDescriptorLastEdit = + tlvReader.getULong(ContextSpecificTag(TAG_LOCATION_DESCRIPTOR_LAST_EDIT)) + val fabricIndex = tlvReader.getUByte(ContextSpecificTag(TAG_FABRIC_INDEX)) + + tlvReader.exitContainer() + + return EcosystemInformationClusterEcosystemLocationStruct( + uniqueLocationID, + locationDescriptor, + locationDescriptorLastEdit, + fabricIndex, + ) + } + } +} diff --git a/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt b/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt new file mode 100644 index 00000000000000..1ecbf220139dba --- /dev/null +++ b/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt @@ -0,0 +1,84 @@ +/* + * + * 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. + */ +package matter.controller.cluster.structs + +import matter.controller.cluster.* +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class EcosystemInformationClusterHomeLocationStruct( + val locationName: String, + val floorNumber: Short?, + val areaType: UByte?, +) { + override fun toString(): String = buildString { + append("EcosystemInformationClusterHomeLocationStruct {\n") + append("\tlocationName : $locationName\n") + append("\tfloorNumber : $floorNumber\n") + append("\tareaType : $areaType\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + put(ContextSpecificTag(TAG_LOCATION_NAME), locationName) + if (floorNumber != null) { + put(ContextSpecificTag(TAG_FLOOR_NUMBER), floorNumber) + } else { + putNull(ContextSpecificTag(TAG_FLOOR_NUMBER)) + } + if (areaType != null) { + put(ContextSpecificTag(TAG_AREA_TYPE), areaType) + } else { + putNull(ContextSpecificTag(TAG_AREA_TYPE)) + } + endStructure() + } + } + + companion object { + private const val TAG_LOCATION_NAME = 0 + private const val TAG_FLOOR_NUMBER = 1 + private const val TAG_AREA_TYPE = 2 + + fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): EcosystemInformationClusterHomeLocationStruct { + tlvReader.enterStructure(tlvTag) + val locationName = tlvReader.getString(ContextSpecificTag(TAG_LOCATION_NAME)) + val floorNumber = + if (!tlvReader.isNull()) { + tlvReader.getShort(ContextSpecificTag(TAG_FLOOR_NUMBER)) + } else { + tlvReader.getNull(ContextSpecificTag(TAG_FLOOR_NUMBER)) + null + } + val areaType = + if (!tlvReader.isNull()) { + tlvReader.getUByte(ContextSpecificTag(TAG_AREA_TYPE)) + } else { + tlvReader.getNull(ContextSpecificTag(TAG_AREA_TYPE)) + null + } + + tlvReader.exitContainer() + + return EcosystemInformationClusterHomeLocationStruct(locationName, floorNumber, areaType) + } + } +} diff --git a/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp b/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp index 183a61a505c875..20a00dd454ae7f 100644 --- a/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp +++ b/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp @@ -42666,6 +42666,461 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR } break; } + case app::Clusters::EcosystemInformation::Id: { + using namespace app::Clusters::EcosystemInformation; + switch (aPath.mAttributeId) + { + case Attributes::RemovedOn::Id: { + using TypeInfo = Attributes::RemovedOn::TypeInfo; + TypeInfo::DecodableType cppValue; + *aError = app::DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) + { + return nullptr; + } + jobject value; + if (cppValue.IsNull()) + { + value = nullptr; + } + else + { + std::string valueClassName = "java/lang/Long"; + std::string valueCtorSignature = "(J)V"; + jlong jnivalue = static_cast(cppValue.Value()); + chip::JniReferences::GetInstance().CreateBoxedObject(valueClassName.c_str(), valueCtorSignature.c_str(), + jnivalue, value); + } + return value; + } + case Attributes::DeviceDirectory::Id: { + using TypeInfo = Attributes::DeviceDirectory::TypeInfo; + TypeInfo::DecodableType cppValue; + *aError = app::DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) + { + return nullptr; + } + jobject value; + chip::JniReferences::GetInstance().CreateArrayList(value); + + auto iter_value_0 = cppValue.begin(); + while (iter_value_0.Next()) + { + auto & entry_0 = iter_value_0.GetValue(); + jobject newElement_0; + jobject newElement_0_deviceName; + if (!entry_0.deviceName.HasValue()) + { + chip::JniReferences::GetInstance().CreateOptional(nullptr, newElement_0_deviceName); + } + else + { + jobject newElement_0_deviceNameInsideOptional; + LogErrorOnFailure(chip::JniReferences::GetInstance().CharToStringUTF(entry_0.deviceName.Value(), + newElement_0_deviceNameInsideOptional)); + chip::JniReferences::GetInstance().CreateOptional(newElement_0_deviceNameInsideOptional, + newElement_0_deviceName); + } + jobject newElement_0_deviceNameLastEdit; + if (!entry_0.deviceNameLastEdit.HasValue()) + { + chip::JniReferences::GetInstance().CreateOptional(nullptr, newElement_0_deviceNameLastEdit); + } + else + { + jobject newElement_0_deviceNameLastEditInsideOptional; + std::string newElement_0_deviceNameLastEditInsideOptionalClassName = "java/lang/Long"; + std::string newElement_0_deviceNameLastEditInsideOptionalCtorSignature = "(J)V"; + jlong jninewElement_0_deviceNameLastEditInsideOptional = static_cast(entry_0.deviceNameLastEdit.Value()); + chip::JniReferences::GetInstance().CreateBoxedObject( + newElement_0_deviceNameLastEditInsideOptionalClassName.c_str(), + newElement_0_deviceNameLastEditInsideOptionalCtorSignature.c_str(), + jninewElement_0_deviceNameLastEditInsideOptional, newElement_0_deviceNameLastEditInsideOptional); + chip::JniReferences::GetInstance().CreateOptional(newElement_0_deviceNameLastEditInsideOptional, + newElement_0_deviceNameLastEdit); + } + jobject newElement_0_bridgedEndpoint; + std::string newElement_0_bridgedEndpointClassName = "java/lang/Integer"; + std::string newElement_0_bridgedEndpointCtorSignature = "(I)V"; + jint jninewElement_0_bridgedEndpoint = static_cast(entry_0.bridgedEndpoint); + chip::JniReferences::GetInstance().CreateBoxedObject( + newElement_0_bridgedEndpointClassName.c_str(), newElement_0_bridgedEndpointCtorSignature.c_str(), + jninewElement_0_bridgedEndpoint, newElement_0_bridgedEndpoint); + jobject newElement_0_originalEndpoint; + std::string newElement_0_originalEndpointClassName = "java/lang/Integer"; + std::string newElement_0_originalEndpointCtorSignature = "(I)V"; + jint jninewElement_0_originalEndpoint = static_cast(entry_0.originalEndpoint); + chip::JniReferences::GetInstance().CreateBoxedObject( + newElement_0_originalEndpointClassName.c_str(), newElement_0_originalEndpointCtorSignature.c_str(), + jninewElement_0_originalEndpoint, newElement_0_originalEndpoint); + jobject newElement_0_deviceTypes; + chip::JniReferences::GetInstance().CreateArrayList(newElement_0_deviceTypes); + + auto iter_newElement_0_deviceTypes_2 = entry_0.deviceTypes.begin(); + while (iter_newElement_0_deviceTypes_2.Next()) + { + auto & entry_2 = iter_newElement_0_deviceTypes_2.GetValue(); + jobject newElement_2; + jobject newElement_2_deviceType; + std::string newElement_2_deviceTypeClassName = "java/lang/Long"; + std::string newElement_2_deviceTypeCtorSignature = "(J)V"; + jlong jninewElement_2_deviceType = static_cast(entry_2.deviceType); + chip::JniReferences::GetInstance().CreateBoxedObject( + newElement_2_deviceTypeClassName.c_str(), newElement_2_deviceTypeCtorSignature.c_str(), + jninewElement_2_deviceType, newElement_2_deviceType); + jobject newElement_2_revision; + std::string newElement_2_revisionClassName = "java/lang/Integer"; + std::string newElement_2_revisionCtorSignature = "(I)V"; + jint jninewElement_2_revision = static_cast(entry_2.revision); + chip::JniReferences::GetInstance().CreateBoxedObject(newElement_2_revisionClassName.c_str(), + newElement_2_revisionCtorSignature.c_str(), + jninewElement_2_revision, newElement_2_revision); + + jclass deviceTypeStructStructClass_3; + err = chip::JniReferences::GetInstance().GetLocalClassRef( + env, "chip/devicecontroller/ChipStructs$EcosystemInformationClusterDeviceTypeStruct", + deviceTypeStructStructClass_3); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Could not find class ChipStructs$EcosystemInformationClusterDeviceTypeStruct"); + return nullptr; + } + + jmethodID deviceTypeStructStructCtor_3; + err = chip::JniReferences::GetInstance().FindMethod(env, deviceTypeStructStructClass_3, "", + "(Ljava/lang/Long;Ljava/lang/Integer;)V", + &deviceTypeStructStructCtor_3); + if (err != CHIP_NO_ERROR || deviceTypeStructStructCtor_3 == nullptr) + { + ChipLogError(Zcl, "Could not find ChipStructs$EcosystemInformationClusterDeviceTypeStruct constructor"); + return nullptr; + } + + newElement_2 = env->NewObject(deviceTypeStructStructClass_3, deviceTypeStructStructCtor_3, + newElement_2_deviceType, newElement_2_revision); + chip::JniReferences::GetInstance().AddToList(newElement_0_deviceTypes, newElement_2); + } + jobject newElement_0_uniqueLocationIDs; + chip::JniReferences::GetInstance().CreateArrayList(newElement_0_uniqueLocationIDs); + + auto iter_newElement_0_uniqueLocationIDs_2 = entry_0.uniqueLocationIDs.begin(); + while (iter_newElement_0_uniqueLocationIDs_2.Next()) + { + auto & entry_2 = iter_newElement_0_uniqueLocationIDs_2.GetValue(); + jobject newElement_2; + LogErrorOnFailure(chip::JniReferences::GetInstance().CharToStringUTF(entry_2, newElement_2)); + chip::JniReferences::GetInstance().AddToList(newElement_0_uniqueLocationIDs, newElement_2); + } + jobject newElement_0_uniqueLocationIDsLastEdit; + std::string newElement_0_uniqueLocationIDsLastEditClassName = "java/lang/Long"; + std::string newElement_0_uniqueLocationIDsLastEditCtorSignature = "(J)V"; + jlong jninewElement_0_uniqueLocationIDsLastEdit = static_cast(entry_0.uniqueLocationIDsLastEdit); + chip::JniReferences::GetInstance().CreateBoxedObject( + newElement_0_uniqueLocationIDsLastEditClassName.c_str(), + newElement_0_uniqueLocationIDsLastEditCtorSignature.c_str(), jninewElement_0_uniqueLocationIDsLastEdit, + newElement_0_uniqueLocationIDsLastEdit); + jobject newElement_0_fabricIndex; + std::string newElement_0_fabricIndexClassName = "java/lang/Integer"; + std::string newElement_0_fabricIndexCtorSignature = "(I)V"; + jint jninewElement_0_fabricIndex = static_cast(entry_0.fabricIndex); + chip::JniReferences::GetInstance().CreateBoxedObject(newElement_0_fabricIndexClassName.c_str(), + newElement_0_fabricIndexCtorSignature.c_str(), + jninewElement_0_fabricIndex, newElement_0_fabricIndex); + + jclass ecosystemDeviceStructStructClass_1; + err = chip::JniReferences::GetInstance().GetLocalClassRef( + env, "chip/devicecontroller/ChipStructs$EcosystemInformationClusterEcosystemDeviceStruct", + ecosystemDeviceStructStructClass_1); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Could not find class ChipStructs$EcosystemInformationClusterEcosystemDeviceStruct"); + return nullptr; + } + + jmethodID ecosystemDeviceStructStructCtor_1; + err = chip::JniReferences::GetInstance().FindMethod( + env, ecosystemDeviceStructStructClass_1, "", + "(Ljava/util/Optional;Ljava/util/Optional;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/util/ArrayList;Ljava/" + "util/ArrayList;Ljava/lang/Long;Ljava/lang/Integer;)V", + &ecosystemDeviceStructStructCtor_1); + if (err != CHIP_NO_ERROR || ecosystemDeviceStructStructCtor_1 == nullptr) + { + ChipLogError(Zcl, "Could not find ChipStructs$EcosystemInformationClusterEcosystemDeviceStruct constructor"); + return nullptr; + } + + newElement_0 = + env->NewObject(ecosystemDeviceStructStructClass_1, ecosystemDeviceStructStructCtor_1, newElement_0_deviceName, + newElement_0_deviceNameLastEdit, newElement_0_bridgedEndpoint, newElement_0_originalEndpoint, + newElement_0_deviceTypes, newElement_0_uniqueLocationIDs, newElement_0_uniqueLocationIDsLastEdit, + newElement_0_fabricIndex); + chip::JniReferences::GetInstance().AddToList(value, newElement_0); + } + return value; + } + case Attributes::LocationDirectory::Id: { + using TypeInfo = Attributes::LocationDirectory::TypeInfo; + TypeInfo::DecodableType cppValue; + *aError = app::DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) + { + return nullptr; + } + jobject value; + chip::JniReferences::GetInstance().CreateArrayList(value); + + auto iter_value_0 = cppValue.begin(); + while (iter_value_0.Next()) + { + auto & entry_0 = iter_value_0.GetValue(); + jobject newElement_0; + jobject newElement_0_uniqueLocationID; + LogErrorOnFailure( + chip::JniReferences::GetInstance().CharToStringUTF(entry_0.uniqueLocationID, newElement_0_uniqueLocationID)); + jobject newElement_0_locationDescriptor; + jobject newElement_0_locationDescriptor_locationName; + LogErrorOnFailure(chip::JniReferences::GetInstance().CharToStringUTF(entry_0.locationDescriptor.locationName, + newElement_0_locationDescriptor_locationName)); + jobject newElement_0_locationDescriptor_floorNumber; + if (entry_0.locationDescriptor.floorNumber.IsNull()) + { + newElement_0_locationDescriptor_floorNumber = nullptr; + } + else + { + std::string newElement_0_locationDescriptor_floorNumberClassName = "java/lang/Integer"; + std::string newElement_0_locationDescriptor_floorNumberCtorSignature = "(I)V"; + jint jninewElement_0_locationDescriptor_floorNumber = + static_cast(entry_0.locationDescriptor.floorNumber.Value()); + chip::JniReferences::GetInstance().CreateBoxedObject( + newElement_0_locationDescriptor_floorNumberClassName.c_str(), + newElement_0_locationDescriptor_floorNumberCtorSignature.c_str(), + jninewElement_0_locationDescriptor_floorNumber, newElement_0_locationDescriptor_floorNumber); + } + jobject newElement_0_locationDescriptor_areaType; + if (entry_0.locationDescriptor.areaType.IsNull()) + { + newElement_0_locationDescriptor_areaType = nullptr; + } + else + { + std::string newElement_0_locationDescriptor_areaTypeClassName = "java/lang/Integer"; + std::string newElement_0_locationDescriptor_areaTypeCtorSignature = "(I)V"; + jint jninewElement_0_locationDescriptor_areaType = + static_cast(entry_0.locationDescriptor.areaType.Value()); + chip::JniReferences::GetInstance().CreateBoxedObject( + newElement_0_locationDescriptor_areaTypeClassName.c_str(), + newElement_0_locationDescriptor_areaTypeCtorSignature.c_str(), jninewElement_0_locationDescriptor_areaType, + newElement_0_locationDescriptor_areaType); + } + + jclass homeLocationStructStructClass_2; + err = chip::JniReferences::GetInstance().GetLocalClassRef( + env, "chip/devicecontroller/ChipStructs$EcosystemInformationClusterHomeLocationStruct", + homeLocationStructStructClass_2); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Could not find class ChipStructs$EcosystemInformationClusterHomeLocationStruct"); + return nullptr; + } + + jmethodID homeLocationStructStructCtor_2; + err = chip::JniReferences::GetInstance().FindMethod(env, homeLocationStructStructClass_2, "", + "(Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;)V", + &homeLocationStructStructCtor_2); + if (err != CHIP_NO_ERROR || homeLocationStructStructCtor_2 == nullptr) + { + ChipLogError(Zcl, "Could not find ChipStructs$EcosystemInformationClusterHomeLocationStruct constructor"); + return nullptr; + } + + newElement_0_locationDescriptor = env->NewObject( + homeLocationStructStructClass_2, homeLocationStructStructCtor_2, newElement_0_locationDescriptor_locationName, + newElement_0_locationDescriptor_floorNumber, newElement_0_locationDescriptor_areaType); + jobject newElement_0_locationDescriptorLastEdit; + std::string newElement_0_locationDescriptorLastEditClassName = "java/lang/Long"; + std::string newElement_0_locationDescriptorLastEditCtorSignature = "(J)V"; + jlong jninewElement_0_locationDescriptorLastEdit = static_cast(entry_0.locationDescriptorLastEdit); + chip::JniReferences::GetInstance().CreateBoxedObject( + newElement_0_locationDescriptorLastEditClassName.c_str(), + newElement_0_locationDescriptorLastEditCtorSignature.c_str(), jninewElement_0_locationDescriptorLastEdit, + newElement_0_locationDescriptorLastEdit); + jobject newElement_0_fabricIndex; + std::string newElement_0_fabricIndexClassName = "java/lang/Integer"; + std::string newElement_0_fabricIndexCtorSignature = "(I)V"; + jint jninewElement_0_fabricIndex = static_cast(entry_0.fabricIndex); + chip::JniReferences::GetInstance().CreateBoxedObject(newElement_0_fabricIndexClassName.c_str(), + newElement_0_fabricIndexCtorSignature.c_str(), + jninewElement_0_fabricIndex, newElement_0_fabricIndex); + + jclass ecosystemLocationStructStructClass_1; + err = chip::JniReferences::GetInstance().GetLocalClassRef( + env, "chip/devicecontroller/ChipStructs$EcosystemInformationClusterEcosystemLocationStruct", + ecosystemLocationStructStructClass_1); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Could not find class ChipStructs$EcosystemInformationClusterEcosystemLocationStruct"); + return nullptr; + } + + jmethodID ecosystemLocationStructStructCtor_1; + err = chip::JniReferences::GetInstance().FindMethod( + env, ecosystemLocationStructStructClass_1, "", + "(Ljava/lang/String;Lchip/devicecontroller/ChipStructs$EcosystemInformationClusterHomeLocationStruct;Ljava/" + "lang/Long;Ljava/lang/Integer;)V", + &ecosystemLocationStructStructCtor_1); + if (err != CHIP_NO_ERROR || ecosystemLocationStructStructCtor_1 == nullptr) + { + ChipLogError(Zcl, "Could not find ChipStructs$EcosystemInformationClusterEcosystemLocationStruct constructor"); + return nullptr; + } + + newElement_0 = env->NewObject(ecosystemLocationStructStructClass_1, ecosystemLocationStructStructCtor_1, + newElement_0_uniqueLocationID, newElement_0_locationDescriptor, + newElement_0_locationDescriptorLastEdit, newElement_0_fabricIndex); + chip::JniReferences::GetInstance().AddToList(value, newElement_0); + } + return value; + } + case Attributes::GeneratedCommandList::Id: { + using TypeInfo = Attributes::GeneratedCommandList::TypeInfo; + TypeInfo::DecodableType cppValue; + *aError = app::DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) + { + return nullptr; + } + jobject value; + chip::JniReferences::GetInstance().CreateArrayList(value); + + auto iter_value_0 = cppValue.begin(); + while (iter_value_0.Next()) + { + auto & entry_0 = iter_value_0.GetValue(); + jobject newElement_0; + std::string newElement_0ClassName = "java/lang/Long"; + std::string newElement_0CtorSignature = "(J)V"; + jlong jninewElement_0 = static_cast(entry_0); + chip::JniReferences::GetInstance().CreateBoxedObject( + newElement_0ClassName.c_str(), newElement_0CtorSignature.c_str(), jninewElement_0, newElement_0); + chip::JniReferences::GetInstance().AddToList(value, newElement_0); + } + return value; + } + case Attributes::AcceptedCommandList::Id: { + using TypeInfo = Attributes::AcceptedCommandList::TypeInfo; + TypeInfo::DecodableType cppValue; + *aError = app::DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) + { + return nullptr; + } + jobject value; + chip::JniReferences::GetInstance().CreateArrayList(value); + + auto iter_value_0 = cppValue.begin(); + while (iter_value_0.Next()) + { + auto & entry_0 = iter_value_0.GetValue(); + jobject newElement_0; + std::string newElement_0ClassName = "java/lang/Long"; + std::string newElement_0CtorSignature = "(J)V"; + jlong jninewElement_0 = static_cast(entry_0); + chip::JniReferences::GetInstance().CreateBoxedObject( + newElement_0ClassName.c_str(), newElement_0CtorSignature.c_str(), jninewElement_0, newElement_0); + chip::JniReferences::GetInstance().AddToList(value, newElement_0); + } + return value; + } + case Attributes::EventList::Id: { + using TypeInfo = Attributes::EventList::TypeInfo; + TypeInfo::DecodableType cppValue; + *aError = app::DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) + { + return nullptr; + } + jobject value; + chip::JniReferences::GetInstance().CreateArrayList(value); + + auto iter_value_0 = cppValue.begin(); + while (iter_value_0.Next()) + { + auto & entry_0 = iter_value_0.GetValue(); + jobject newElement_0; + std::string newElement_0ClassName = "java/lang/Long"; + std::string newElement_0CtorSignature = "(J)V"; + jlong jninewElement_0 = static_cast(entry_0); + chip::JniReferences::GetInstance().CreateBoxedObject( + newElement_0ClassName.c_str(), newElement_0CtorSignature.c_str(), jninewElement_0, newElement_0); + chip::JniReferences::GetInstance().AddToList(value, newElement_0); + } + return value; + } + case Attributes::AttributeList::Id: { + using TypeInfo = Attributes::AttributeList::TypeInfo; + TypeInfo::DecodableType cppValue; + *aError = app::DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) + { + return nullptr; + } + jobject value; + chip::JniReferences::GetInstance().CreateArrayList(value); + + auto iter_value_0 = cppValue.begin(); + while (iter_value_0.Next()) + { + auto & entry_0 = iter_value_0.GetValue(); + jobject newElement_0; + std::string newElement_0ClassName = "java/lang/Long"; + std::string newElement_0CtorSignature = "(J)V"; + jlong jninewElement_0 = static_cast(entry_0); + chip::JniReferences::GetInstance().CreateBoxedObject( + newElement_0ClassName.c_str(), newElement_0CtorSignature.c_str(), jninewElement_0, newElement_0); + chip::JniReferences::GetInstance().AddToList(value, newElement_0); + } + return value; + } + case Attributes::FeatureMap::Id: { + using TypeInfo = Attributes::FeatureMap::TypeInfo; + TypeInfo::DecodableType cppValue; + *aError = app::DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) + { + return nullptr; + } + jobject value; + std::string valueClassName = "java/lang/Long"; + std::string valueCtorSignature = "(J)V"; + jlong jnivalue = static_cast(cppValue); + chip::JniReferences::GetInstance().CreateBoxedObject(valueClassName.c_str(), valueCtorSignature.c_str(), + jnivalue, value); + return value; + } + case Attributes::ClusterRevision::Id: { + using TypeInfo = Attributes::ClusterRevision::TypeInfo; + TypeInfo::DecodableType cppValue; + *aError = app::DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) + { + return nullptr; + } + jobject value; + std::string valueClassName = "java/lang/Integer"; + std::string valueCtorSignature = "(I)V"; + jint jnivalue = static_cast(cppValue); + chip::JniReferences::GetInstance().CreateBoxedObject(valueClassName.c_str(), valueCtorSignature.c_str(), jnivalue, + value); + return value; + } + default: + *aError = CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH_IB; + break; + } + break; + } case app::Clusters::CommissionerControl::Id: { using namespace app::Clusters::CommissionerControl; switch (aPath.mAttributeId) diff --git a/src/controller/java/zap-generated/CHIPEventTLVValueDecoder.cpp b/src/controller/java/zap-generated/CHIPEventTLVValueDecoder.cpp index 8d7826aed4d310..42868bfd0465b1 100644 --- a/src/controller/java/zap-generated/CHIPEventTLVValueDecoder.cpp +++ b/src/controller/java/zap-generated/CHIPEventTLVValueDecoder.cpp @@ -7979,6 +7979,16 @@ jobject DecodeEventValue(const app::ConcreteEventPath & aPath, TLV::TLVReader & } break; } + case app::Clusters::EcosystemInformation::Id: { + using namespace app::Clusters::EcosystemInformation; + switch (aPath.mEventId) + { + default: + *aError = CHIP_ERROR_IM_MALFORMED_EVENT_PATH_IB; + break; + } + break; + } case app::Clusters::CommissionerControl::Id: { using namespace app::Clusters::CommissionerControl; switch (aPath.mEventId) diff --git a/src/controller/python/chip/clusters/CHIPClusters.py b/src/controller/python/chip/clusters/CHIPClusters.py index c9f559f9f61915..6621bbc4bea2a6 100644 --- a/src/controller/python/chip/clusters/CHIPClusters.py +++ b/src/controller/python/chip/clusters/CHIPClusters.py @@ -13275,6 +13275,68 @@ class ChipClusters: }, }, } + _ECOSYSTEM_INFORMATION_CLUSTER_INFO = { + "clusterName": "EcosystemInformation", + "clusterId": 0x00000750, + "commands": { + }, + "attributes": { + 0x00000000: { + "attributeName": "RemovedOn", + "attributeId": 0x00000000, + "type": "int", + "reportable": True, + }, + 0x00000001: { + "attributeName": "DeviceDirectory", + "attributeId": 0x00000001, + "type": "", + "reportable": True, + }, + 0x00000002: { + "attributeName": "LocationDirectory", + "attributeId": 0x00000002, + "type": "", + "reportable": True, + }, + 0x0000FFF8: { + "attributeName": "GeneratedCommandList", + "attributeId": 0x0000FFF8, + "type": "int", + "reportable": True, + }, + 0x0000FFF9: { + "attributeName": "AcceptedCommandList", + "attributeId": 0x0000FFF9, + "type": "int", + "reportable": True, + }, + 0x0000FFFA: { + "attributeName": "EventList", + "attributeId": 0x0000FFFA, + "type": "int", + "reportable": True, + }, + 0x0000FFFB: { + "attributeName": "AttributeList", + "attributeId": 0x0000FFFB, + "type": "int", + "reportable": True, + }, + 0x0000FFFC: { + "attributeName": "FeatureMap", + "attributeId": 0x0000FFFC, + "type": "int", + "reportable": True, + }, + 0x0000FFFD: { + "attributeName": "ClusterRevision", + "attributeId": 0x0000FFFD, + "type": "int", + "reportable": True, + }, + }, + } _COMMISSIONER_CONTROL_CLUSTER_INFO = { "clusterName": "CommissionerControl", "clusterId": 0x00000751, @@ -15257,6 +15319,7 @@ class ChipClusters: 0x0000050E: _ACCOUNT_LOGIN_CLUSTER_INFO, 0x0000050F: _CONTENT_CONTROL_CLUSTER_INFO, 0x00000510: _CONTENT_APP_OBSERVER_CLUSTER_INFO, + 0x00000750: _ECOSYSTEM_INFORMATION_CLUSTER_INFO, 0x00000751: _COMMISSIONER_CONTROL_CLUSTER_INFO, 0x00000B04: _ELECTRICAL_MEASUREMENT_CLUSTER_INFO, 0xFFF1FC05: _UNIT_TESTING_CLUSTER_INFO, @@ -15385,6 +15448,7 @@ class ChipClusters: "AccountLogin": _ACCOUNT_LOGIN_CLUSTER_INFO, "ContentControl": _CONTENT_CONTROL_CLUSTER_INFO, "ContentAppObserver": _CONTENT_APP_OBSERVER_CLUSTER_INFO, + "EcosystemInformation": _ECOSYSTEM_INFORMATION_CLUSTER_INFO, "CommissionerControl": _COMMISSIONER_CONTROL_CLUSTER_INFO, "ElectricalMeasurement": _ELECTRICAL_MEASUREMENT_CLUSTER_INFO, "UnitTesting": _UNIT_TESTING_CLUSTER_INFO, diff --git a/src/controller/python/chip/clusters/Objects.py b/src/controller/python/chip/clusters/Objects.py index 654a6d292a6910..2ad8d02fb1aae2 100644 --- a/src/controller/python/chip/clusters/Objects.py +++ b/src/controller/python/chip/clusters/Objects.py @@ -46633,6 +46633,355 @@ def attribute_type(cls) -> ClusterObjectFieldDescriptor: value: 'uint' = 0 +@dataclass +class EcosystemInformation(Cluster): + id: typing.ClassVar[int] = 0x00000750 + + @ChipUtility.classproperty + def descriptor(cls) -> ClusterObjectDescriptor: + return ClusterObjectDescriptor( + Fields=[ + ClusterObjectFieldDescriptor(Label="removedOn", Tag=0x00000000, Type=typing.Union[None, Nullable, uint]), + ClusterObjectFieldDescriptor(Label="deviceDirectory", Tag=0x00000001, Type=typing.List[EcosystemInformation.Structs.EcosystemDeviceStruct]), + ClusterObjectFieldDescriptor(Label="locationDirectory", Tag=0x00000002, Type=typing.List[EcosystemInformation.Structs.EcosystemLocationStruct]), + ClusterObjectFieldDescriptor(Label="generatedCommandList", Tag=0x0000FFF8, Type=typing.List[uint]), + ClusterObjectFieldDescriptor(Label="acceptedCommandList", Tag=0x0000FFF9, Type=typing.List[uint]), + ClusterObjectFieldDescriptor(Label="eventList", Tag=0x0000FFFA, Type=typing.List[uint]), + ClusterObjectFieldDescriptor(Label="attributeList", Tag=0x0000FFFB, Type=typing.List[uint]), + ClusterObjectFieldDescriptor(Label="featureMap", Tag=0x0000FFFC, Type=uint), + ClusterObjectFieldDescriptor(Label="clusterRevision", Tag=0x0000FFFD, Type=uint), + ]) + + removedOn: 'typing.Union[None, Nullable, uint]' = None + deviceDirectory: 'typing.List[EcosystemInformation.Structs.EcosystemDeviceStruct]' = None + locationDirectory: 'typing.List[EcosystemInformation.Structs.EcosystemLocationStruct]' = None + generatedCommandList: 'typing.List[uint]' = None + acceptedCommandList: 'typing.List[uint]' = None + eventList: 'typing.List[uint]' = None + attributeList: 'typing.List[uint]' = None + featureMap: 'uint' = None + clusterRevision: 'uint' = None + + class Enums: + class AreaTypeTag(MatterIntEnum): + kAisle = 0x00 + kAttic = 0x01 + kBackDoor = 0x02 + kBackYard = 0x03 + kBalcony = 0x04 + kBallroom = 0x05 + kBathroom = 0x06 + kBedroom = 0x07 + kBorder = 0x08 + kBoxroom = 0x09 + kBreakfastRoom = 0x0A + kCarport = 0x0B + kCellar = 0x0C + kCloakroom = 0x0D + kCloset = 0x0E + kConservatory = 0x0F + kCorridor = 0x10 + kCraftRoom = 0x11 + kCupboard = 0x12 + kDeck = 0x13 + kDen = 0x14 + kDining = 0x15 + kDrawingRoom = 0x16 + kDressingRoom = 0x17 + kDriveway = 0x18 + kElevator = 0x19 + kEnsuite = 0x1A + kEntrance = 0x1B + kEntryway = 0x1C + kFamilyRoom = 0x1D + kFoyer = 0x1E + kFrontDoor = 0x1F + kFrontYard = 0x20 + kGameRoom = 0x21 + kGarage = 0x22 + kGarageDoor = 0x23 + kGarden = 0x24 + kGardenDoor = 0x25 + kGuestBathroom = 0x26 + kGuestBedroom = 0x27 + kGuestRestroom = 0x28 + kGuestRoom = 0x29 + kGym = 0x2A + kHallway = 0x2B + kHearthRoom = 0x2C + kKidsRoom = 0x2D + kKidsBedroom = 0x2E + kKitchen = 0x2F + kLarder = 0x30 + kLaundryRoom = 0x31 + kLawn = 0x32 + kLibrary = 0x33 + kLivingRoom = 0x34 + kLounge = 0x35 + kMediaTvRoom = 0x36 + kMudRoom = 0x37 + kMusicRoom = 0x38 + kNursery = 0x39 + kOffice = 0x3A + kOutdoorKitchen = 0x3B + kOutside = 0x3C + kPantry = 0x3D + kParkingLot = 0x3E + kParlor = 0x3F + kPatio = 0x40 + kPlayRoom = 0x41 + kPoolRoom = 0x42 + kPorch = 0x43 + kPrimaryBathroom = 0x44 + kPrimaryBedroom = 0x45 + kRamp = 0x46 + kReceptionRoom = 0x47 + kRecreationRoom = 0x48 + kRestroom = 0x49 + kRoof = 0x4A + kSauna = 0x4B + kScullery = 0x4C + kSewingRoom = 0x4D + kShed = 0x4E + kSideDoor = 0x4F + kSideYard = 0x50 + kSittingRoom = 0x51 + kSnug = 0x52 + kSpa = 0x53 + kStaircase = 0x54 + kSteamRoom = 0x55 + kStorageRoom = 0x56 + kStudio = 0x57 + kStudy = 0x58 + kSunRoom = 0x59 + kSwimmingPool = 0x5A + kTerrace = 0x5B + kUtilityRoom = 0x5C + kWard = 0x5D + kWorkshop = 0x5E + # All received enum values that are not listed above will be mapped + # to kUnknownEnumValue. This is a helper enum value that should only + # be used by code to process how it handles receiving and unknown + # enum value. This specific should never be transmitted. + kUnknownEnumValue = 95, + + class Structs: + @dataclass + class HomeLocationStruct(ClusterObject): + @ChipUtility.classproperty + def descriptor(cls) -> ClusterObjectDescriptor: + return ClusterObjectDescriptor( + Fields=[ + ClusterObjectFieldDescriptor(Label="locationName", Tag=0, Type=str), + ClusterObjectFieldDescriptor(Label="floorNumber", Tag=1, Type=typing.Union[Nullable, int]), + ClusterObjectFieldDescriptor(Label="areaType", Tag=2, Type=typing.Union[Nullable, EcosystemInformation.Enums.AreaTypeTag]), + ]) + + locationName: 'str' = "" + floorNumber: 'typing.Union[Nullable, int]' = NullValue + areaType: 'typing.Union[Nullable, EcosystemInformation.Enums.AreaTypeTag]' = NullValue + + @dataclass + class EcosystemLocationStruct(ClusterObject): + @ChipUtility.classproperty + def descriptor(cls) -> ClusterObjectDescriptor: + return ClusterObjectDescriptor( + Fields=[ + ClusterObjectFieldDescriptor(Label="uniqueLocationID", Tag=0, Type=str), + ClusterObjectFieldDescriptor(Label="locationDescriptor", Tag=1, Type=EcosystemInformation.Structs.HomeLocationStruct), + ClusterObjectFieldDescriptor(Label="locationDescriptorLastEdit", Tag=2, Type=uint), + ClusterObjectFieldDescriptor(Label="fabricIndex", Tag=254, Type=uint), + ]) + + uniqueLocationID: 'str' = "" + locationDescriptor: 'EcosystemInformation.Structs.HomeLocationStruct' = field(default_factory=lambda: EcosystemInformation.Structs.HomeLocationStruct()) + locationDescriptorLastEdit: 'uint' = 0 + fabricIndex: 'uint' = 0 + + @dataclass + class DeviceTypeStruct(ClusterObject): + @ChipUtility.classproperty + def descriptor(cls) -> ClusterObjectDescriptor: + return ClusterObjectDescriptor( + Fields=[ + ClusterObjectFieldDescriptor(Label="deviceType", Tag=0, Type=uint), + ClusterObjectFieldDescriptor(Label="revision", Tag=1, Type=uint), + ]) + + deviceType: 'uint' = 0 + revision: 'uint' = 0 + + @dataclass + class EcosystemDeviceStruct(ClusterObject): + @ChipUtility.classproperty + def descriptor(cls) -> ClusterObjectDescriptor: + return ClusterObjectDescriptor( + Fields=[ + ClusterObjectFieldDescriptor(Label="deviceName", Tag=0, Type=typing.Optional[str]), + ClusterObjectFieldDescriptor(Label="deviceNameLastEdit", Tag=1, Type=typing.Optional[uint]), + ClusterObjectFieldDescriptor(Label="bridgedEndpoint", Tag=2, Type=uint), + ClusterObjectFieldDescriptor(Label="originalEndpoint", Tag=3, Type=uint), + ClusterObjectFieldDescriptor(Label="deviceTypes", Tag=4, Type=typing.List[EcosystemInformation.Structs.DeviceTypeStruct]), + ClusterObjectFieldDescriptor(Label="uniqueLocationIDs", Tag=5, Type=typing.List[str]), + ClusterObjectFieldDescriptor(Label="uniqueLocationIDsLastEdit", Tag=6, Type=uint), + ClusterObjectFieldDescriptor(Label="fabricIndex", Tag=254, Type=uint), + ]) + + deviceName: 'typing.Optional[str]' = None + deviceNameLastEdit: 'typing.Optional[uint]' = None + bridgedEndpoint: 'uint' = 0 + originalEndpoint: 'uint' = 0 + deviceTypes: 'typing.List[EcosystemInformation.Structs.DeviceTypeStruct]' = field(default_factory=lambda: []) + uniqueLocationIDs: 'typing.List[str]' = field(default_factory=lambda: []) + uniqueLocationIDsLastEdit: 'uint' = 0 + fabricIndex: 'uint' = 0 + + class Attributes: + @dataclass + class RemovedOn(ClusterAttributeDescriptor): + @ChipUtility.classproperty + def cluster_id(cls) -> int: + return 0x00000750 + + @ChipUtility.classproperty + def attribute_id(cls) -> int: + return 0x00000000 + + @ChipUtility.classproperty + def attribute_type(cls) -> ClusterObjectFieldDescriptor: + return ClusterObjectFieldDescriptor(Type=typing.Union[None, Nullable, uint]) + + value: 'typing.Union[None, Nullable, uint]' = None + + @dataclass + class DeviceDirectory(ClusterAttributeDescriptor): + @ChipUtility.classproperty + def cluster_id(cls) -> int: + return 0x00000750 + + @ChipUtility.classproperty + def attribute_id(cls) -> int: + return 0x00000001 + + @ChipUtility.classproperty + def attribute_type(cls) -> ClusterObjectFieldDescriptor: + return ClusterObjectFieldDescriptor(Type=typing.List[EcosystemInformation.Structs.EcosystemDeviceStruct]) + + value: 'typing.List[EcosystemInformation.Structs.EcosystemDeviceStruct]' = field(default_factory=lambda: []) + + @dataclass + class LocationDirectory(ClusterAttributeDescriptor): + @ChipUtility.classproperty + def cluster_id(cls) -> int: + return 0x00000750 + + @ChipUtility.classproperty + def attribute_id(cls) -> int: + return 0x00000002 + + @ChipUtility.classproperty + def attribute_type(cls) -> ClusterObjectFieldDescriptor: + return ClusterObjectFieldDescriptor(Type=typing.List[EcosystemInformation.Structs.EcosystemLocationStruct]) + + value: 'typing.List[EcosystemInformation.Structs.EcosystemLocationStruct]' = field(default_factory=lambda: []) + + @dataclass + class GeneratedCommandList(ClusterAttributeDescriptor): + @ChipUtility.classproperty + def cluster_id(cls) -> int: + return 0x00000750 + + @ChipUtility.classproperty + def attribute_id(cls) -> int: + return 0x0000FFF8 + + @ChipUtility.classproperty + def attribute_type(cls) -> ClusterObjectFieldDescriptor: + return ClusterObjectFieldDescriptor(Type=typing.List[uint]) + + value: 'typing.List[uint]' = field(default_factory=lambda: []) + + @dataclass + class AcceptedCommandList(ClusterAttributeDescriptor): + @ChipUtility.classproperty + def cluster_id(cls) -> int: + return 0x00000750 + + @ChipUtility.classproperty + def attribute_id(cls) -> int: + return 0x0000FFF9 + + @ChipUtility.classproperty + def attribute_type(cls) -> ClusterObjectFieldDescriptor: + return ClusterObjectFieldDescriptor(Type=typing.List[uint]) + + value: 'typing.List[uint]' = field(default_factory=lambda: []) + + @dataclass + class EventList(ClusterAttributeDescriptor): + @ChipUtility.classproperty + def cluster_id(cls) -> int: + return 0x00000750 + + @ChipUtility.classproperty + def attribute_id(cls) -> int: + return 0x0000FFFA + + @ChipUtility.classproperty + def attribute_type(cls) -> ClusterObjectFieldDescriptor: + return ClusterObjectFieldDescriptor(Type=typing.List[uint]) + + value: 'typing.List[uint]' = field(default_factory=lambda: []) + + @dataclass + class AttributeList(ClusterAttributeDescriptor): + @ChipUtility.classproperty + def cluster_id(cls) -> int: + return 0x00000750 + + @ChipUtility.classproperty + def attribute_id(cls) -> int: + return 0x0000FFFB + + @ChipUtility.classproperty + def attribute_type(cls) -> ClusterObjectFieldDescriptor: + return ClusterObjectFieldDescriptor(Type=typing.List[uint]) + + value: 'typing.List[uint]' = field(default_factory=lambda: []) + + @dataclass + class FeatureMap(ClusterAttributeDescriptor): + @ChipUtility.classproperty + def cluster_id(cls) -> int: + return 0x00000750 + + @ChipUtility.classproperty + def attribute_id(cls) -> int: + return 0x0000FFFC + + @ChipUtility.classproperty + def attribute_type(cls) -> ClusterObjectFieldDescriptor: + return ClusterObjectFieldDescriptor(Type=uint) + + value: 'uint' = 0 + + @dataclass + class ClusterRevision(ClusterAttributeDescriptor): + @ChipUtility.classproperty + def cluster_id(cls) -> int: + return 0x00000750 + + @ChipUtility.classproperty + def attribute_id(cls) -> int: + return 0x0000FFFD + + @ChipUtility.classproperty + def attribute_type(cls) -> ClusterObjectFieldDescriptor: + return ClusterObjectFieldDescriptor(Type=uint) + + value: 'uint' = 0 + + @dataclass class CommissionerControl(Cluster): id: typing.ClassVar[int] = 0x00000751 diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeSpecifiedCheck.mm b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeSpecifiedCheck.mm index 0d2f7bca464902..204d15937b23ca 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeSpecifiedCheck.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeSpecifiedCheck.mm @@ -6035,6 +6035,42 @@ static BOOL AttributeIsSpecifiedInContentAppObserverCluster(AttributeId aAttribu } } } +static BOOL AttributeIsSpecifiedInEcosystemInformationCluster(AttributeId aAttributeId) +{ + using namespace Clusters::EcosystemInformation; + switch (aAttributeId) { + case Attributes::RemovedOn::Id: { + return YES; + } + case Attributes::DeviceDirectory::Id: { + return YES; + } + case Attributes::LocationDirectory::Id: { + return YES; + } + case Attributes::GeneratedCommandList::Id: { + return YES; + } + case Attributes::AcceptedCommandList::Id: { + return YES; + } + case Attributes::EventList::Id: { + return YES; + } + case Attributes::AttributeList::Id: { + return YES; + } + case Attributes::FeatureMap::Id: { + return YES; + } + case Attributes::ClusterRevision::Id: { + return YES; + } + default: { + return NO; + } + } +} static BOOL AttributeIsSpecifiedInCommissionerControlCluster(AttributeId aAttributeId) { using namespace Clusters::CommissionerControl; @@ -7137,6 +7173,9 @@ BOOL MTRAttributeIsSpecified(ClusterId aClusterId, AttributeId aAttributeId) case Clusters::ContentAppObserver::Id: { return AttributeIsSpecifiedInContentAppObserverCluster(aAttributeId); } + case Clusters::EcosystemInformation::Id: { + return AttributeIsSpecifiedInEcosystemInformationCluster(aAttributeId); + } case Clusters::CommissionerControl::Id: { return AttributeIsSpecifiedInCommissionerControlCluster(aAttributeId); } diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm index 8ce69c91d5ae56..c20bade614d8e3 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm @@ -17037,6 +17037,168 @@ static id _Nullable DecodeAttributeValueForContentAppObserverCluster(AttributeId *aError = CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH_IB; return nil; } +static id _Nullable DecodeAttributeValueForEcosystemInformationCluster(AttributeId aAttributeId, TLV::TLVReader & aReader, CHIP_ERROR * aError) +{ + using namespace Clusters::EcosystemInformation; + switch (aAttributeId) { + case Attributes::RemovedOn::Id: { + using TypeInfo = Attributes::RemovedOn::TypeInfo; + TypeInfo::DecodableType cppValue; + *aError = DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) { + return nil; + } + NSNumber * _Nullable value; + if (cppValue.IsNull()) { + value = nil; + } else { + value = [NSNumber numberWithUnsignedLongLong:cppValue.Value()]; + } + return value; + } + case Attributes::DeviceDirectory::Id: { + using TypeInfo = Attributes::DeviceDirectory::TypeInfo; + TypeInfo::DecodableType cppValue; + *aError = DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) { + return nil; + } + NSArray * _Nonnull value; + { // Scope for our temporary variables + auto * array_0 = [NSMutableArray new]; + auto iter_0 = cppValue.begin(); + while (iter_0.Next()) { + auto & entry_0 = iter_0.GetValue(); + MTREcosystemInformationClusterEcosystemDeviceStruct * newElement_0; + newElement_0 = [MTREcosystemInformationClusterEcosystemDeviceStruct new]; + if (entry_0.deviceName.HasValue()) { + newElement_0.deviceName = AsString(entry_0.deviceName.Value()); + if (newElement_0.deviceName == nil) { + CHIP_ERROR err = CHIP_ERROR_INVALID_ARGUMENT; + *aError = err; + return nil; + } + } else { + newElement_0.deviceName = nil; + } + if (entry_0.deviceNameLastEdit.HasValue()) { + newElement_0.deviceNameLastEdit = [NSNumber numberWithUnsignedLongLong:entry_0.deviceNameLastEdit.Value()]; + } else { + newElement_0.deviceNameLastEdit = nil; + } + newElement_0.bridgedEndpoint = [NSNumber numberWithUnsignedShort:entry_0.bridgedEndpoint]; + newElement_0.originalEndpoint = [NSNumber numberWithUnsignedShort:entry_0.originalEndpoint]; + { // Scope for our temporary variables + auto * array_2 = [NSMutableArray new]; + auto iter_2 = entry_0.deviceTypes.begin(); + while (iter_2.Next()) { + auto & entry_2 = iter_2.GetValue(); + MTREcosystemInformationClusterDeviceTypeStruct * newElement_2; + newElement_2 = [MTREcosystemInformationClusterDeviceTypeStruct new]; + newElement_2.deviceType = [NSNumber numberWithUnsignedInt:entry_2.deviceType]; + newElement_2.revision = [NSNumber numberWithUnsignedShort:entry_2.revision]; + [array_2 addObject:newElement_2]; + } + CHIP_ERROR err = iter_2.GetStatus(); + if (err != CHIP_NO_ERROR) { + *aError = err; + return nil; + } + newElement_0.deviceTypes = array_2; + } + { // Scope for our temporary variables + auto * array_2 = [NSMutableArray new]; + auto iter_2 = entry_0.uniqueLocationIDs.begin(); + while (iter_2.Next()) { + auto & entry_2 = iter_2.GetValue(); + NSString * newElement_2; + newElement_2 = AsString(entry_2); + if (newElement_2 == nil) { + CHIP_ERROR err = CHIP_ERROR_INVALID_ARGUMENT; + *aError = err; + return nil; + } + [array_2 addObject:newElement_2]; + } + CHIP_ERROR err = iter_2.GetStatus(); + if (err != CHIP_NO_ERROR) { + *aError = err; + return nil; + } + newElement_0.uniqueLocationIDs = array_2; + } + newElement_0.uniqueLocationIDsLastEdit = [NSNumber numberWithUnsignedLongLong:entry_0.uniqueLocationIDsLastEdit]; + newElement_0.fabricIndex = [NSNumber numberWithUnsignedChar:entry_0.fabricIndex]; + [array_0 addObject:newElement_0]; + } + CHIP_ERROR err = iter_0.GetStatus(); + if (err != CHIP_NO_ERROR) { + *aError = err; + return nil; + } + value = array_0; + } + return value; + } + case Attributes::LocationDirectory::Id: { + using TypeInfo = Attributes::LocationDirectory::TypeInfo; + TypeInfo::DecodableType cppValue; + *aError = DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) { + return nil; + } + NSArray * _Nonnull value; + { // Scope for our temporary variables + auto * array_0 = [NSMutableArray new]; + auto iter_0 = cppValue.begin(); + while (iter_0.Next()) { + auto & entry_0 = iter_0.GetValue(); + MTREcosystemInformationClusterEcosystemLocationStruct * newElement_0; + newElement_0 = [MTREcosystemInformationClusterEcosystemLocationStruct new]; + newElement_0.uniqueLocationID = AsString(entry_0.uniqueLocationID); + if (newElement_0.uniqueLocationID == nil) { + CHIP_ERROR err = CHIP_ERROR_INVALID_ARGUMENT; + *aError = err; + return nil; + } + newElement_0.locationDescriptor = [MTREcosystemInformationClusterHomeLocationStruct new]; + newElement_0.locationDescriptor.locationName = AsString(entry_0.locationDescriptor.locationName); + if (newElement_0.locationDescriptor.locationName == nil) { + CHIP_ERROR err = CHIP_ERROR_INVALID_ARGUMENT; + *aError = err; + return nil; + } + if (entry_0.locationDescriptor.floorNumber.IsNull()) { + newElement_0.locationDescriptor.floorNumber = nil; + } else { + newElement_0.locationDescriptor.floorNumber = [NSNumber numberWithShort:entry_0.locationDescriptor.floorNumber.Value()]; + } + if (entry_0.locationDescriptor.areaType.IsNull()) { + newElement_0.locationDescriptor.areaType = nil; + } else { + newElement_0.locationDescriptor.areaType = [NSNumber numberWithUnsignedChar:chip::to_underlying(entry_0.locationDescriptor.areaType.Value())]; + } + newElement_0.locationDescriptorLastEdit = [NSNumber numberWithUnsignedLongLong:entry_0.locationDescriptorLastEdit]; + newElement_0.fabricIndex = [NSNumber numberWithUnsignedChar:entry_0.fabricIndex]; + [array_0 addObject:newElement_0]; + } + CHIP_ERROR err = iter_0.GetStatus(); + if (err != CHIP_NO_ERROR) { + *aError = err; + return nil; + } + value = array_0; + } + return value; + } + default: { + break; + } + } + + *aError = CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH_IB; + return nil; +} static id _Nullable DecodeAttributeValueForCommissionerControlCluster(AttributeId aAttributeId, TLV::TLVReader & aReader, CHIP_ERROR * aError) { using namespace Clusters::CommissionerControl; @@ -20284,6 +20446,9 @@ id _Nullable MTRDecodeAttributeValue(const ConcreteAttributePath & aPath, TLV::T case Clusters::ContentAppObserver::Id: { return DecodeAttributeValueForContentAppObserverCluster(aPath.mAttributeId, aReader, aError); } + case Clusters::EcosystemInformation::Id: { + return DecodeAttributeValueForEcosystemInformationCluster(aPath.mAttributeId, aReader, aError); + } case Clusters::CommissionerControl::Id: { return DecodeAttributeValueForCommissionerControlCluster(aPath.mAttributeId, aReader, aError); } diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h index 7c3e36f0797323..dd0eecc0006f2a 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h @@ -15077,6 +15077,85 @@ MTR_PROVISIONALLY_AVAILABLE @end +/** + * Cluster Ecosystem Information + * + * Provides extended device information for all the logical devices represented by a Bridged Node. + */ +MTR_PROVISIONALLY_AVAILABLE +@interface MTRBaseClusterEcosystemInformation : MTRGenericBaseCluster + +- (void)readAttributeRemovedOnWithCompletion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; +- (void)subscribeAttributeRemovedOnWithParams:(MTRSubscribeParams *)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))reportHandler MTR_PROVISIONALLY_AVAILABLE; ++ (void)readAttributeRemovedOnWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; + +- (void)readAttributeDeviceDirectoryWithParams:(MTRReadParams * _Nullable)params completion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; +- (void)subscribeAttributeDeviceDirectoryWithParams:(MTRSubscribeParams *)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler MTR_PROVISIONALLY_AVAILABLE; ++ (void)readAttributeDeviceDirectoryWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; + +- (void)readAttributeLocationDirectoryWithParams:(MTRReadParams * _Nullable)params completion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; +- (void)subscribeAttributeLocationDirectoryWithParams:(MTRSubscribeParams *)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler MTR_PROVISIONALLY_AVAILABLE; ++ (void)readAttributeLocationDirectoryWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; + +- (void)readAttributeGeneratedCommandListWithCompletion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; +- (void)subscribeAttributeGeneratedCommandListWithParams:(MTRSubscribeParams *)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler MTR_PROVISIONALLY_AVAILABLE; ++ (void)readAttributeGeneratedCommandListWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; + +- (void)readAttributeAcceptedCommandListWithCompletion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; +- (void)subscribeAttributeAcceptedCommandListWithParams:(MTRSubscribeParams *)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler MTR_PROVISIONALLY_AVAILABLE; ++ (void)readAttributeAcceptedCommandListWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; + +- (void)readAttributeEventListWithCompletion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; +- (void)subscribeAttributeEventListWithParams:(MTRSubscribeParams *)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler MTR_PROVISIONALLY_AVAILABLE; ++ (void)readAttributeEventListWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; + +- (void)readAttributeAttributeListWithCompletion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; +- (void)subscribeAttributeAttributeListWithParams:(MTRSubscribeParams *)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler MTR_PROVISIONALLY_AVAILABLE; ++ (void)readAttributeAttributeListWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; + +- (void)readAttributeFeatureMapWithCompletion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; +- (void)subscribeAttributeFeatureMapWithParams:(MTRSubscribeParams *)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))reportHandler MTR_PROVISIONALLY_AVAILABLE; ++ (void)readAttributeFeatureMapWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; + +- (void)readAttributeClusterRevisionWithCompletion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; +- (void)subscribeAttributeClusterRevisionWithParams:(MTRSubscribeParams *)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))reportHandler MTR_PROVISIONALLY_AVAILABLE; ++ (void)readAttributeClusterRevisionWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +@end + +@interface MTRBaseClusterEcosystemInformation (Availability) + +/** + * For all instance methods (reads, writes, commands) that take a completion, + * the completion will be called on the provided queue. + */ +- (instancetype _Nullable)initWithDevice:(MTRBaseDevice *)device + endpointID:(NSNumber *)endpointID + queue:(dispatch_queue_t)queue MTR_PROVISIONALLY_AVAILABLE; + +@end + /** * Cluster Commissioner Control * @@ -20912,6 +20991,104 @@ typedef NS_ENUM(uint8_t, MTRContentAppObserverStatus) { MTRContentAppObserverStatusUnexpectedData MTR_PROVISIONALLY_AVAILABLE = 0x01, } MTR_PROVISIONALLY_AVAILABLE; +typedef NS_ENUM(uint8_t, MTREcosystemInformationAreaTypeTag) { + MTREcosystemInformationAreaTypeTagAisle MTR_PROVISIONALLY_AVAILABLE = 0x00, + MTREcosystemInformationAreaTypeTagAttic MTR_PROVISIONALLY_AVAILABLE = 0x01, + MTREcosystemInformationAreaTypeTagBackDoor MTR_PROVISIONALLY_AVAILABLE = 0x02, + MTREcosystemInformationAreaTypeTagBackYard MTR_PROVISIONALLY_AVAILABLE = 0x03, + MTREcosystemInformationAreaTypeTagBalcony MTR_PROVISIONALLY_AVAILABLE = 0x04, + MTREcosystemInformationAreaTypeTagBallroom MTR_PROVISIONALLY_AVAILABLE = 0x05, + MTREcosystemInformationAreaTypeTagBathroom MTR_PROVISIONALLY_AVAILABLE = 0x06, + MTREcosystemInformationAreaTypeTagBedroom MTR_PROVISIONALLY_AVAILABLE = 0x07, + MTREcosystemInformationAreaTypeTagBorder MTR_PROVISIONALLY_AVAILABLE = 0x08, + MTREcosystemInformationAreaTypeTagBoxroom MTR_PROVISIONALLY_AVAILABLE = 0x09, + MTREcosystemInformationAreaTypeTagBreakfastRoom MTR_PROVISIONALLY_AVAILABLE = 0x0A, + MTREcosystemInformationAreaTypeTagCarport MTR_PROVISIONALLY_AVAILABLE = 0x0B, + MTREcosystemInformationAreaTypeTagCellar MTR_PROVISIONALLY_AVAILABLE = 0x0C, + MTREcosystemInformationAreaTypeTagCloakroom MTR_PROVISIONALLY_AVAILABLE = 0x0D, + MTREcosystemInformationAreaTypeTagCloset MTR_PROVISIONALLY_AVAILABLE = 0x0E, + MTREcosystemInformationAreaTypeTagConservatory MTR_PROVISIONALLY_AVAILABLE = 0x0F, + MTREcosystemInformationAreaTypeTagCorridor MTR_PROVISIONALLY_AVAILABLE = 0x10, + MTREcosystemInformationAreaTypeTagCraftRoom MTR_PROVISIONALLY_AVAILABLE = 0x11, + MTREcosystemInformationAreaTypeTagCupboard MTR_PROVISIONALLY_AVAILABLE = 0x12, + MTREcosystemInformationAreaTypeTagDeck MTR_PROVISIONALLY_AVAILABLE = 0x13, + MTREcosystemInformationAreaTypeTagDen MTR_PROVISIONALLY_AVAILABLE = 0x14, + MTREcosystemInformationAreaTypeTagDining MTR_PROVISIONALLY_AVAILABLE = 0x15, + MTREcosystemInformationAreaTypeTagDrawingRoom MTR_PROVISIONALLY_AVAILABLE = 0x16, + MTREcosystemInformationAreaTypeTagDressingRoom MTR_PROVISIONALLY_AVAILABLE = 0x17, + MTREcosystemInformationAreaTypeTagDriveway MTR_PROVISIONALLY_AVAILABLE = 0x18, + MTREcosystemInformationAreaTypeTagElevator MTR_PROVISIONALLY_AVAILABLE = 0x19, + MTREcosystemInformationAreaTypeTagEnsuite MTR_PROVISIONALLY_AVAILABLE = 0x1A, + MTREcosystemInformationAreaTypeTagEntrance MTR_PROVISIONALLY_AVAILABLE = 0x1B, + MTREcosystemInformationAreaTypeTagEntryway MTR_PROVISIONALLY_AVAILABLE = 0x1C, + MTREcosystemInformationAreaTypeTagFamilyRoom MTR_PROVISIONALLY_AVAILABLE = 0x1D, + MTREcosystemInformationAreaTypeTagFoyer MTR_PROVISIONALLY_AVAILABLE = 0x1E, + MTREcosystemInformationAreaTypeTagFrontDoor MTR_PROVISIONALLY_AVAILABLE = 0x1F, + MTREcosystemInformationAreaTypeTagFrontYard MTR_PROVISIONALLY_AVAILABLE = 0x20, + MTREcosystemInformationAreaTypeTagGameRoom MTR_PROVISIONALLY_AVAILABLE = 0x21, + MTREcosystemInformationAreaTypeTagGarage MTR_PROVISIONALLY_AVAILABLE = 0x22, + MTREcosystemInformationAreaTypeTagGarageDoor MTR_PROVISIONALLY_AVAILABLE = 0x23, + MTREcosystemInformationAreaTypeTagGarden MTR_PROVISIONALLY_AVAILABLE = 0x24, + MTREcosystemInformationAreaTypeTagGardenDoor MTR_PROVISIONALLY_AVAILABLE = 0x25, + MTREcosystemInformationAreaTypeTagGuestBathroom MTR_PROVISIONALLY_AVAILABLE = 0x26, + MTREcosystemInformationAreaTypeTagGuestBedroom MTR_PROVISIONALLY_AVAILABLE = 0x27, + MTREcosystemInformationAreaTypeTagGuestRestroom MTR_PROVISIONALLY_AVAILABLE = 0x28, + MTREcosystemInformationAreaTypeTagGuestRoom MTR_PROVISIONALLY_AVAILABLE = 0x29, + MTREcosystemInformationAreaTypeTagGym MTR_PROVISIONALLY_AVAILABLE = 0x2A, + MTREcosystemInformationAreaTypeTagHallway MTR_PROVISIONALLY_AVAILABLE = 0x2B, + MTREcosystemInformationAreaTypeTagHearthRoom MTR_PROVISIONALLY_AVAILABLE = 0x2C, + MTREcosystemInformationAreaTypeTagKidsRoom MTR_PROVISIONALLY_AVAILABLE = 0x2D, + MTREcosystemInformationAreaTypeTagKidsBedroom MTR_PROVISIONALLY_AVAILABLE = 0x2E, + MTREcosystemInformationAreaTypeTagKitchen MTR_PROVISIONALLY_AVAILABLE = 0x2F, + MTREcosystemInformationAreaTypeTagLarder MTR_PROVISIONALLY_AVAILABLE = 0x30, + MTREcosystemInformationAreaTypeTagLaundryRoom MTR_PROVISIONALLY_AVAILABLE = 0x31, + MTREcosystemInformationAreaTypeTagLawn MTR_PROVISIONALLY_AVAILABLE = 0x32, + MTREcosystemInformationAreaTypeTagLibrary MTR_PROVISIONALLY_AVAILABLE = 0x33, + MTREcosystemInformationAreaTypeTagLivingRoom MTR_PROVISIONALLY_AVAILABLE = 0x34, + MTREcosystemInformationAreaTypeTagLounge MTR_PROVISIONALLY_AVAILABLE = 0x35, + MTREcosystemInformationAreaTypeTagMediaTVRoom MTR_PROVISIONALLY_AVAILABLE = 0x36, + MTREcosystemInformationAreaTypeTagMudRoom MTR_PROVISIONALLY_AVAILABLE = 0x37, + MTREcosystemInformationAreaTypeTagMusicRoom MTR_PROVISIONALLY_AVAILABLE = 0x38, + MTREcosystemInformationAreaTypeTagNursery MTR_PROVISIONALLY_AVAILABLE = 0x39, + MTREcosystemInformationAreaTypeTagOffice MTR_PROVISIONALLY_AVAILABLE = 0x3A, + MTREcosystemInformationAreaTypeTagOutdoorKitchen MTR_PROVISIONALLY_AVAILABLE = 0x3B, + MTREcosystemInformationAreaTypeTagOutside MTR_PROVISIONALLY_AVAILABLE = 0x3C, + MTREcosystemInformationAreaTypeTagPantry MTR_PROVISIONALLY_AVAILABLE = 0x3D, + MTREcosystemInformationAreaTypeTagParkingLot MTR_PROVISIONALLY_AVAILABLE = 0x3E, + MTREcosystemInformationAreaTypeTagParlor MTR_PROVISIONALLY_AVAILABLE = 0x3F, + MTREcosystemInformationAreaTypeTagPatio MTR_PROVISIONALLY_AVAILABLE = 0x40, + MTREcosystemInformationAreaTypeTagPlayRoom MTR_PROVISIONALLY_AVAILABLE = 0x41, + MTREcosystemInformationAreaTypeTagPoolRoom MTR_PROVISIONALLY_AVAILABLE = 0x42, + MTREcosystemInformationAreaTypeTagPorch MTR_PROVISIONALLY_AVAILABLE = 0x43, + MTREcosystemInformationAreaTypeTagPrimaryBathroom MTR_PROVISIONALLY_AVAILABLE = 0x44, + MTREcosystemInformationAreaTypeTagPrimaryBedroom MTR_PROVISIONALLY_AVAILABLE = 0x45, + MTREcosystemInformationAreaTypeTagRamp MTR_PROVISIONALLY_AVAILABLE = 0x46, + MTREcosystemInformationAreaTypeTagReceptionRoom MTR_PROVISIONALLY_AVAILABLE = 0x47, + MTREcosystemInformationAreaTypeTagRecreationRoom MTR_PROVISIONALLY_AVAILABLE = 0x48, + MTREcosystemInformationAreaTypeTagRestroom MTR_PROVISIONALLY_AVAILABLE = 0x49, + MTREcosystemInformationAreaTypeTagRoof MTR_PROVISIONALLY_AVAILABLE = 0x4A, + MTREcosystemInformationAreaTypeTagSauna MTR_PROVISIONALLY_AVAILABLE = 0x4B, + MTREcosystemInformationAreaTypeTagScullery MTR_PROVISIONALLY_AVAILABLE = 0x4C, + MTREcosystemInformationAreaTypeTagSewingRoom MTR_PROVISIONALLY_AVAILABLE = 0x4D, + MTREcosystemInformationAreaTypeTagShed MTR_PROVISIONALLY_AVAILABLE = 0x4E, + MTREcosystemInformationAreaTypeTagSideDoor MTR_PROVISIONALLY_AVAILABLE = 0x4F, + MTREcosystemInformationAreaTypeTagSideYard MTR_PROVISIONALLY_AVAILABLE = 0x50, + MTREcosystemInformationAreaTypeTagSittingRoom MTR_PROVISIONALLY_AVAILABLE = 0x51, + MTREcosystemInformationAreaTypeTagSnug MTR_PROVISIONALLY_AVAILABLE = 0x52, + MTREcosystemInformationAreaTypeTagSpa MTR_PROVISIONALLY_AVAILABLE = 0x53, + MTREcosystemInformationAreaTypeTagStaircase MTR_PROVISIONALLY_AVAILABLE = 0x54, + MTREcosystemInformationAreaTypeTagSteamRoom MTR_PROVISIONALLY_AVAILABLE = 0x55, + MTREcosystemInformationAreaTypeTagStorageRoom MTR_PROVISIONALLY_AVAILABLE = 0x56, + MTREcosystemInformationAreaTypeTagStudio MTR_PROVISIONALLY_AVAILABLE = 0x57, + MTREcosystemInformationAreaTypeTagStudy MTR_PROVISIONALLY_AVAILABLE = 0x58, + MTREcosystemInformationAreaTypeTagSunRoom MTR_PROVISIONALLY_AVAILABLE = 0x59, + MTREcosystemInformationAreaTypeTagSwimmingPool MTR_PROVISIONALLY_AVAILABLE = 0x5A, + MTREcosystemInformationAreaTypeTagTerrace MTR_PROVISIONALLY_AVAILABLE = 0x5B, + MTREcosystemInformationAreaTypeTagUtilityRoom MTR_PROVISIONALLY_AVAILABLE = 0x5C, + MTREcosystemInformationAreaTypeTagWard MTR_PROVISIONALLY_AVAILABLE = 0x5D, + MTREcosystemInformationAreaTypeTagWorkshop MTR_PROVISIONALLY_AVAILABLE = 0x5E, +} MTR_PROVISIONALLY_AVAILABLE; + typedef NS_OPTIONS(uint32_t, MTRCommissionerControlSupportedDeviceCategoryBitmap) { MTRCommissionerControlSupportedDeviceCategoryBitmapFabricSynchronization MTR_PROVISIONALLY_AVAILABLE = 0x1, } MTR_PROVISIONALLY_AVAILABLE; diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm index ddb25fb19bd0a8..641fea65515da4 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm @@ -105123,6 +105123,334 @@ + (void)readAttributeClusterRevisionWithClusterStateCache:(MTRClusterStateCacheC @end +@implementation MTRBaseClusterEcosystemInformation + +- (void)readAttributeRemovedOnWithCompletion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::RemovedOn::TypeInfo; + [self.device _readKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:nil + queue:self.callbackQueue + completion:completion]; +} + +- (void)subscribeAttributeRemovedOnWithParams:(MTRSubscribeParams * _Nonnull)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))reportHandler +{ + using TypeInfo = EcosystemInformation::Attributes::RemovedOn::TypeInfo; + [self.device _subscribeToKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:params + queue:self.callbackQueue + reportHandler:reportHandler + subscriptionEstablished:subscriptionEstablished]; +} + ++ (void)readAttributeRemovedOnWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::RemovedOn::TypeInfo; + [clusterStateCacheContainer + _readKnownCachedAttributeWithEndpointID:static_cast([endpoint unsignedShortValue]) + clusterID:TypeInfo::GetClusterId() + attributeID:TypeInfo::GetAttributeId() + queue:queue + completion:completion]; +} + +- (void)readAttributeDeviceDirectoryWithParams:(MTRReadParams * _Nullable)params completion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::DeviceDirectory::TypeInfo; + [self.device _readKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:params + queue:self.callbackQueue + completion:completion]; +} + +- (void)subscribeAttributeDeviceDirectoryWithParams:(MTRSubscribeParams * _Nonnull)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler +{ + using TypeInfo = EcosystemInformation::Attributes::DeviceDirectory::TypeInfo; + [self.device _subscribeToKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:params + queue:self.callbackQueue + reportHandler:reportHandler + subscriptionEstablished:subscriptionEstablished]; +} + ++ (void)readAttributeDeviceDirectoryWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::DeviceDirectory::TypeInfo; + [clusterStateCacheContainer + _readKnownCachedAttributeWithEndpointID:static_cast([endpoint unsignedShortValue]) + clusterID:TypeInfo::GetClusterId() + attributeID:TypeInfo::GetAttributeId() + queue:queue + completion:completion]; +} + +- (void)readAttributeLocationDirectoryWithParams:(MTRReadParams * _Nullable)params completion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::LocationDirectory::TypeInfo; + [self.device _readKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:params + queue:self.callbackQueue + completion:completion]; +} + +- (void)subscribeAttributeLocationDirectoryWithParams:(MTRSubscribeParams * _Nonnull)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler +{ + using TypeInfo = EcosystemInformation::Attributes::LocationDirectory::TypeInfo; + [self.device _subscribeToKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:params + queue:self.callbackQueue + reportHandler:reportHandler + subscriptionEstablished:subscriptionEstablished]; +} + ++ (void)readAttributeLocationDirectoryWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::LocationDirectory::TypeInfo; + [clusterStateCacheContainer + _readKnownCachedAttributeWithEndpointID:static_cast([endpoint unsignedShortValue]) + clusterID:TypeInfo::GetClusterId() + attributeID:TypeInfo::GetAttributeId() + queue:queue + completion:completion]; +} + +- (void)readAttributeGeneratedCommandListWithCompletion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::GeneratedCommandList::TypeInfo; + [self.device _readKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:nil + queue:self.callbackQueue + completion:completion]; +} + +- (void)subscribeAttributeGeneratedCommandListWithParams:(MTRSubscribeParams * _Nonnull)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler +{ + using TypeInfo = EcosystemInformation::Attributes::GeneratedCommandList::TypeInfo; + [self.device _subscribeToKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:params + queue:self.callbackQueue + reportHandler:reportHandler + subscriptionEstablished:subscriptionEstablished]; +} + ++ (void)readAttributeGeneratedCommandListWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::GeneratedCommandList::TypeInfo; + [clusterStateCacheContainer + _readKnownCachedAttributeWithEndpointID:static_cast([endpoint unsignedShortValue]) + clusterID:TypeInfo::GetClusterId() + attributeID:TypeInfo::GetAttributeId() + queue:queue + completion:completion]; +} + +- (void)readAttributeAcceptedCommandListWithCompletion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::AcceptedCommandList::TypeInfo; + [self.device _readKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:nil + queue:self.callbackQueue + completion:completion]; +} + +- (void)subscribeAttributeAcceptedCommandListWithParams:(MTRSubscribeParams * _Nonnull)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler +{ + using TypeInfo = EcosystemInformation::Attributes::AcceptedCommandList::TypeInfo; + [self.device _subscribeToKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:params + queue:self.callbackQueue + reportHandler:reportHandler + subscriptionEstablished:subscriptionEstablished]; +} + ++ (void)readAttributeAcceptedCommandListWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::AcceptedCommandList::TypeInfo; + [clusterStateCacheContainer + _readKnownCachedAttributeWithEndpointID:static_cast([endpoint unsignedShortValue]) + clusterID:TypeInfo::GetClusterId() + attributeID:TypeInfo::GetAttributeId() + queue:queue + completion:completion]; +} + +- (void)readAttributeEventListWithCompletion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::EventList::TypeInfo; + [self.device _readKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:nil + queue:self.callbackQueue + completion:completion]; +} + +- (void)subscribeAttributeEventListWithParams:(MTRSubscribeParams * _Nonnull)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler +{ + using TypeInfo = EcosystemInformation::Attributes::EventList::TypeInfo; + [self.device _subscribeToKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:params + queue:self.callbackQueue + reportHandler:reportHandler + subscriptionEstablished:subscriptionEstablished]; +} + ++ (void)readAttributeEventListWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::EventList::TypeInfo; + [clusterStateCacheContainer + _readKnownCachedAttributeWithEndpointID:static_cast([endpoint unsignedShortValue]) + clusterID:TypeInfo::GetClusterId() + attributeID:TypeInfo::GetAttributeId() + queue:queue + completion:completion]; +} + +- (void)readAttributeAttributeListWithCompletion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::AttributeList::TypeInfo; + [self.device _readKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:nil + queue:self.callbackQueue + completion:completion]; +} + +- (void)subscribeAttributeAttributeListWithParams:(MTRSubscribeParams * _Nonnull)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler +{ + using TypeInfo = EcosystemInformation::Attributes::AttributeList::TypeInfo; + [self.device _subscribeToKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:params + queue:self.callbackQueue + reportHandler:reportHandler + subscriptionEstablished:subscriptionEstablished]; +} + ++ (void)readAttributeAttributeListWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::AttributeList::TypeInfo; + [clusterStateCacheContainer + _readKnownCachedAttributeWithEndpointID:static_cast([endpoint unsignedShortValue]) + clusterID:TypeInfo::GetClusterId() + attributeID:TypeInfo::GetAttributeId() + queue:queue + completion:completion]; +} + +- (void)readAttributeFeatureMapWithCompletion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::FeatureMap::TypeInfo; + [self.device _readKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:nil + queue:self.callbackQueue + completion:completion]; +} + +- (void)subscribeAttributeFeatureMapWithParams:(MTRSubscribeParams * _Nonnull)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))reportHandler +{ + using TypeInfo = EcosystemInformation::Attributes::FeatureMap::TypeInfo; + [self.device _subscribeToKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:params + queue:self.callbackQueue + reportHandler:reportHandler + subscriptionEstablished:subscriptionEstablished]; +} + ++ (void)readAttributeFeatureMapWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::FeatureMap::TypeInfo; + [clusterStateCacheContainer + _readKnownCachedAttributeWithEndpointID:static_cast([endpoint unsignedShortValue]) + clusterID:TypeInfo::GetClusterId() + attributeID:TypeInfo::GetAttributeId() + queue:queue + completion:completion]; +} + +- (void)readAttributeClusterRevisionWithCompletion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::ClusterRevision::TypeInfo; + [self.device _readKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:nil + queue:self.callbackQueue + completion:completion]; +} + +- (void)subscribeAttributeClusterRevisionWithParams:(MTRSubscribeParams * _Nonnull)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))reportHandler +{ + using TypeInfo = EcosystemInformation::Attributes::ClusterRevision::TypeInfo; + [self.device _subscribeToKnownAttributeWithEndpointID:self.endpointID + clusterID:@(TypeInfo::GetClusterId()) + attributeID:@(TypeInfo::GetAttributeId()) + params:params + queue:self.callbackQueue + reportHandler:reportHandler + subscriptionEstablished:subscriptionEstablished]; +} + ++ (void)readAttributeClusterRevisionWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion +{ + using TypeInfo = EcosystemInformation::Attributes::ClusterRevision::TypeInfo; + [clusterStateCacheContainer + _readKnownCachedAttributeWithEndpointID:static_cast([endpoint unsignedShortValue]) + clusterID:TypeInfo::GetClusterId() + attributeID:TypeInfo::GetAttributeId() + queue:queue + completion:completion]; +} + +@end + @implementation MTRBaseClusterCommissionerControl - (void)requestCommissioningApprovalWithParams:(MTRCommissionerControlClusterRequestCommissioningApprovalParams *)params completion:(MTRStatusCompletion)completion diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h b/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h index ff6351d9a50ff3..2178edd403cfe4 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h @@ -204,6 +204,7 @@ typedef NS_ENUM(uint32_t, MTRClusterIDType) { MTRClusterIDTypeAccountLoginID MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) = 0x0000050E, MTRClusterIDTypeContentControlID MTR_PROVISIONALLY_AVAILABLE = 0x0000050F, MTRClusterIDTypeContentAppObserverID MTR_PROVISIONALLY_AVAILABLE = 0x00000510, + MTRClusterIDTypeEcosystemInformationID MTR_PROVISIONALLY_AVAILABLE = 0x00000750, MTRClusterIDTypeCommissionerControlID MTR_PROVISIONALLY_AVAILABLE = 0x00000751, MTRClusterIDTypeElectricalMeasurementID MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) = 0x00000B04, MTRClusterIDTypeUnitTestingID MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) = 0xFFF1FC05, @@ -4888,6 +4889,17 @@ typedef NS_ENUM(uint32_t, MTRAttributeIDType) { MTRAttributeIDTypeClusterContentAppObserverAttributeFeatureMapID MTR_PROVISIONALLY_AVAILABLE = MTRAttributeIDTypeGlobalAttributeFeatureMapID, MTRAttributeIDTypeClusterContentAppObserverAttributeClusterRevisionID MTR_PROVISIONALLY_AVAILABLE = MTRAttributeIDTypeGlobalAttributeClusterRevisionID, + // Cluster EcosystemInformation attributes + MTRAttributeIDTypeClusterEcosystemInformationAttributeRemovedOnID MTR_PROVISIONALLY_AVAILABLE = 0x00000000, + MTRAttributeIDTypeClusterEcosystemInformationAttributeDeviceDirectoryID MTR_PROVISIONALLY_AVAILABLE = 0x00000001, + MTRAttributeIDTypeClusterEcosystemInformationAttributeLocationDirectoryID MTR_PROVISIONALLY_AVAILABLE = 0x00000002, + MTRAttributeIDTypeClusterEcosystemInformationAttributeGeneratedCommandListID MTR_PROVISIONALLY_AVAILABLE = MTRAttributeIDTypeGlobalAttributeGeneratedCommandListID, + MTRAttributeIDTypeClusterEcosystemInformationAttributeAcceptedCommandListID MTR_PROVISIONALLY_AVAILABLE = MTRAttributeIDTypeGlobalAttributeAcceptedCommandListID, + MTRAttributeIDTypeClusterEcosystemInformationAttributeEventListID MTR_PROVISIONALLY_AVAILABLE = MTRAttributeIDTypeGlobalAttributeEventListID, + MTRAttributeIDTypeClusterEcosystemInformationAttributeAttributeListID MTR_PROVISIONALLY_AVAILABLE = MTRAttributeIDTypeGlobalAttributeAttributeListID, + MTRAttributeIDTypeClusterEcosystemInformationAttributeFeatureMapID MTR_PROVISIONALLY_AVAILABLE = MTRAttributeIDTypeGlobalAttributeFeatureMapID, + MTRAttributeIDTypeClusterEcosystemInformationAttributeClusterRevisionID MTR_PROVISIONALLY_AVAILABLE = MTRAttributeIDTypeGlobalAttributeClusterRevisionID, + // Cluster CommissionerControl attributes MTRAttributeIDTypeClusterCommissionerControlAttributeSupportedDeviceCategoriesID MTR_PROVISIONALLY_AVAILABLE = 0x00000000, MTRAttributeIDTypeClusterCommissionerControlAttributeGeneratedCommandListID MTR_PROVISIONALLY_AVAILABLE = MTRAttributeIDTypeGlobalAttributeGeneratedCommandListID, diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusterNames.mm b/src/darwin/Framework/CHIP/zap-generated/MTRClusterNames.mm index 91bdbf48d13e74..575661bfa078bd 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusterNames.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusterNames.mm @@ -378,6 +378,9 @@ case MTRClusterIDTypeContentAppObserverID: result = @"ContentAppObserver"; break; + case MTRClusterIDTypeEcosystemInformationID: + result = @"EcosystemInformation"; + break; case MTRClusterIDTypeCommissionerControlID: result = @"CommissionerControl"; break; @@ -8189,6 +8192,52 @@ break; } + case MTRClusterIDTypeEcosystemInformationID: + + switch (attributeID) { + + // Cluster EcosystemInformation attributes + case MTRAttributeIDTypeClusterEcosystemInformationAttributeRemovedOnID: + result = @"RemovedOn"; + break; + + case MTRAttributeIDTypeClusterEcosystemInformationAttributeDeviceDirectoryID: + result = @"DeviceDirectory"; + break; + + case MTRAttributeIDTypeClusterEcosystemInformationAttributeLocationDirectoryID: + result = @"LocationDirectory"; + break; + + case MTRAttributeIDTypeClusterEcosystemInformationAttributeGeneratedCommandListID: + result = @"GeneratedCommandList"; + break; + + case MTRAttributeIDTypeClusterEcosystemInformationAttributeAcceptedCommandListID: + result = @"AcceptedCommandList"; + break; + + case MTRAttributeIDTypeClusterEcosystemInformationAttributeEventListID: + result = @"EventList"; + break; + + case MTRAttributeIDTypeClusterEcosystemInformationAttributeAttributeListID: + result = @"AttributeList"; + break; + + case MTRAttributeIDTypeClusterEcosystemInformationAttributeFeatureMapID: + result = @"FeatureMap"; + break; + + case MTRAttributeIDTypeClusterEcosystemInformationAttributeClusterRevisionID: + result = @"ClusterRevision"; + break; + + default: + result = [NSString stringWithFormat:@"", attributeID]; + break; + } + case MTRClusterIDTypeCommissionerControlID: switch (attributeID) { diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h index a30b71bc75cb22..cefb92935beab4 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h @@ -6999,6 +6999,48 @@ MTR_PROVISIONALLY_AVAILABLE @end +/** + * Cluster Ecosystem Information + * Provides extended device information for all the logical devices represented by a Bridged Node. + */ +MTR_PROVISIONALLY_AVAILABLE +@interface MTRClusterEcosystemInformation : MTRGenericCluster + +- (NSDictionary * _Nullable)readAttributeRemovedOnWithParams:(MTRReadParams * _Nullable)params MTR_PROVISIONALLY_AVAILABLE; + +- (NSDictionary * _Nullable)readAttributeDeviceDirectoryWithParams:(MTRReadParams * _Nullable)params MTR_PROVISIONALLY_AVAILABLE; + +- (NSDictionary * _Nullable)readAttributeLocationDirectoryWithParams:(MTRReadParams * _Nullable)params MTR_PROVISIONALLY_AVAILABLE; + +- (NSDictionary * _Nullable)readAttributeGeneratedCommandListWithParams:(MTRReadParams * _Nullable)params MTR_PROVISIONALLY_AVAILABLE; + +- (NSDictionary * _Nullable)readAttributeAcceptedCommandListWithParams:(MTRReadParams * _Nullable)params MTR_PROVISIONALLY_AVAILABLE; + +- (NSDictionary * _Nullable)readAttributeEventListWithParams:(MTRReadParams * _Nullable)params MTR_PROVISIONALLY_AVAILABLE; + +- (NSDictionary * _Nullable)readAttributeAttributeListWithParams:(MTRReadParams * _Nullable)params MTR_PROVISIONALLY_AVAILABLE; + +- (NSDictionary * _Nullable)readAttributeFeatureMapWithParams:(MTRReadParams * _Nullable)params MTR_PROVISIONALLY_AVAILABLE; + +- (NSDictionary * _Nullable)readAttributeClusterRevisionWithParams:(MTRReadParams * _Nullable)params MTR_PROVISIONALLY_AVAILABLE; + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +@end + +@interface MTRClusterEcosystemInformation (Availability) + +/** + * The queue is currently unused, but may be used in the future for calling completions + * for command invocations if commands are added to this cluster. + */ +- (instancetype _Nullable)initWithDevice:(MTRDevice *)device + endpointID:(NSNumber *)endpointID + queue:(dispatch_queue_t)queue MTR_PROVISIONALLY_AVAILABLE; + +@end + /** * Cluster Commissioner Control * Supports the ability for clients to request the commissioning of themselves or other nodes onto a fabric which the cluster server can commission onto. diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm index bab799b843c24f..8c38a6c584b79e 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm @@ -20009,6 +20009,55 @@ - (void)contentAppMessageWithParams:(MTRContentAppObserverClusterContentAppMessa @end +@implementation MTRClusterEcosystemInformation + +- (NSDictionary * _Nullable)readAttributeRemovedOnWithParams:(MTRReadParams * _Nullable)params +{ + return [self.device readAttributeWithEndpointID:self.endpointID clusterID:@(MTRClusterIDTypeEcosystemInformationID) attributeID:@(MTRAttributeIDTypeClusterEcosystemInformationAttributeRemovedOnID) params:params]; +} + +- (NSDictionary * _Nullable)readAttributeDeviceDirectoryWithParams:(MTRReadParams * _Nullable)params +{ + return [self.device readAttributeWithEndpointID:self.endpointID clusterID:@(MTRClusterIDTypeEcosystemInformationID) attributeID:@(MTRAttributeIDTypeClusterEcosystemInformationAttributeDeviceDirectoryID) params:params]; +} + +- (NSDictionary * _Nullable)readAttributeLocationDirectoryWithParams:(MTRReadParams * _Nullable)params +{ + return [self.device readAttributeWithEndpointID:self.endpointID clusterID:@(MTRClusterIDTypeEcosystemInformationID) attributeID:@(MTRAttributeIDTypeClusterEcosystemInformationAttributeLocationDirectoryID) params:params]; +} + +- (NSDictionary * _Nullable)readAttributeGeneratedCommandListWithParams:(MTRReadParams * _Nullable)params +{ + return [self.device readAttributeWithEndpointID:self.endpointID clusterID:@(MTRClusterIDTypeEcosystemInformationID) attributeID:@(MTRAttributeIDTypeClusterEcosystemInformationAttributeGeneratedCommandListID) params:params]; +} + +- (NSDictionary * _Nullable)readAttributeAcceptedCommandListWithParams:(MTRReadParams * _Nullable)params +{ + return [self.device readAttributeWithEndpointID:self.endpointID clusterID:@(MTRClusterIDTypeEcosystemInformationID) attributeID:@(MTRAttributeIDTypeClusterEcosystemInformationAttributeAcceptedCommandListID) params:params]; +} + +- (NSDictionary * _Nullable)readAttributeEventListWithParams:(MTRReadParams * _Nullable)params +{ + return [self.device readAttributeWithEndpointID:self.endpointID clusterID:@(MTRClusterIDTypeEcosystemInformationID) attributeID:@(MTRAttributeIDTypeClusterEcosystemInformationAttributeEventListID) params:params]; +} + +- (NSDictionary * _Nullable)readAttributeAttributeListWithParams:(MTRReadParams * _Nullable)params +{ + return [self.device readAttributeWithEndpointID:self.endpointID clusterID:@(MTRClusterIDTypeEcosystemInformationID) attributeID:@(MTRAttributeIDTypeClusterEcosystemInformationAttributeAttributeListID) params:params]; +} + +- (NSDictionary * _Nullable)readAttributeFeatureMapWithParams:(MTRReadParams * _Nullable)params +{ + return [self.device readAttributeWithEndpointID:self.endpointID clusterID:@(MTRClusterIDTypeEcosystemInformationID) attributeID:@(MTRAttributeIDTypeClusterEcosystemInformationAttributeFeatureMapID) params:params]; +} + +- (NSDictionary * _Nullable)readAttributeClusterRevisionWithParams:(MTRReadParams * _Nullable)params +{ + return [self.device readAttributeWithEndpointID:self.endpointID clusterID:@(MTRClusterIDTypeEcosystemInformationID) attributeID:@(MTRAttributeIDTypeClusterEcosystemInformationAttributeClusterRevisionID) params:params]; +} + +@end + @implementation MTRClusterCommissionerControl - (void)requestCommissioningApprovalWithParams:(MTRCommissionerControlClusterRequestCommissioningApprovalParams *)params expectedValues:(NSArray *> * _Nullable)expectedValues expectedValueInterval:(NSNumber * _Nullable)expectedValueIntervalMs completion:(MTRStatusCompletion)completion diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandTimedCheck.mm b/src/darwin/Framework/CHIP/zap-generated/MTRCommandTimedCheck.mm index 4abe6bf88050bd..6fdc015ad0aac3 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandTimedCheck.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandTimedCheck.mm @@ -1154,6 +1154,15 @@ static BOOL CommandNeedsTimedInvokeInContentAppObserverCluster(AttributeId aAttr } } } +static BOOL CommandNeedsTimedInvokeInEcosystemInformationCluster(AttributeId aAttributeId) +{ + using namespace Clusters::EcosystemInformation; + switch (aAttributeId) { + default: { + return NO; + } + } +} static BOOL CommandNeedsTimedInvokeInCommissionerControlCluster(AttributeId aAttributeId) { using namespace Clusters::CommissionerControl; @@ -1551,6 +1560,9 @@ BOOL MTRCommandNeedsTimedInvoke(NSNumber * _Nonnull aClusterID, NSNumber * _Nonn case Clusters::ContentAppObserver::Id: { return CommandNeedsTimedInvokeInContentAppObserverCluster(commandID); } + case Clusters::EcosystemInformation::Id: { + return CommandNeedsTimedInvokeInEcosystemInformationCluster(commandID); + } case Clusters::CommissionerControl::Id: { return CommandNeedsTimedInvokeInCommissionerControlCluster(commandID); } diff --git a/src/darwin/Framework/CHIP/zap-generated/MTREventTLVValueDecoder.mm b/src/darwin/Framework/CHIP/zap-generated/MTREventTLVValueDecoder.mm index 13f125b6328642..fd2bee34f1aafa 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTREventTLVValueDecoder.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTREventTLVValueDecoder.mm @@ -4458,6 +4458,18 @@ static id _Nullable DecodeEventPayloadForContentAppObserverCluster(EventId aEven *aError = CHIP_ERROR_IM_MALFORMED_EVENT_PATH_IB; return nil; } +static id _Nullable DecodeEventPayloadForEcosystemInformationCluster(EventId aEventId, TLV::TLVReader & aReader, CHIP_ERROR * aError) +{ + using namespace Clusters::EcosystemInformation; + switch (aEventId) { + default: { + break; + } + } + + *aError = CHIP_ERROR_IM_MALFORMED_EVENT_PATH_IB; + return nil; +} static id _Nullable DecodeEventPayloadForCommissionerControlCluster(EventId aEventId, TLV::TLVReader & aReader, CHIP_ERROR * aError) { using namespace Clusters::CommissionerControl; @@ -5047,6 +5059,9 @@ id _Nullable MTRDecodeEventPayload(const ConcreteEventPath & aPath, TLV::TLVRead case Clusters::ContentAppObserver::Id: { return DecodeEventPayloadForContentAppObserverCluster(aPath.mEventId, aReader, aError); } + case Clusters::EcosystemInformation::Id: { + return DecodeEventPayloadForEcosystemInformationCluster(aPath.mEventId, aReader, aError); + } case Clusters::CommissionerControl::Id: { return DecodeEventPayloadForCommissionerControlCluster(aPath.mEventId, aReader, aError); } diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h index d4da2052d3cb3e..f87d5b31dd75b5 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h @@ -2047,6 +2047,39 @@ MTR_PROVISIONALLY_AVAILABLE @interface MTRContentControlClusterRemainingScreenTimeExpiredEvent : NSObject @end +MTR_PROVISIONALLY_AVAILABLE +@interface MTREcosystemInformationClusterHomeLocationStruct : NSObject +@property (nonatomic, copy) NSString * _Nonnull locationName MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nullable floorNumber MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nullable areaType MTR_PROVISIONALLY_AVAILABLE; +@end + +MTR_PROVISIONALLY_AVAILABLE +@interface MTREcosystemInformationClusterEcosystemLocationStruct : NSObject +@property (nonatomic, copy) NSString * _Nonnull uniqueLocationID MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) MTREcosystemInformationClusterHomeLocationStruct * _Nonnull locationDescriptor MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nonnull locationDescriptorLastEdit MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nonnull fabricIndex MTR_PROVISIONALLY_AVAILABLE; +@end + +MTR_PROVISIONALLY_AVAILABLE +@interface MTREcosystemInformationClusterDeviceTypeStruct : NSObject +@property (nonatomic, copy) NSNumber * _Nonnull deviceType MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nonnull revision MTR_PROVISIONALLY_AVAILABLE; +@end + +MTR_PROVISIONALLY_AVAILABLE +@interface MTREcosystemInformationClusterEcosystemDeviceStruct : NSObject +@property (nonatomic, copy) NSString * _Nullable deviceName MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nullable deviceNameLastEdit MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nonnull bridgedEndpoint MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nonnull originalEndpoint MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSArray * _Nonnull deviceTypes MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSArray * _Nonnull uniqueLocationIDs MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nonnull uniqueLocationIDsLastEdit MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nonnull fabricIndex MTR_PROVISIONALLY_AVAILABLE; +@end + MTR_PROVISIONALLY_AVAILABLE @interface MTRCommissionerControlClusterCommissioningRequestResultEvent : NSObject @property (nonatomic, copy) NSNumber * _Nonnull requestId MTR_PROVISIONALLY_AVAILABLE; diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm index 99503c79d6e2bf..dc7a0ad672bb25 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm @@ -8427,6 +8427,153 @@ - (NSString *)description @end +@implementation MTREcosystemInformationClusterHomeLocationStruct +- (instancetype)init +{ + if (self = [super init]) { + + _locationName = @""; + + _floorNumber = nil; + + _areaType = nil; + } + return self; +} + +- (id)copyWithZone:(NSZone * _Nullable)zone +{ + auto other = [[MTREcosystemInformationClusterHomeLocationStruct alloc] init]; + + other.locationName = self.locationName; + other.floorNumber = self.floorNumber; + other.areaType = self.areaType; + + return other; +} + +- (NSString *)description +{ + NSString * descriptionString = [NSString stringWithFormat:@"<%@: locationName:%@; floorNumber:%@; areaType:%@; >", NSStringFromClass([self class]), _locationName, _floorNumber, _areaType]; + return descriptionString; +} + +@end + +@implementation MTREcosystemInformationClusterEcosystemLocationStruct +- (instancetype)init +{ + if (self = [super init]) { + + _uniqueLocationID = @""; + + _locationDescriptor = [MTREcosystemInformationClusterHomeLocationStruct new]; + + _locationDescriptorLastEdit = @(0); + + _fabricIndex = @(0); + } + return self; +} + +- (id)copyWithZone:(NSZone * _Nullable)zone +{ + auto other = [[MTREcosystemInformationClusterEcosystemLocationStruct alloc] init]; + + other.uniqueLocationID = self.uniqueLocationID; + other.locationDescriptor = self.locationDescriptor; + other.locationDescriptorLastEdit = self.locationDescriptorLastEdit; + other.fabricIndex = self.fabricIndex; + + return other; +} + +- (NSString *)description +{ + NSString * descriptionString = [NSString stringWithFormat:@"<%@: uniqueLocationID:%@; locationDescriptor:%@; locationDescriptorLastEdit:%@; fabricIndex:%@; >", NSStringFromClass([self class]), _uniqueLocationID, _locationDescriptor, _locationDescriptorLastEdit, _fabricIndex]; + return descriptionString; +} + +@end + +@implementation MTREcosystemInformationClusterDeviceTypeStruct +- (instancetype)init +{ + if (self = [super init]) { + + _deviceType = @(0); + + _revision = @(0); + } + return self; +} + +- (id)copyWithZone:(NSZone * _Nullable)zone +{ + auto other = [[MTREcosystemInformationClusterDeviceTypeStruct alloc] init]; + + other.deviceType = self.deviceType; + other.revision = self.revision; + + return other; +} + +- (NSString *)description +{ + NSString * descriptionString = [NSString stringWithFormat:@"<%@: deviceType:%@; revision:%@; >", NSStringFromClass([self class]), _deviceType, _revision]; + return descriptionString; +} + +@end + +@implementation MTREcosystemInformationClusterEcosystemDeviceStruct +- (instancetype)init +{ + if (self = [super init]) { + + _deviceName = nil; + + _deviceNameLastEdit = nil; + + _bridgedEndpoint = @(0); + + _originalEndpoint = @(0); + + _deviceTypes = [NSArray array]; + + _uniqueLocationIDs = [NSArray array]; + + _uniqueLocationIDsLastEdit = @(0); + + _fabricIndex = @(0); + } + return self; +} + +- (id)copyWithZone:(NSZone * _Nullable)zone +{ + auto other = [[MTREcosystemInformationClusterEcosystemDeviceStruct alloc] init]; + + other.deviceName = self.deviceName; + other.deviceNameLastEdit = self.deviceNameLastEdit; + other.bridgedEndpoint = self.bridgedEndpoint; + other.originalEndpoint = self.originalEndpoint; + other.deviceTypes = self.deviceTypes; + other.uniqueLocationIDs = self.uniqueLocationIDs; + other.uniqueLocationIDsLastEdit = self.uniqueLocationIDsLastEdit; + other.fabricIndex = self.fabricIndex; + + return other; +} + +- (NSString *)description +{ + NSString * descriptionString = [NSString stringWithFormat:@"<%@: deviceName:%@; deviceNameLastEdit:%@; bridgedEndpoint:%@; originalEndpoint:%@; deviceTypes:%@; uniqueLocationIDs:%@; uniqueLocationIDsLastEdit:%@; fabricIndex:%@; >", NSStringFromClass([self class]), _deviceName, _deviceNameLastEdit, _bridgedEndpoint, _originalEndpoint, _deviceTypes, _uniqueLocationIDs, _uniqueLocationIDsLastEdit, _fabricIndex]; + return descriptionString; +} + +@end + @implementation MTRCommissionerControlClusterCommissioningRequestResultEvent - (instancetype)init { diff --git a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp index ca35f4d8464e3c..4f4d62a3e67bd6 100644 --- a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp +++ b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp @@ -38783,6 +38783,195 @@ Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t valu } // namespace Attributes } // namespace ContentAppObserver +namespace EcosystemInformation { +namespace Attributes { + +namespace RemovedOn { + +Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, DataModel::Nullable & value) +{ + using Traits = NumericAttributeTraits; + Traits::StorageType temp; + uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); + Protocols::InteractionModel::Status status = + emberAfReadAttribute(endpoint, Clusters::EcosystemInformation::Id, Id, readable, sizeof(temp)); + VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); + if (Traits::IsNullValue(temp)) + { + value.SetNull(); + } + else + { + value.SetNonNull() = Traits::StorageToWorking(temp); + } + return status; +} + +Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint64_t value, MarkAttributeDirty markDirty) +{ + using Traits = NumericAttributeTraits; + if (!Traits::CanRepresentValue(/* isNullable = */ true, value)) + { + return Protocols::InteractionModel::Status::ConstraintError; + } + Traits::StorageType storageValue; + Traits::WorkingToStorage(value, storageValue); + uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); + return emberAfWriteAttribute(endpoint, Clusters::EcosystemInformation::Id, Id, writable, ZCL_EPOCH_US_ATTRIBUTE_TYPE, + markDirty); +} + +Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint64_t value) +{ + using Traits = NumericAttributeTraits; + if (!Traits::CanRepresentValue(/* isNullable = */ true, value)) + { + return Protocols::InteractionModel::Status::ConstraintError; + } + Traits::StorageType storageValue; + Traits::WorkingToStorage(value, storageValue); + uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); + return emberAfWriteAttribute(endpoint, Clusters::EcosystemInformation::Id, Id, writable, ZCL_EPOCH_US_ATTRIBUTE_TYPE); +} + +Protocols::InteractionModel::Status SetNull(chip::EndpointId endpoint, MarkAttributeDirty markDirty) +{ + using Traits = NumericAttributeTraits; + Traits::StorageType value; + Traits::SetNull(value); + uint8_t * writable = Traits::ToAttributeStoreRepresentation(value); + return emberAfWriteAttribute(endpoint, Clusters::EcosystemInformation::Id, Id, writable, ZCL_EPOCH_US_ATTRIBUTE_TYPE, + markDirty); +} + +Protocols::InteractionModel::Status SetNull(chip::EndpointId endpoint) +{ + using Traits = NumericAttributeTraits; + Traits::StorageType value; + Traits::SetNull(value); + uint8_t * writable = Traits::ToAttributeStoreRepresentation(value); + return emberAfWriteAttribute(endpoint, Clusters::EcosystemInformation::Id, Id, writable, ZCL_EPOCH_US_ATTRIBUTE_TYPE); +} + +Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, const chip::app::DataModel::Nullable & value, + MarkAttributeDirty markDirty) +{ + if (value.IsNull()) + { + return SetNull(endpoint, markDirty); + } + + return Set(endpoint, value.Value(), markDirty); +} + +Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, const chip::app::DataModel::Nullable & value) +{ + if (value.IsNull()) + { + return SetNull(endpoint); + } + + return Set(endpoint, value.Value()); +} + +} // namespace RemovedOn + +namespace FeatureMap { + +Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint32_t * value) +{ + using Traits = NumericAttributeTraits; + Traits::StorageType temp; + uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); + Protocols::InteractionModel::Status status = + emberAfReadAttribute(endpoint, Clusters::EcosystemInformation::Id, Id, readable, sizeof(temp)); + VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); + if (!Traits::CanRepresentValue(/* isNullable = */ false, temp)) + { + return Protocols::InteractionModel::Status::ConstraintError; + } + *value = Traits::StorageToWorking(temp); + return status; +} + +Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint32_t value, MarkAttributeDirty markDirty) +{ + using Traits = NumericAttributeTraits; + if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) + { + return Protocols::InteractionModel::Status::ConstraintError; + } + Traits::StorageType storageValue; + Traits::WorkingToStorage(value, storageValue); + uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); + return emberAfWriteAttribute(endpoint, Clusters::EcosystemInformation::Id, Id, writable, ZCL_BITMAP32_ATTRIBUTE_TYPE, + markDirty); +} + +Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint32_t value) +{ + using Traits = NumericAttributeTraits; + if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) + { + return Protocols::InteractionModel::Status::ConstraintError; + } + Traits::StorageType storageValue; + Traits::WorkingToStorage(value, storageValue); + uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); + return emberAfWriteAttribute(endpoint, Clusters::EcosystemInformation::Id, Id, writable, ZCL_BITMAP32_ATTRIBUTE_TYPE); +} + +} // namespace FeatureMap + +namespace ClusterRevision { + +Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint16_t * value) +{ + using Traits = NumericAttributeTraits; + Traits::StorageType temp; + uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); + Protocols::InteractionModel::Status status = + emberAfReadAttribute(endpoint, Clusters::EcosystemInformation::Id, Id, readable, sizeof(temp)); + VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); + if (!Traits::CanRepresentValue(/* isNullable = */ false, temp)) + { + return Protocols::InteractionModel::Status::ConstraintError; + } + *value = Traits::StorageToWorking(temp); + return status; +} + +Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t value, MarkAttributeDirty markDirty) +{ + using Traits = NumericAttributeTraits; + if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) + { + return Protocols::InteractionModel::Status::ConstraintError; + } + Traits::StorageType storageValue; + Traits::WorkingToStorage(value, storageValue); + uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); + return emberAfWriteAttribute(endpoint, Clusters::EcosystemInformation::Id, Id, writable, ZCL_INT16U_ATTRIBUTE_TYPE, markDirty); +} + +Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t value) +{ + using Traits = NumericAttributeTraits; + if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) + { + return Protocols::InteractionModel::Status::ConstraintError; + } + Traits::StorageType storageValue; + Traits::WorkingToStorage(value, storageValue); + uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); + return emberAfWriteAttribute(endpoint, Clusters::EcosystemInformation::Id, Id, writable, ZCL_INT16U_ATTRIBUTE_TYPE); +} + +} // namespace ClusterRevision + +} // namespace Attributes +} // namespace EcosystemInformation + namespace CommissionerControl { namespace Attributes { diff --git a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h index 78334996c0e75e..da42291b70eb2a 100644 --- a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h +++ b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h @@ -5984,6 +5984,35 @@ Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t valu } // namespace Attributes } // namespace ContentAppObserver +namespace EcosystemInformation { +namespace Attributes { + +namespace RemovedOn { +Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, DataModel::Nullable & value); // epoch_us +Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint64_t value); +Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint64_t value, MarkAttributeDirty markDirty); +Protocols::InteractionModel::Status SetNull(chip::EndpointId endpoint); +Protocols::InteractionModel::Status SetNull(chip::EndpointId endpoint, MarkAttributeDirty markDirty); +Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, const chip::app::DataModel::Nullable & value); +Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, const chip::app::DataModel::Nullable & value, + MarkAttributeDirty markDirty); +} // namespace RemovedOn + +namespace FeatureMap { +Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint32_t * value); // bitmap32 +Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint32_t value); +Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint32_t value, MarkAttributeDirty markDirty); +} // namespace FeatureMap + +namespace ClusterRevision { +Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint16_t * value); // int16u +Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t value); +Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t value, MarkAttributeDirty markDirty); +} // namespace ClusterRevision + +} // namespace Attributes +} // namespace EcosystemInformation + namespace CommissionerControl { namespace Attributes { diff --git a/zzz_generated/app-common/app-common/zap-generated/callback.h b/zzz_generated/app-common/app-common/zap-generated/callback.h index 03f0b7bbef238e..f1aa9705d7c8c3 100644 --- a/zzz_generated/app-common/app-common/zap-generated/callback.h +++ b/zzz_generated/app-common/app-common/zap-generated/callback.h @@ -633,6 +633,11 @@ void emberAfContentControlClusterInitCallback(chip::EndpointId endpoint); */ void emberAfContentAppObserverClusterInitCallback(chip::EndpointId endpoint); +/** + * @param endpoint Endpoint that is being initialized + */ +void emberAfEcosystemInformationClusterInitCallback(chip::EndpointId endpoint); + /** * @param endpoint Endpoint that is being initialized */ @@ -5298,6 +5303,44 @@ chip::Protocols::InteractionModel::Status MatterContentAppObserverClusterServerP */ void emberAfContentAppObserverClusterServerTickCallback(chip::EndpointId endpoint); +// +// Ecosystem Information Cluster +// + +/** + * @param endpoint Endpoint that is being initialized + */ +void emberAfEcosystemInformationClusterServerInitCallback(chip::EndpointId endpoint); + +/** + * @param endpoint Endpoint that is being shutdown + */ +void MatterEcosystemInformationClusterServerShutdownCallback(chip::EndpointId endpoint); + +/** + * @param endpoint Endpoint that is being initialized + */ +void emberAfEcosystemInformationClusterClientInitCallback(chip::EndpointId endpoint); + +/** + * @param attributePath Concrete attribute path that changed + */ +void MatterEcosystemInformationClusterServerAttributeChangedCallback(const chip::app::ConcreteAttributePath & attributePath); + +/** + * @param attributePath Concrete attribute path to be changed + * @param attributeType Attribute type + * @param size Attribute size + * @param value Attribute value + */ +chip::Protocols::InteractionModel::Status MatterEcosystemInformationClusterServerPreAttributeChangedCallback( + const chip::app::ConcreteAttributePath & attributePath, EmberAfAttributeType attributeType, uint16_t size, uint8_t * value); + +/** + * @param endpoint Endpoint that is being served + */ +void emberAfEcosystemInformationClusterServerTickCallback(chip::EndpointId endpoint); + // // Commissioner Control Cluster // diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h b/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h index 0304b683406b8b..dd4bf2387c3a2e 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h @@ -24,6 +24,111 @@ namespace chip { namespace app { namespace Clusters { +static auto __attribute__((unused)) EnsureKnownEnumValue(detail::AreaTypeTag val) +{ + using EnumType = detail::AreaTypeTag; + switch (val) + { + case EnumType::kAisle: + case EnumType::kAttic: + case EnumType::kBackDoor: + case EnumType::kBackYard: + case EnumType::kBalcony: + case EnumType::kBallroom: + case EnumType::kBathroom: + case EnumType::kBedroom: + case EnumType::kBorder: + case EnumType::kBoxroom: + case EnumType::kBreakfastRoom: + case EnumType::kCarport: + case EnumType::kCellar: + case EnumType::kCloakroom: + case EnumType::kCloset: + case EnumType::kConservatory: + case EnumType::kCorridor: + case EnumType::kCraftRoom: + case EnumType::kCupboard: + case EnumType::kDeck: + case EnumType::kDen: + case EnumType::kDining: + case EnumType::kDrawingRoom: + case EnumType::kDressingRoom: + case EnumType::kDriveway: + case EnumType::kElevator: + case EnumType::kEnsuite: + case EnumType::kEntrance: + case EnumType::kEntryway: + case EnumType::kFamilyRoom: + case EnumType::kFoyer: + case EnumType::kFrontDoor: + case EnumType::kFrontYard: + case EnumType::kGameRoom: + case EnumType::kGarage: + case EnumType::kGarageDoor: + case EnumType::kGarden: + case EnumType::kGardenDoor: + case EnumType::kGuestBathroom: + case EnumType::kGuestBedroom: + case EnumType::kGuestRestroom: + case EnumType::kGuestRoom: + case EnumType::kGym: + case EnumType::kHallway: + case EnumType::kHearthRoom: + case EnumType::kKidsRoom: + case EnumType::kKidsBedroom: + case EnumType::kKitchen: + case EnumType::kLarder: + case EnumType::kLaundryRoom: + case EnumType::kLawn: + case EnumType::kLibrary: + case EnumType::kLivingRoom: + case EnumType::kLounge: + case EnumType::kMediaTvRoom: + case EnumType::kMudRoom: + case EnumType::kMusicRoom: + case EnumType::kNursery: + case EnumType::kOffice: + case EnumType::kOutdoorKitchen: + case EnumType::kOutside: + case EnumType::kPantry: + case EnumType::kParkingLot: + case EnumType::kParlor: + case EnumType::kPatio: + case EnumType::kPlayRoom: + case EnumType::kPoolRoom: + case EnumType::kPorch: + case EnumType::kPrimaryBathroom: + case EnumType::kPrimaryBedroom: + case EnumType::kRamp: + case EnumType::kReceptionRoom: + case EnumType::kRecreationRoom: + case EnumType::kRestroom: + case EnumType::kRoof: + case EnumType::kSauna: + case EnumType::kScullery: + case EnumType::kSewingRoom: + case EnumType::kShed: + case EnumType::kSideDoor: + case EnumType::kSideYard: + case EnumType::kSittingRoom: + case EnumType::kSnug: + case EnumType::kSpa: + case EnumType::kStaircase: + case EnumType::kSteamRoom: + case EnumType::kStorageRoom: + case EnumType::kStudio: + case EnumType::kStudy: + case EnumType::kSunRoom: + case EnumType::kSwimmingPool: + case EnumType::kTerrace: + case EnumType::kUtilityRoom: + case EnumType::kWard: + case EnumType::kWorkshop: + return val; + default: + return EnumType::kUnknownEnumValue; + } +} static auto __attribute__((unused)) EnsureKnownEnumValue(detail::ChangeIndicationEnum val) { using EnumType = detail::ChangeIndicationEnum; @@ -2294,111 +2399,6 @@ static auto __attribute__((unused)) EnsureKnownEnumValue(WindowCovering::Type va } } -static auto __attribute__((unused)) EnsureKnownEnumValue(ServiceArea::AreaTypeTag val) -{ - using EnumType = ServiceArea::AreaTypeTag; - switch (val) - { - case EnumType::kAisle: - case EnumType::kAttic: - case EnumType::kBackDoor: - case EnumType::kBackYard: - case EnumType::kBalcony: - case EnumType::kBallroom: - case EnumType::kBathroom: - case EnumType::kBedroom: - case EnumType::kBorder: - case EnumType::kBoxroom: - case EnumType::kBreakfastRoom: - case EnumType::kCarport: - case EnumType::kCellar: - case EnumType::kCloakroom: - case EnumType::kCloset: - case EnumType::kConservatory: - case EnumType::kCorridor: - case EnumType::kCraftRoom: - case EnumType::kCupboard: - case EnumType::kDeck: - case EnumType::kDen: - case EnumType::kDining: - case EnumType::kDrawingRoom: - case EnumType::kDressingRoom: - case EnumType::kDriveway: - case EnumType::kElevator: - case EnumType::kEnsuite: - case EnumType::kEntrance: - case EnumType::kEntryway: - case EnumType::kFamilyRoom: - case EnumType::kFoyer: - case EnumType::kFrontDoor: - case EnumType::kFrontYard: - case EnumType::kGameRoom: - case EnumType::kGarage: - case EnumType::kGarageDoor: - case EnumType::kGarden: - case EnumType::kGardenDoor: - case EnumType::kGuestBathroom: - case EnumType::kGuestBedroom: - case EnumType::kGuestRestroom: - case EnumType::kGuestRoom: - case EnumType::kGym: - case EnumType::kHallway: - case EnumType::kHearthRoom: - case EnumType::kKidsRoom: - case EnumType::kKidsBedroom: - case EnumType::kKitchen: - case EnumType::kLarder: - case EnumType::kLaundryRoom: - case EnumType::kLawn: - case EnumType::kLibrary: - case EnumType::kLivingRoom: - case EnumType::kLounge: - case EnumType::kMediaTvRoom: - case EnumType::kMudRoom: - case EnumType::kMusicRoom: - case EnumType::kNursery: - case EnumType::kOffice: - case EnumType::kOutdoorKitchen: - case EnumType::kOutside: - case EnumType::kPantry: - case EnumType::kParkingLot: - case EnumType::kParlor: - case EnumType::kPatio: - case EnumType::kPlayRoom: - case EnumType::kPoolRoom: - case EnumType::kPorch: - case EnumType::kPrimaryBathroom: - case EnumType::kPrimaryBedroom: - case EnumType::kRamp: - case EnumType::kReceptionRoom: - case EnumType::kRecreationRoom: - case EnumType::kRestroom: - case EnumType::kRoof: - case EnumType::kSauna: - case EnumType::kScullery: - case EnumType::kSewingRoom: - case EnumType::kShed: - case EnumType::kSideDoor: - case EnumType::kSideYard: - case EnumType::kSittingRoom: - case EnumType::kSnug: - case EnumType::kSpa: - case EnumType::kStaircase: - case EnumType::kSteamRoom: - case EnumType::kStorageRoom: - case EnumType::kStudio: - case EnumType::kStudy: - case EnumType::kSunRoom: - case EnumType::kSwimmingPool: - case EnumType::kTerrace: - case EnumType::kUtilityRoom: - case EnumType::kWard: - case EnumType::kWorkshop: - return val; - default: - return EnumType::kUnknownEnumValue; - } -} static auto __attribute__((unused)) EnsureKnownEnumValue(ServiceArea::FloorSurfaceTag val) { using EnumType = ServiceArea::FloorSurfaceTag; diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h index 612637d3af1776..aaa24825e49d82 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h @@ -28,6 +28,111 @@ namespace Clusters { namespace detail { // Enums shared across multiple clusters. +// Enum for AreaTypeTag +enum class AreaTypeTag : uint8_t +{ + kAisle = 0x00, + kAttic = 0x01, + kBackDoor = 0x02, + kBackYard = 0x03, + kBalcony = 0x04, + kBallroom = 0x05, + kBathroom = 0x06, + kBedroom = 0x07, + kBorder = 0x08, + kBoxroom = 0x09, + kBreakfastRoom = 0x0A, + kCarport = 0x0B, + kCellar = 0x0C, + kCloakroom = 0x0D, + kCloset = 0x0E, + kConservatory = 0x0F, + kCorridor = 0x10, + kCraftRoom = 0x11, + kCupboard = 0x12, + kDeck = 0x13, + kDen = 0x14, + kDining = 0x15, + kDrawingRoom = 0x16, + kDressingRoom = 0x17, + kDriveway = 0x18, + kElevator = 0x19, + kEnsuite = 0x1A, + kEntrance = 0x1B, + kEntryway = 0x1C, + kFamilyRoom = 0x1D, + kFoyer = 0x1E, + kFrontDoor = 0x1F, + kFrontYard = 0x20, + kGameRoom = 0x21, + kGarage = 0x22, + kGarageDoor = 0x23, + kGarden = 0x24, + kGardenDoor = 0x25, + kGuestBathroom = 0x26, + kGuestBedroom = 0x27, + kGuestRestroom = 0x28, + kGuestRoom = 0x29, + kGym = 0x2A, + kHallway = 0x2B, + kHearthRoom = 0x2C, + kKidsRoom = 0x2D, + kKidsBedroom = 0x2E, + kKitchen = 0x2F, + kLarder = 0x30, + kLaundryRoom = 0x31, + kLawn = 0x32, + kLibrary = 0x33, + kLivingRoom = 0x34, + kLounge = 0x35, + kMediaTvRoom = 0x36, + kMudRoom = 0x37, + kMusicRoom = 0x38, + kNursery = 0x39, + kOffice = 0x3A, + kOutdoorKitchen = 0x3B, + kOutside = 0x3C, + kPantry = 0x3D, + kParkingLot = 0x3E, + kParlor = 0x3F, + kPatio = 0x40, + kPlayRoom = 0x41, + kPoolRoom = 0x42, + kPorch = 0x43, + kPrimaryBathroom = 0x44, + kPrimaryBedroom = 0x45, + kRamp = 0x46, + kReceptionRoom = 0x47, + kRecreationRoom = 0x48, + kRestroom = 0x49, + kRoof = 0x4A, + kSauna = 0x4B, + kScullery = 0x4C, + kSewingRoom = 0x4D, + kShed = 0x4E, + kSideDoor = 0x4F, + kSideYard = 0x50, + kSittingRoom = 0x51, + kSnug = 0x52, + kSpa = 0x53, + kStaircase = 0x54, + kSteamRoom = 0x55, + kStorageRoom = 0x56, + kStudio = 0x57, + kStudy = 0x58, + kSunRoom = 0x59, + kSwimmingPool = 0x5A, + kTerrace = 0x5B, + kUtilityRoom = 0x5C, + kWard = 0x5D, + kWorkshop = 0x5E, + // All received enum values that are not listed above will be mapped + // to kUnknownEnumValue. This is a helper enum value that should only + // be used by code to process how it handles receiving and unknown + // enum value. This specific should never be transmitted. + kUnknownEnumValue = 95, +}; + // Enum for ChangeIndicationEnum enum class ChangeIndicationEnum : uint8_t { @@ -3512,110 +3617,7 @@ enum class BarrierControlSafetyStatus : uint16_t namespace ServiceArea { -// Enum for AreaTypeTag -enum class AreaTypeTag : uint8_t -{ - kAisle = 0x00, - kAttic = 0x01, - kBackDoor = 0x02, - kBackYard = 0x03, - kBalcony = 0x04, - kBallroom = 0x05, - kBathroom = 0x06, - kBedroom = 0x07, - kBorder = 0x08, - kBoxroom = 0x09, - kBreakfastRoom = 0x0A, - kCarport = 0x0B, - kCellar = 0x0C, - kCloakroom = 0x0D, - kCloset = 0x0E, - kConservatory = 0x0F, - kCorridor = 0x10, - kCraftRoom = 0x11, - kCupboard = 0x12, - kDeck = 0x13, - kDen = 0x14, - kDining = 0x15, - kDrawingRoom = 0x16, - kDressingRoom = 0x17, - kDriveway = 0x18, - kElevator = 0x19, - kEnsuite = 0x1A, - kEntrance = 0x1B, - kEntryway = 0x1C, - kFamilyRoom = 0x1D, - kFoyer = 0x1E, - kFrontDoor = 0x1F, - kFrontYard = 0x20, - kGameRoom = 0x21, - kGarage = 0x22, - kGarageDoor = 0x23, - kGarden = 0x24, - kGardenDoor = 0x25, - kGuestBathroom = 0x26, - kGuestBedroom = 0x27, - kGuestRestroom = 0x28, - kGuestRoom = 0x29, - kGym = 0x2A, - kHallway = 0x2B, - kHearthRoom = 0x2C, - kKidsRoom = 0x2D, - kKidsBedroom = 0x2E, - kKitchen = 0x2F, - kLarder = 0x30, - kLaundryRoom = 0x31, - kLawn = 0x32, - kLibrary = 0x33, - kLivingRoom = 0x34, - kLounge = 0x35, - kMediaTvRoom = 0x36, - kMudRoom = 0x37, - kMusicRoom = 0x38, - kNursery = 0x39, - kOffice = 0x3A, - kOutdoorKitchen = 0x3B, - kOutside = 0x3C, - kPantry = 0x3D, - kParkingLot = 0x3E, - kParlor = 0x3F, - kPatio = 0x40, - kPlayRoom = 0x41, - kPoolRoom = 0x42, - kPorch = 0x43, - kPrimaryBathroom = 0x44, - kPrimaryBedroom = 0x45, - kRamp = 0x46, - kReceptionRoom = 0x47, - kRecreationRoom = 0x48, - kRestroom = 0x49, - kRoof = 0x4A, - kSauna = 0x4B, - kScullery = 0x4C, - kSewingRoom = 0x4D, - kShed = 0x4E, - kSideDoor = 0x4F, - kSideYard = 0x50, - kSittingRoom = 0x51, - kSnug = 0x52, - kSpa = 0x53, - kStaircase = 0x54, - kSteamRoom = 0x55, - kStorageRoom = 0x56, - kStudio = 0x57, - kStudy = 0x58, - kSunRoom = 0x59, - kSwimmingPool = 0x5A, - kTerrace = 0x5B, - kUtilityRoom = 0x5C, - kWard = 0x5D, - kWorkshop = 0x5E, - // All received enum values that are not listed above will be mapped - // to kUnknownEnumValue. This is a helper enum value that should only - // be used by code to process how it handles receiving and unknown - // enum value. This specific should never be transmitted. - kUnknownEnumValue = 95, -}; +using AreaTypeTag = Clusters::detail::AreaTypeTag; // Enum for FloorSurfaceTag enum class FloorSurfaceTag : uint8_t @@ -5204,6 +5206,11 @@ enum class StatusEnum : uint8_t }; } // namespace ContentAppObserver +namespace EcosystemInformation { + +using AreaTypeTag = Clusters::detail::AreaTypeTag; +} // namespace EcosystemInformation + namespace CommissionerControl { // Bitmap for SupportedDeviceCategoryBitmap diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp index 7a08cb455c6452..9126422f85bd03 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp @@ -294,6 +294,93 @@ CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) } // namespace MeasurementAccuracyStruct +namespace HomeLocationStruct { +CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const +{ + DataModel::WrappedStructEncoder encoder{ aWriter, aTag }; + encoder.Encode(to_underlying(Fields::kLocationName), locationName); + encoder.Encode(to_underlying(Fields::kFloorNumber), floorNumber); + encoder.Encode(to_underlying(Fields::kAreaType), areaType); + return encoder.Finalize(); +} + +CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) +{ + detail::StructDecodeIterator __iterator(reader); + while (true) + { + auto __element = __iterator.Next(); + if (std::holds_alternative(__element)) + { + return std::get(__element); + } + + CHIP_ERROR err = CHIP_NO_ERROR; + const uint8_t __context_tag = std::get(__element); + + if (__context_tag == to_underlying(Fields::kLocationName)) + { + err = DataModel::Decode(reader, locationName); + } + else if (__context_tag == to_underlying(Fields::kFloorNumber)) + { + err = DataModel::Decode(reader, floorNumber); + } + else if (__context_tag == to_underlying(Fields::kAreaType)) + { + err = DataModel::Decode(reader, areaType); + } + else + { + } + + ReturnErrorOnFailure(err); + } +} + +} // namespace HomeLocationStruct + +namespace DeviceTypeStruct { +CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const +{ + DataModel::WrappedStructEncoder encoder{ aWriter, aTag }; + encoder.Encode(to_underlying(Fields::kDeviceType), deviceType); + encoder.Encode(to_underlying(Fields::kRevision), revision); + return encoder.Finalize(); +} + +CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) +{ + detail::StructDecodeIterator __iterator(reader); + while (true) + { + auto __element = __iterator.Next(); + if (std::holds_alternative(__element)) + { + return std::get(__element); + } + + CHIP_ERROR err = CHIP_NO_ERROR; + const uint8_t __context_tag = std::get(__element); + + if (__context_tag == to_underlying(Fields::kDeviceType)) + { + err = DataModel::Decode(reader, deviceType); + } + else if (__context_tag == to_underlying(Fields::kRevision)) + { + err = DataModel::Decode(reader, revision); + } + else + { + } + + ReturnErrorOnFailure(err); + } +} + +} // namespace DeviceTypeStruct + namespace ApplicationStruct { CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const { @@ -1761,47 +1848,6 @@ namespace Events {} // namespace Events namespace Descriptor { namespace Structs { -namespace DeviceTypeStruct { -CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const -{ - DataModel::WrappedStructEncoder encoder{ aWriter, aTag }; - encoder.Encode(to_underlying(Fields::kDeviceType), deviceType); - encoder.Encode(to_underlying(Fields::kRevision), revision); - return encoder.Finalize(); -} - -CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) -{ - detail::StructDecodeIterator __iterator(reader); - while (true) - { - auto __element = __iterator.Next(); - if (std::holds_alternative(__element)) - { - return std::get(__element); - } - - CHIP_ERROR err = CHIP_NO_ERROR; - const uint8_t __context_tag = std::get(__element); - - if (__context_tag == to_underlying(Fields::kDeviceType)) - { - err = DataModel::Decode(reader, deviceType); - } - else if (__context_tag == to_underlying(Fields::kRevision)) - { - err = DataModel::Decode(reader, revision); - } - else - { - } - - ReturnErrorOnFailure(err); - } -} - -} // namespace DeviceTypeStruct - namespace SemanticTagStruct { CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const { @@ -19798,52 +19844,6 @@ namespace Events {} // namespace Events namespace ServiceArea { namespace Structs { -namespace HomeLocationStruct { -CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const -{ - DataModel::WrappedStructEncoder encoder{ aWriter, aTag }; - encoder.Encode(to_underlying(Fields::kLocationName), locationName); - encoder.Encode(to_underlying(Fields::kFloorNumber), floorNumber); - encoder.Encode(to_underlying(Fields::kAreaType), areaType); - return encoder.Finalize(); -} - -CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) -{ - detail::StructDecodeIterator __iterator(reader); - while (true) - { - auto __element = __iterator.Next(); - if (std::holds_alternative(__element)) - { - return std::get(__element); - } - - CHIP_ERROR err = CHIP_NO_ERROR; - const uint8_t __context_tag = std::get(__element); - - if (__context_tag == to_underlying(Fields::kLocationName)) - { - err = DataModel::Decode(reader, locationName); - } - else if (__context_tag == to_underlying(Fields::kFloorNumber)) - { - err = DataModel::Decode(reader, floorNumber); - } - else if (__context_tag == to_underlying(Fields::kAreaType)) - { - err = DataModel::Decode(reader, areaType); - } - else - { - } - - ReturnErrorOnFailure(err); - } -} - -} // namespace HomeLocationStruct - namespace LocationInfoStruct { CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const { @@ -28019,6 +28019,230 @@ CHIP_ERROR TypeInfo::DecodableType::Decode(TLV::TLVReader & reader, const Concre namespace Events {} // namespace Events } // namespace ContentAppObserver +namespace EcosystemInformation { +namespace Structs { + +namespace EcosystemLocationStruct { +CHIP_ERROR Type::EncodeForWrite(TLV::TLVWriter & aWriter, TLV::Tag aTag) const +{ + return DoEncode(aWriter, aTag, NullOptional); +} + +CHIP_ERROR Type::EncodeForRead(TLV::TLVWriter & aWriter, TLV::Tag aTag, FabricIndex aAccessingFabricIndex) const +{ + return DoEncode(aWriter, aTag, MakeOptional(aAccessingFabricIndex)); +} + +CHIP_ERROR Type::DoEncode(TLV::TLVWriter & aWriter, TLV::Tag aTag, const Optional & aAccessingFabricIndex) const +{ + bool includeSensitive = !aAccessingFabricIndex.HasValue() || (aAccessingFabricIndex.Value() == fabricIndex); + + DataModel::WrappedStructEncoder encoder{ aWriter, aTag }; + + if (includeSensitive) + { + encoder.Encode(to_underlying(Fields::kUniqueLocationID), uniqueLocationID); + } + if (includeSensitive) + { + encoder.Encode(to_underlying(Fields::kLocationDescriptor), locationDescriptor); + } + if (includeSensitive) + { + encoder.Encode(to_underlying(Fields::kLocationDescriptorLastEdit), locationDescriptorLastEdit); + } + if (aAccessingFabricIndex.HasValue()) + { + encoder.Encode(to_underlying(Fields::kFabricIndex), fabricIndex); + } + + return encoder.Finalize(); +} + +CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) +{ + detail::StructDecodeIterator __iterator(reader); + while (true) + { + auto __element = __iterator.Next(); + if (std::holds_alternative(__element)) + { + return std::get(__element); + } + + CHIP_ERROR err = CHIP_NO_ERROR; + const uint8_t __context_tag = std::get(__element); + + if (__context_tag == to_underlying(Fields::kUniqueLocationID)) + { + err = DataModel::Decode(reader, uniqueLocationID); + } + else if (__context_tag == to_underlying(Fields::kLocationDescriptor)) + { + err = DataModel::Decode(reader, locationDescriptor); + } + else if (__context_tag == to_underlying(Fields::kLocationDescriptorLastEdit)) + { + err = DataModel::Decode(reader, locationDescriptorLastEdit); + } + else if (__context_tag == to_underlying(Fields::kFabricIndex)) + { + err = DataModel::Decode(reader, fabricIndex); + } + else + { + } + + ReturnErrorOnFailure(err); + } +} + +} // namespace EcosystemLocationStruct + +namespace EcosystemDeviceStruct { +CHIP_ERROR Type::EncodeForWrite(TLV::TLVWriter & aWriter, TLV::Tag aTag) const +{ + return DoEncode(aWriter, aTag, NullOptional); +} + +CHIP_ERROR Type::EncodeForRead(TLV::TLVWriter & aWriter, TLV::Tag aTag, FabricIndex aAccessingFabricIndex) const +{ + return DoEncode(aWriter, aTag, MakeOptional(aAccessingFabricIndex)); +} + +CHIP_ERROR Type::DoEncode(TLV::TLVWriter & aWriter, TLV::Tag aTag, const Optional & aAccessingFabricIndex) const +{ + bool includeSensitive = !aAccessingFabricIndex.HasValue() || (aAccessingFabricIndex.Value() == fabricIndex); + + DataModel::WrappedStructEncoder encoder{ aWriter, aTag }; + + if (includeSensitive) + { + encoder.Encode(to_underlying(Fields::kDeviceName), deviceName); + } + if (includeSensitive) + { + encoder.Encode(to_underlying(Fields::kDeviceNameLastEdit), deviceNameLastEdit); + } + if (includeSensitive) + { + encoder.Encode(to_underlying(Fields::kBridgedEndpoint), bridgedEndpoint); + } + if (includeSensitive) + { + encoder.Encode(to_underlying(Fields::kOriginalEndpoint), originalEndpoint); + } + if (includeSensitive) + { + encoder.Encode(to_underlying(Fields::kDeviceTypes), deviceTypes); + } + if (includeSensitive) + { + encoder.Encode(to_underlying(Fields::kUniqueLocationIDs), uniqueLocationIDs); + } + if (includeSensitive) + { + encoder.Encode(to_underlying(Fields::kUniqueLocationIDsLastEdit), uniqueLocationIDsLastEdit); + } + if (aAccessingFabricIndex.HasValue()) + { + encoder.Encode(to_underlying(Fields::kFabricIndex), fabricIndex); + } + + return encoder.Finalize(); +} + +CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) +{ + detail::StructDecodeIterator __iterator(reader); + while (true) + { + auto __element = __iterator.Next(); + if (std::holds_alternative(__element)) + { + return std::get(__element); + } + + CHIP_ERROR err = CHIP_NO_ERROR; + const uint8_t __context_tag = std::get(__element); + + if (__context_tag == to_underlying(Fields::kDeviceName)) + { + err = DataModel::Decode(reader, deviceName); + } + else if (__context_tag == to_underlying(Fields::kDeviceNameLastEdit)) + { + err = DataModel::Decode(reader, deviceNameLastEdit); + } + else if (__context_tag == to_underlying(Fields::kBridgedEndpoint)) + { + err = DataModel::Decode(reader, bridgedEndpoint); + } + else if (__context_tag == to_underlying(Fields::kOriginalEndpoint)) + { + err = DataModel::Decode(reader, originalEndpoint); + } + else if (__context_tag == to_underlying(Fields::kDeviceTypes)) + { + err = DataModel::Decode(reader, deviceTypes); + } + else if (__context_tag == to_underlying(Fields::kUniqueLocationIDs)) + { + err = DataModel::Decode(reader, uniqueLocationIDs); + } + else if (__context_tag == to_underlying(Fields::kUniqueLocationIDsLastEdit)) + { + err = DataModel::Decode(reader, uniqueLocationIDsLastEdit); + } + else if (__context_tag == to_underlying(Fields::kFabricIndex)) + { + err = DataModel::Decode(reader, fabricIndex); + } + else + { + } + + ReturnErrorOnFailure(err); + } +} + +} // namespace EcosystemDeviceStruct +} // namespace Structs + +namespace Commands {} // namespace Commands + +namespace Attributes { +CHIP_ERROR TypeInfo::DecodableType::Decode(TLV::TLVReader & reader, const ConcreteAttributePath & path) +{ + switch (path.mAttributeId) + { + case Attributes::RemovedOn::TypeInfo::GetAttributeId(): + return DataModel::Decode(reader, removedOn); + case Attributes::DeviceDirectory::TypeInfo::GetAttributeId(): + return DataModel::Decode(reader, deviceDirectory); + case Attributes::LocationDirectory::TypeInfo::GetAttributeId(): + return DataModel::Decode(reader, locationDirectory); + case Attributes::GeneratedCommandList::TypeInfo::GetAttributeId(): + return DataModel::Decode(reader, generatedCommandList); + case Attributes::AcceptedCommandList::TypeInfo::GetAttributeId(): + return DataModel::Decode(reader, acceptedCommandList); + case Attributes::EventList::TypeInfo::GetAttributeId(): + return DataModel::Decode(reader, eventList); + case Attributes::AttributeList::TypeInfo::GetAttributeId(): + return DataModel::Decode(reader, attributeList); + case Attributes::FeatureMap::TypeInfo::GetAttributeId(): + return DataModel::Decode(reader, featureMap); + case Attributes::ClusterRevision::TypeInfo::GetAttributeId(): + return DataModel::Decode(reader, clusterRevision); + default: + return CHIP_NO_ERROR; + } +} +} // namespace Attributes + +namespace Events {} // namespace Events + +} // namespace EcosystemInformation namespace CommissionerControl { namespace Commands { diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h index 5952309732b90c..c99987215c8316 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h @@ -173,6 +173,54 @@ struct DecodableType }; } // namespace MeasurementAccuracyStruct +namespace HomeLocationStruct { +enum class Fields : uint8_t +{ + kLocationName = 0, + kFloorNumber = 1, + kAreaType = 2, +}; + +struct Type +{ +public: + chip::CharSpan locationName; + DataModel::Nullable floorNumber; + DataModel::Nullable areaType; + + CHIP_ERROR Decode(TLV::TLVReader & reader); + + static constexpr bool kIsFabricScoped = false; + + CHIP_ERROR Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; +}; + +using DecodableType = Type; + +} // namespace HomeLocationStruct +namespace DeviceTypeStruct { +enum class Fields : uint8_t +{ + kDeviceType = 0, + kRevision = 1, +}; + +struct Type +{ +public: + chip::DeviceTypeId deviceType = static_cast(0); + uint16_t revision = static_cast(0); + + CHIP_ERROR Decode(TLV::TLVReader & reader); + + static constexpr bool kIsFabricScoped = false; + + CHIP_ERROR Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; +}; + +using DecodableType = Type; + +} // namespace DeviceTypeStruct namespace ApplicationStruct { enum class Fields : uint8_t { @@ -2307,29 +2355,7 @@ struct TypeInfo } // namespace PulseWidthModulation namespace Descriptor { namespace Structs { -namespace DeviceTypeStruct { -enum class Fields : uint8_t -{ - kDeviceType = 0, - kRevision = 1, -}; - -struct Type -{ -public: - chip::DeviceTypeId deviceType = static_cast(0); - uint16_t revision = static_cast(0); - - CHIP_ERROR Decode(TLV::TLVReader & reader); - - static constexpr bool kIsFabricScoped = false; - - CHIP_ERROR Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; -}; - -using DecodableType = Type; - -} // namespace DeviceTypeStruct +namespace DeviceTypeStruct = Clusters::detail::Structs::DeviceTypeStruct; namespace SemanticTagStruct { enum class Fields : uint8_t { @@ -27819,31 +27845,7 @@ struct TypeInfo } // namespace BarrierControl namespace ServiceArea { namespace Structs { -namespace HomeLocationStruct { -enum class Fields : uint8_t -{ - kLocationName = 0, - kFloorNumber = 1, - kAreaType = 2, -}; - -struct Type -{ -public: - chip::CharSpan locationName; - DataModel::Nullable floorNumber; - DataModel::Nullable areaType; - - CHIP_ERROR Decode(TLV::TLVReader & reader); - - static constexpr bool kIsFabricScoped = false; - - CHIP_ERROR Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; -}; - -using DecodableType = Type; - -} // namespace HomeLocationStruct +namespace HomeLocationStruct = Clusters::detail::Structs::HomeLocationStruct; namespace LocationInfoStruct { enum class Fields : uint8_t { @@ -41172,6 +41174,208 @@ struct TypeInfo }; } // namespace Attributes } // namespace ContentAppObserver +namespace EcosystemInformation { +namespace Structs { +namespace HomeLocationStruct = Clusters::detail::Structs::HomeLocationStruct; +namespace EcosystemLocationStruct { +enum class Fields : uint8_t +{ + kUniqueLocationID = 0, + kLocationDescriptor = 1, + kLocationDescriptorLastEdit = 2, + kFabricIndex = 254, +}; + +struct Type +{ +public: + chip::CharSpan uniqueLocationID; + Structs::HomeLocationStruct::Type locationDescriptor; + uint64_t locationDescriptorLastEdit = static_cast(0); + chip::FabricIndex fabricIndex = static_cast(0); + + CHIP_ERROR Decode(TLV::TLVReader & reader); + + static constexpr bool kIsFabricScoped = true; + + auto GetFabricIndex() const { return fabricIndex; } + + void SetFabricIndex(chip::FabricIndex fabricIndex_) { fabricIndex = fabricIndex_; } + + CHIP_ERROR EncodeForWrite(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; + CHIP_ERROR EncodeForRead(TLV::TLVWriter & aWriter, TLV::Tag aTag, FabricIndex aAccessingFabricIndex) const; + +private: + CHIP_ERROR DoEncode(TLV::TLVWriter & aWriter, TLV::Tag aTag, const Optional & aAccessingFabricIndex) const; +}; + +using DecodableType = Type; + +} // namespace EcosystemLocationStruct +namespace DeviceTypeStruct = Clusters::detail::Structs::DeviceTypeStruct; +namespace EcosystemDeviceStruct { +enum class Fields : uint8_t +{ + kDeviceName = 0, + kDeviceNameLastEdit = 1, + kBridgedEndpoint = 2, + kOriginalEndpoint = 3, + kDeviceTypes = 4, + kUniqueLocationIDs = 5, + kUniqueLocationIDsLastEdit = 6, + kFabricIndex = 254, +}; + +struct Type +{ +public: + Optional deviceName; + Optional deviceNameLastEdit; + chip::EndpointId bridgedEndpoint = static_cast(0); + chip::EndpointId originalEndpoint = static_cast(0); + DataModel::List deviceTypes; + DataModel::List uniqueLocationIDs; + uint64_t uniqueLocationIDsLastEdit = static_cast(0); + chip::FabricIndex fabricIndex = static_cast(0); + + static constexpr bool kIsFabricScoped = true; + + auto GetFabricIndex() const { return fabricIndex; } + + void SetFabricIndex(chip::FabricIndex fabricIndex_) { fabricIndex = fabricIndex_; } + + CHIP_ERROR EncodeForWrite(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; + CHIP_ERROR EncodeForRead(TLV::TLVWriter & aWriter, TLV::Tag aTag, FabricIndex aAccessingFabricIndex) const; + +private: + CHIP_ERROR DoEncode(TLV::TLVWriter & aWriter, TLV::Tag aTag, const Optional & aAccessingFabricIndex) const; +}; + +struct DecodableType +{ +public: + Optional deviceName; + Optional deviceNameLastEdit; + chip::EndpointId bridgedEndpoint = static_cast(0); + chip::EndpointId originalEndpoint = static_cast(0); + DataModel::DecodableList deviceTypes; + DataModel::DecodableList uniqueLocationIDs; + uint64_t uniqueLocationIDsLastEdit = static_cast(0); + chip::FabricIndex fabricIndex = static_cast(0); + + CHIP_ERROR Decode(TLV::TLVReader & reader); + + static constexpr bool kIsFabricScoped = true; + + auto GetFabricIndex() const { return fabricIndex; } + + void SetFabricIndex(chip::FabricIndex fabricIndex_) { fabricIndex = fabricIndex_; } +}; + +} // namespace EcosystemDeviceStruct +} // namespace Structs + +namespace Attributes { + +namespace RemovedOn { +struct TypeInfo +{ + using Type = chip::app::DataModel::Nullable; + using DecodableType = chip::app::DataModel::Nullable; + using DecodableArgType = const chip::app::DataModel::Nullable &; + + static constexpr ClusterId GetClusterId() { return Clusters::EcosystemInformation::Id; } + static constexpr AttributeId GetAttributeId() { return Attributes::RemovedOn::Id; } + static constexpr bool MustUseTimedWrite() { return false; } +}; +} // namespace RemovedOn +namespace DeviceDirectory { +struct TypeInfo +{ + using Type = chip::app::DataModel::List; + using DecodableType = chip::app::DataModel::DecodableList< + chip::app::Clusters::EcosystemInformation::Structs::EcosystemDeviceStruct::DecodableType>; + using DecodableArgType = const chip::app::DataModel::DecodableList< + chip::app::Clusters::EcosystemInformation::Structs::EcosystemDeviceStruct::DecodableType> &; + + static constexpr ClusterId GetClusterId() { return Clusters::EcosystemInformation::Id; } + static constexpr AttributeId GetAttributeId() { return Attributes::DeviceDirectory::Id; } + static constexpr bool MustUseTimedWrite() { return false; } +}; +} // namespace DeviceDirectory +namespace LocationDirectory { +struct TypeInfo +{ + using Type = + chip::app::DataModel::List; + using DecodableType = chip::app::DataModel::DecodableList< + chip::app::Clusters::EcosystemInformation::Structs::EcosystemLocationStruct::DecodableType>; + using DecodableArgType = const chip::app::DataModel::DecodableList< + chip::app::Clusters::EcosystemInformation::Structs::EcosystemLocationStruct::DecodableType> &; + + static constexpr ClusterId GetClusterId() { return Clusters::EcosystemInformation::Id; } + static constexpr AttributeId GetAttributeId() { return Attributes::LocationDirectory::Id; } + static constexpr bool MustUseTimedWrite() { return false; } +}; +} // namespace LocationDirectory +namespace GeneratedCommandList { +struct TypeInfo : public Clusters::Globals::Attributes::GeneratedCommandList::TypeInfo +{ + static constexpr ClusterId GetClusterId() { return Clusters::EcosystemInformation::Id; } +}; +} // namespace GeneratedCommandList +namespace AcceptedCommandList { +struct TypeInfo : public Clusters::Globals::Attributes::AcceptedCommandList::TypeInfo +{ + static constexpr ClusterId GetClusterId() { return Clusters::EcosystemInformation::Id; } +}; +} // namespace AcceptedCommandList +namespace EventList { +struct TypeInfo : public Clusters::Globals::Attributes::EventList::TypeInfo +{ + static constexpr ClusterId GetClusterId() { return Clusters::EcosystemInformation::Id; } +}; +} // namespace EventList +namespace AttributeList { +struct TypeInfo : public Clusters::Globals::Attributes::AttributeList::TypeInfo +{ + static constexpr ClusterId GetClusterId() { return Clusters::EcosystemInformation::Id; } +}; +} // namespace AttributeList +namespace FeatureMap { +struct TypeInfo : public Clusters::Globals::Attributes::FeatureMap::TypeInfo +{ + static constexpr ClusterId GetClusterId() { return Clusters::EcosystemInformation::Id; } +}; +} // namespace FeatureMap +namespace ClusterRevision { +struct TypeInfo : public Clusters::Globals::Attributes::ClusterRevision::TypeInfo +{ + static constexpr ClusterId GetClusterId() { return Clusters::EcosystemInformation::Id; } +}; +} // namespace ClusterRevision + +struct TypeInfo +{ + struct DecodableType + { + static constexpr ClusterId GetClusterId() { return Clusters::EcosystemInformation::Id; } + + CHIP_ERROR Decode(TLV::TLVReader & reader, const ConcreteAttributePath & path); + + Attributes::RemovedOn::TypeInfo::DecodableType removedOn; + Attributes::DeviceDirectory::TypeInfo::DecodableType deviceDirectory; + Attributes::LocationDirectory::TypeInfo::DecodableType locationDirectory; + Attributes::GeneratedCommandList::TypeInfo::DecodableType generatedCommandList; + Attributes::AcceptedCommandList::TypeInfo::DecodableType acceptedCommandList; + Attributes::EventList::TypeInfo::DecodableType eventList; + Attributes::AttributeList::TypeInfo::DecodableType attributeList; + Attributes::FeatureMap::TypeInfo::DecodableType featureMap = static_cast(0); + Attributes::ClusterRevision::TypeInfo::DecodableType clusterRevision = static_cast(0); + }; +}; +} // namespace Attributes +} // namespace EcosystemInformation namespace CommissionerControl { namespace Commands { diff --git a/zzz_generated/app-common/app-common/zap-generated/ids/Attributes.h b/zzz_generated/app-common/app-common/zap-generated/ids/Attributes.h index 224f09ff69f44d..c1d26d64c5d27b 100644 --- a/zzz_generated/app-common/app-common/zap-generated/ids/Attributes.h +++ b/zzz_generated/app-common/app-common/zap-generated/ids/Attributes.h @@ -7459,6 +7459,48 @@ static constexpr AttributeId Id = Globals::Attributes::ClusterRevision::Id; } // namespace Attributes } // namespace ContentAppObserver +namespace EcosystemInformation { +namespace Attributes { + +namespace RemovedOn { +static constexpr AttributeId Id = 0x00000000; +} // namespace RemovedOn + +namespace DeviceDirectory { +static constexpr AttributeId Id = 0x00000001; +} // namespace DeviceDirectory + +namespace LocationDirectory { +static constexpr AttributeId Id = 0x00000002; +} // namespace LocationDirectory + +namespace GeneratedCommandList { +static constexpr AttributeId Id = Globals::Attributes::GeneratedCommandList::Id; +} // namespace GeneratedCommandList + +namespace AcceptedCommandList { +static constexpr AttributeId Id = Globals::Attributes::AcceptedCommandList::Id; +} // namespace AcceptedCommandList + +namespace EventList { +static constexpr AttributeId Id = Globals::Attributes::EventList::Id; +} // namespace EventList + +namespace AttributeList { +static constexpr AttributeId Id = Globals::Attributes::AttributeList::Id; +} // namespace AttributeList + +namespace FeatureMap { +static constexpr AttributeId Id = Globals::Attributes::FeatureMap::Id; +} // namespace FeatureMap + +namespace ClusterRevision { +static constexpr AttributeId Id = Globals::Attributes::ClusterRevision::Id; +} // namespace ClusterRevision + +} // namespace Attributes +} // namespace EcosystemInformation + namespace CommissionerControl { namespace Attributes { diff --git a/zzz_generated/app-common/app-common/zap-generated/ids/Clusters.h b/zzz_generated/app-common/app-common/zap-generated/ids/Clusters.h index b94e6c1772f2aa..9e5bb87b52ef51 100644 --- a/zzz_generated/app-common/app-common/zap-generated/ids/Clusters.h +++ b/zzz_generated/app-common/app-common/zap-generated/ids/Clusters.h @@ -385,6 +385,9 @@ static constexpr ClusterId Id = 0x0000050F; namespace ContentAppObserver { static constexpr ClusterId Id = 0x00000510; } // namespace ContentAppObserver +namespace EcosystemInformation { +static constexpr ClusterId Id = 0x00000750; +} // namespace EcosystemInformation namespace CommissionerControl { static constexpr ClusterId Id = 0x00000751; } // namespace CommissionerControl diff --git a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h index 99221c08e3f71c..273f743a19cf00 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h @@ -155,6 +155,7 @@ | AccountLogin | 0x050E | | ContentControl | 0x050F | | ContentAppObserver | 0x0510 | +| EcosystemInformation | 0x0750 | | CommissionerControl | 0x0751 | | ElectricalMeasurement | 0x0B04 | | UnitTesting | 0xFFF1FC05| @@ -13814,6 +13815,25 @@ class ContentAppObserverContentAppMessage : public ClusterCommand chip::app::Clusters::ContentAppObserver::Commands::ContentAppMessage::Type mRequest; }; +/*----------------------------------------------------------------------------*\ +| Cluster EcosystemInformation | 0x0750 | +|------------------------------------------------------------------------------| +| Commands: | | +|------------------------------------------------------------------------------| +| Attributes: | | +| * RemovedOn | 0x0000 | +| * DeviceDirectory | 0x0001 | +| * LocationDirectory | 0x0002 | +| * GeneratedCommandList | 0xFFF8 | +| * AcceptedCommandList | 0xFFF9 | +| * EventList | 0xFFFA | +| * AttributeList | 0xFFFB | +| * FeatureMap | 0xFFFC | +| * ClusterRevision | 0xFFFD | +|------------------------------------------------------------------------------| +| Events: | | +\*----------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------*\ | Cluster CommissionerControl | 0x0751 | |------------------------------------------------------------------------------| @@ -26701,6 +26721,71 @@ void registerClusterContentAppObserver(Commands & commands, CredentialIssuerComm commands.RegisterCluster(clusterName, clusterCommands); } +void registerClusterEcosystemInformation(Commands & commands, CredentialIssuerCommands * credsIssuerConfig) +{ + using namespace chip::app::Clusters::EcosystemInformation; + + const char * clusterName = "EcosystemInformation"; + + commands_list clusterCommands = { + // + // Commands + // + make_unique(Id, credsIssuerConfig), // + // + // Attributes + // + make_unique(Id, credsIssuerConfig), // + make_unique(Id, "removed-on", Attributes::RemovedOn::Id, credsIssuerConfig), // + make_unique(Id, "device-directory", Attributes::DeviceDirectory::Id, credsIssuerConfig), // + make_unique(Id, "location-directory", Attributes::LocationDirectory::Id, credsIssuerConfig), // + make_unique(Id, "generated-command-list", Attributes::GeneratedCommandList::Id, credsIssuerConfig), // + make_unique(Id, "accepted-command-list", Attributes::AcceptedCommandList::Id, credsIssuerConfig), // + make_unique(Id, "event-list", Attributes::EventList::Id, credsIssuerConfig), // + make_unique(Id, "attribute-list", Attributes::AttributeList::Id, credsIssuerConfig), // + make_unique(Id, "feature-map", Attributes::FeatureMap::Id, credsIssuerConfig), // + make_unique(Id, "cluster-revision", Attributes::ClusterRevision::Id, credsIssuerConfig), // + make_unique>(Id, credsIssuerConfig), // + make_unique>>( + Id, "removed-on", 0, UINT64_MAX, Attributes::RemovedOn::Id, WriteCommandType::kForceWrite, credsIssuerConfig), // + make_unique>>( + Id, "device-directory", Attributes::DeviceDirectory::Id, WriteCommandType::kForceWrite, credsIssuerConfig), // + make_unique>>( + Id, "location-directory", Attributes::LocationDirectory::Id, WriteCommandType::kForceWrite, credsIssuerConfig), // + make_unique>>( + Id, "generated-command-list", Attributes::GeneratedCommandList::Id, WriteCommandType::kForceWrite, + credsIssuerConfig), // + make_unique>>( + Id, "accepted-command-list", Attributes::AcceptedCommandList::Id, WriteCommandType::kForceWrite, credsIssuerConfig), // + make_unique>>( + Id, "event-list", Attributes::EventList::Id, WriteCommandType::kForceWrite, credsIssuerConfig), // + make_unique>>( + Id, "attribute-list", Attributes::AttributeList::Id, WriteCommandType::kForceWrite, credsIssuerConfig), // + make_unique>(Id, "feature-map", 0, UINT32_MAX, Attributes::FeatureMap::Id, + WriteCommandType::kForceWrite, credsIssuerConfig), // + make_unique>(Id, "cluster-revision", 0, UINT16_MAX, Attributes::ClusterRevision::Id, + WriteCommandType::kForceWrite, credsIssuerConfig), // + make_unique(Id, credsIssuerConfig), // + make_unique(Id, "removed-on", Attributes::RemovedOn::Id, credsIssuerConfig), // + make_unique(Id, "device-directory", Attributes::DeviceDirectory::Id, credsIssuerConfig), // + make_unique(Id, "location-directory", Attributes::LocationDirectory::Id, credsIssuerConfig), // + make_unique(Id, "generated-command-list", Attributes::GeneratedCommandList::Id, credsIssuerConfig), // + make_unique(Id, "accepted-command-list", Attributes::AcceptedCommandList::Id, credsIssuerConfig), // + make_unique(Id, "event-list", Attributes::EventList::Id, credsIssuerConfig), // + make_unique(Id, "attribute-list", Attributes::AttributeList::Id, credsIssuerConfig), // + make_unique(Id, "feature-map", Attributes::FeatureMap::Id, credsIssuerConfig), // + make_unique(Id, "cluster-revision", Attributes::ClusterRevision::Id, credsIssuerConfig), // + // + // Events + // + make_unique(Id, credsIssuerConfig), // + make_unique(Id, credsIssuerConfig), // + }; + + commands.RegisterCluster(clusterName, clusterCommands); +} void registerClusterCommissionerControl(Commands & commands, CredentialIssuerCommands * credsIssuerConfig) { using namespace chip::app::Clusters::CommissionerControl; @@ -28174,6 +28259,7 @@ void registerClusters(Commands & commands, CredentialIssuerCommands * credsIssue registerClusterAccountLogin(commands, credsIssuerConfig); registerClusterContentControl(commands, credsIssuerConfig); registerClusterContentAppObserver(commands, credsIssuerConfig); + registerClusterEcosystemInformation(commands, credsIssuerConfig); registerClusterCommissionerControl(commands, credsIssuerConfig); registerClusterElectricalMeasurement(commands, credsIssuerConfig); registerClusterUnitTesting(commands, credsIssuerConfig); diff --git a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp index c6d7864443fafe..d78bc468849928 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp +++ b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp @@ -219,6 +219,76 @@ void ComplexArgumentParser::Finalize(chip::app::Clusters::detail::Structs::Measu ComplexArgumentParser::Finalize(request.accuracyRanges); } +CHIP_ERROR ComplexArgumentParser::Setup(const char * label, + chip::app::Clusters::detail::Structs::HomeLocationStruct::Type & request, + Json::Value & value) +{ + VerifyOrReturnError(value.isObject(), CHIP_ERROR_INVALID_ARGUMENT); + + // Copy to track which members we already processed. + Json::Value valueCopy(value); + + ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("HomeLocationStruct.locationName", "locationName", + value.isMember("locationName"))); + ReturnErrorOnFailure( + ComplexArgumentParser::EnsureMemberExist("HomeLocationStruct.floorNumber", "floorNumber", value.isMember("floorNumber"))); + ReturnErrorOnFailure( + ComplexArgumentParser::EnsureMemberExist("HomeLocationStruct.areaType", "areaType", value.isMember("areaType"))); + + char labelWithMember[kMaxLabelLength]; + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "locationName"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.locationName, value["locationName"])); + valueCopy.removeMember("locationName"); + + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "floorNumber"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.floorNumber, value["floorNumber"])); + valueCopy.removeMember("floorNumber"); + + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "areaType"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.areaType, value["areaType"])); + valueCopy.removeMember("areaType"); + + return ComplexArgumentParser::EnsureNoMembersRemaining(label, valueCopy); +} + +void ComplexArgumentParser::Finalize(chip::app::Clusters::detail::Structs::HomeLocationStruct::Type & request) +{ + ComplexArgumentParser::Finalize(request.locationName); + ComplexArgumentParser::Finalize(request.floorNumber); + ComplexArgumentParser::Finalize(request.areaType); +} + +CHIP_ERROR ComplexArgumentParser::Setup(const char * label, chip::app::Clusters::detail::Structs::DeviceTypeStruct::Type & request, + Json::Value & value) +{ + VerifyOrReturnError(value.isObject(), CHIP_ERROR_INVALID_ARGUMENT); + + // Copy to track which members we already processed. + Json::Value valueCopy(value); + + ReturnErrorOnFailure( + ComplexArgumentParser::EnsureMemberExist("DeviceTypeStruct.deviceType", "deviceType", value.isMember("deviceType"))); + ReturnErrorOnFailure( + ComplexArgumentParser::EnsureMemberExist("DeviceTypeStruct.revision", "revision", value.isMember("revision"))); + + char labelWithMember[kMaxLabelLength]; + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "deviceType"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.deviceType, value["deviceType"])); + valueCopy.removeMember("deviceType"); + + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "revision"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.revision, value["revision"])); + valueCopy.removeMember("revision"); + + return ComplexArgumentParser::EnsureNoMembersRemaining(label, valueCopy); +} + +void ComplexArgumentParser::Finalize(chip::app::Clusters::detail::Structs::DeviceTypeStruct::Type & request) +{ + ComplexArgumentParser::Finalize(request.deviceType); + ComplexArgumentParser::Finalize(request.revision); +} + CHIP_ERROR ComplexArgumentParser::Setup(const char * label, chip::app::Clusters::detail::Structs::ApplicationStruct::Type & request, Json::Value & value) { @@ -353,38 +423,6 @@ void ComplexArgumentParser::Finalize(chip::app::Clusters::detail::Structs::Opera ComplexArgumentParser::Finalize(request.operationalStateLabel); } -CHIP_ERROR ComplexArgumentParser::Setup(const char * label, - chip::app::Clusters::Descriptor::Structs::DeviceTypeStruct::Type & request, - Json::Value & value) -{ - VerifyOrReturnError(value.isObject(), CHIP_ERROR_INVALID_ARGUMENT); - - // Copy to track which members we already processed. - Json::Value valueCopy(value); - - ReturnErrorOnFailure( - ComplexArgumentParser::EnsureMemberExist("DeviceTypeStruct.deviceType", "deviceType", value.isMember("deviceType"))); - ReturnErrorOnFailure( - ComplexArgumentParser::EnsureMemberExist("DeviceTypeStruct.revision", "revision", value.isMember("revision"))); - - char labelWithMember[kMaxLabelLength]; - snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "deviceType"); - ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.deviceType, value["deviceType"])); - valueCopy.removeMember("deviceType"); - - snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "revision"); - ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.revision, value["revision"])); - valueCopy.removeMember("revision"); - - return ComplexArgumentParser::EnsureNoMembersRemaining(label, valueCopy); -} - -void ComplexArgumentParser::Finalize(chip::app::Clusters::Descriptor::Structs::DeviceTypeStruct::Type & request) -{ - ComplexArgumentParser::Finalize(request.deviceType); - ComplexArgumentParser::Finalize(request.revision); -} - CHIP_ERROR ComplexArgumentParser::Setup(const char * label, chip::app::Clusters::Descriptor::Structs::SemanticTagStruct::Type & request, Json::Value & value) @@ -3748,45 +3786,6 @@ void ComplexArgumentParser::Finalize(chip::app::Clusters::DoorLock::Structs::Cre ComplexArgumentParser::Finalize(request.credentialIndex); } -CHIP_ERROR ComplexArgumentParser::Setup(const char * label, - chip::app::Clusters::ServiceArea::Structs::HomeLocationStruct::Type & request, - Json::Value & value) -{ - VerifyOrReturnError(value.isObject(), CHIP_ERROR_INVALID_ARGUMENT); - - // Copy to track which members we already processed. - Json::Value valueCopy(value); - - ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("HomeLocationStruct.locationName", "locationName", - value.isMember("locationName"))); - ReturnErrorOnFailure( - ComplexArgumentParser::EnsureMemberExist("HomeLocationStruct.floorNumber", "floorNumber", value.isMember("floorNumber"))); - ReturnErrorOnFailure( - ComplexArgumentParser::EnsureMemberExist("HomeLocationStruct.areaType", "areaType", value.isMember("areaType"))); - - char labelWithMember[kMaxLabelLength]; - snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "locationName"); - ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.locationName, value["locationName"])); - valueCopy.removeMember("locationName"); - - snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "floorNumber"); - ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.floorNumber, value["floorNumber"])); - valueCopy.removeMember("floorNumber"); - - snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "areaType"); - ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.areaType, value["areaType"])); - valueCopy.removeMember("areaType"); - - return ComplexArgumentParser::EnsureNoMembersRemaining(label, valueCopy); -} - -void ComplexArgumentParser::Finalize(chip::app::Clusters::ServiceArea::Structs::HomeLocationStruct::Type & request) -{ - ComplexArgumentParser::Finalize(request.locationName); - ComplexArgumentParser::Finalize(request.floorNumber); - ComplexArgumentParser::Finalize(request.areaType); -} - CHIP_ERROR ComplexArgumentParser::Setup(const char * label, chip::app::Clusters::ServiceArea::Structs::LocationInfoStruct::Type & request, Json::Value & value) @@ -5405,6 +5404,135 @@ void ComplexArgumentParser::Finalize(chip::app::Clusters::ContentControl::Struct ComplexArgumentParser::Finalize(request.ratingNameDesc); } +CHIP_ERROR ComplexArgumentParser::Setup(const char * label, + chip::app::Clusters::EcosystemInformation::Structs::EcosystemLocationStruct::Type & request, + Json::Value & value) +{ + VerifyOrReturnError(value.isObject(), CHIP_ERROR_INVALID_ARGUMENT); + + // Copy to track which members we already processed. + Json::Value valueCopy(value); + + ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("EcosystemLocationStruct.uniqueLocationID", "uniqueLocationID", + value.isMember("uniqueLocationID"))); + ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("EcosystemLocationStruct.locationDescriptor", + "locationDescriptor", value.isMember("locationDescriptor"))); + ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("EcosystemLocationStruct.locationDescriptorLastEdit", + "locationDescriptorLastEdit", + value.isMember("locationDescriptorLastEdit"))); + + char labelWithMember[kMaxLabelLength]; + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "uniqueLocationID"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.uniqueLocationID, value["uniqueLocationID"])); + valueCopy.removeMember("uniqueLocationID"); + + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "locationDescriptor"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.locationDescriptor, value["locationDescriptor"])); + valueCopy.removeMember("locationDescriptor"); + + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "locationDescriptorLastEdit"); + ReturnErrorOnFailure( + ComplexArgumentParser::Setup(labelWithMember, request.locationDescriptorLastEdit, value["locationDescriptorLastEdit"])); + valueCopy.removeMember("locationDescriptorLastEdit"); + + if (value.isMember("fabricIndex")) + { + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "fabricIndex"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.fabricIndex, value["fabricIndex"])); + } + valueCopy.removeMember("fabricIndex"); + + return ComplexArgumentParser::EnsureNoMembersRemaining(label, valueCopy); +} + +void ComplexArgumentParser::Finalize(chip::app::Clusters::EcosystemInformation::Structs::EcosystemLocationStruct::Type & request) +{ + ComplexArgumentParser::Finalize(request.uniqueLocationID); + ComplexArgumentParser::Finalize(request.locationDescriptor); + ComplexArgumentParser::Finalize(request.locationDescriptorLastEdit); + ComplexArgumentParser::Finalize(request.fabricIndex); +} + +CHIP_ERROR ComplexArgumentParser::Setup(const char * label, + chip::app::Clusters::EcosystemInformation::Structs::EcosystemDeviceStruct::Type & request, + Json::Value & value) +{ + VerifyOrReturnError(value.isObject(), CHIP_ERROR_INVALID_ARGUMENT); + + // Copy to track which members we already processed. + Json::Value valueCopy(value); + + ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("EcosystemDeviceStruct.bridgedEndpoint", "bridgedEndpoint", + value.isMember("bridgedEndpoint"))); + ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("EcosystemDeviceStruct.originalEndpoint", "originalEndpoint", + value.isMember("originalEndpoint"))); + ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("EcosystemDeviceStruct.deviceTypes", "deviceTypes", + value.isMember("deviceTypes"))); + ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("EcosystemDeviceStruct.uniqueLocationIDs", "uniqueLocationIDs", + value.isMember("uniqueLocationIDs"))); + ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("EcosystemDeviceStruct.uniqueLocationIDsLastEdit", + "uniqueLocationIDsLastEdit", + value.isMember("uniqueLocationIDsLastEdit"))); + + char labelWithMember[kMaxLabelLength]; + if (value.isMember("deviceName")) + { + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "deviceName"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.deviceName, value["deviceName"])); + } + valueCopy.removeMember("deviceName"); + + if (value.isMember("deviceNameLastEdit")) + { + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "deviceNameLastEdit"); + ReturnErrorOnFailure( + ComplexArgumentParser::Setup(labelWithMember, request.deviceNameLastEdit, value["deviceNameLastEdit"])); + } + valueCopy.removeMember("deviceNameLastEdit"); + + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "bridgedEndpoint"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.bridgedEndpoint, value["bridgedEndpoint"])); + valueCopy.removeMember("bridgedEndpoint"); + + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "originalEndpoint"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.originalEndpoint, value["originalEndpoint"])); + valueCopy.removeMember("originalEndpoint"); + + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "deviceTypes"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.deviceTypes, value["deviceTypes"])); + valueCopy.removeMember("deviceTypes"); + + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "uniqueLocationIDs"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.uniqueLocationIDs, value["uniqueLocationIDs"])); + valueCopy.removeMember("uniqueLocationIDs"); + + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "uniqueLocationIDsLastEdit"); + ReturnErrorOnFailure( + ComplexArgumentParser::Setup(labelWithMember, request.uniqueLocationIDsLastEdit, value["uniqueLocationIDsLastEdit"])); + valueCopy.removeMember("uniqueLocationIDsLastEdit"); + + if (value.isMember("fabricIndex")) + { + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "fabricIndex"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.fabricIndex, value["fabricIndex"])); + } + valueCopy.removeMember("fabricIndex"); + + return ComplexArgumentParser::EnsureNoMembersRemaining(label, valueCopy); +} + +void ComplexArgumentParser::Finalize(chip::app::Clusters::EcosystemInformation::Structs::EcosystemDeviceStruct::Type & request) +{ + ComplexArgumentParser::Finalize(request.deviceName); + ComplexArgumentParser::Finalize(request.deviceNameLastEdit); + ComplexArgumentParser::Finalize(request.bridgedEndpoint); + ComplexArgumentParser::Finalize(request.originalEndpoint); + ComplexArgumentParser::Finalize(request.deviceTypes); + ComplexArgumentParser::Finalize(request.uniqueLocationIDs); + ComplexArgumentParser::Finalize(request.uniqueLocationIDsLastEdit); + ComplexArgumentParser::Finalize(request.fabricIndex); +} + CHIP_ERROR ComplexArgumentParser::Setup(const char * label, chip::app::Clusters::UnitTesting::Structs::SimpleStruct::Type & request, Json::Value & value) { diff --git a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.h b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.h index c258467efab2a6..f777963970303c 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.h @@ -42,6 +42,16 @@ static CHIP_ERROR Setup(const char * label, chip::app::Clusters::detail::Structs static void Finalize(chip::app::Clusters::detail::Structs::MeasurementAccuracyStruct::Type & request); +static CHIP_ERROR Setup(const char * label, chip::app::Clusters::detail::Structs::HomeLocationStruct::Type & request, + Json::Value & value); + +static void Finalize(chip::app::Clusters::detail::Structs::HomeLocationStruct::Type & request); + +static CHIP_ERROR Setup(const char * label, chip::app::Clusters::detail::Structs::DeviceTypeStruct::Type & request, + Json::Value & value); + +static void Finalize(chip::app::Clusters::detail::Structs::DeviceTypeStruct::Type & request); + static CHIP_ERROR Setup(const char * label, chip::app::Clusters::detail::Structs::ApplicationStruct::Type & request, Json::Value & value); @@ -61,11 +71,6 @@ static CHIP_ERROR Setup(const char * label, chip::app::Clusters::detail::Structs static void Finalize(chip::app::Clusters::detail::Structs::OperationalStateStruct::Type & request); -static CHIP_ERROR Setup(const char * label, chip::app::Clusters::Descriptor::Structs::DeviceTypeStruct::Type & request, - Json::Value & value); - -static void Finalize(chip::app::Clusters::Descriptor::Structs::DeviceTypeStruct::Type & request); - static CHIP_ERROR Setup(const char * label, chip::app::Clusters::Descriptor::Structs::SemanticTagStruct::Type & request, Json::Value & value); @@ -426,11 +431,6 @@ static CHIP_ERROR Setup(const char * label, chip::app::Clusters::DoorLock::Struc static void Finalize(chip::app::Clusters::DoorLock::Structs::CredentialStruct::Type & request); -static CHIP_ERROR Setup(const char * label, chip::app::Clusters::ServiceArea::Structs::HomeLocationStruct::Type & request, - Json::Value & value); - -static void Finalize(chip::app::Clusters::ServiceArea::Structs::HomeLocationStruct::Type & request); - static CHIP_ERROR Setup(const char * label, chip::app::Clusters::ServiceArea::Structs::LocationInfoStruct::Type & request, Json::Value & value); @@ -620,6 +620,18 @@ static CHIP_ERROR Setup(const char * label, chip::app::Clusters::ContentControl: static void Finalize(chip::app::Clusters::ContentControl::Structs::RatingNameStruct::Type & request); +static CHIP_ERROR Setup(const char * label, + chip::app::Clusters::EcosystemInformation::Structs::EcosystemLocationStruct::Type & request, + Json::Value & value); + +static void Finalize(chip::app::Clusters::EcosystemInformation::Structs::EcosystemLocationStruct::Type & request); + +static CHIP_ERROR Setup(const char * label, + chip::app::Clusters::EcosystemInformation::Structs::EcosystemDeviceStruct::Type & request, + Json::Value & value); + +static void Finalize(chip::app::Clusters::EcosystemInformation::Structs::EcosystemDeviceStruct::Type & request); + static CHIP_ERROR Setup(const char * label, chip::app::Clusters::UnitTesting::Structs::SimpleStruct::Type & request, Json::Value & value); diff --git a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp index 5071a49a141e51..1fade3e28c9c22 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp +++ b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp @@ -202,6 +202,64 @@ CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, return CHIP_NO_ERROR; } +CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, + const chip::app::Clusters::detail::Structs::HomeLocationStruct::DecodableType & value) +{ + DataModelLogger::LogString(label, indent, "{"); + { + CHIP_ERROR err = LogValue("LocationName", indent + 1, value.locationName); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'LocationName'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("FloorNumber", indent + 1, value.floorNumber); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'FloorNumber'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("AreaType", indent + 1, value.areaType); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'AreaType'"); + return err; + } + } + DataModelLogger::LogString(indent, "}"); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, + const chip::app::Clusters::detail::Structs::DeviceTypeStruct::DecodableType & value) +{ + DataModelLogger::LogString(label, indent, "{"); + { + CHIP_ERROR err = LogValue("DeviceType", indent + 1, value.deviceType); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'DeviceType'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("Revision", indent + 1, value.revision); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'Revision'"); + return err; + } + } + DataModelLogger::LogString(indent, "}"); + + return CHIP_NO_ERROR; +} + CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, const chip::app::Clusters::detail::Structs::ApplicationStruct::DecodableType & value) { @@ -310,31 +368,6 @@ CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, return CHIP_NO_ERROR; } -CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, - const chip::app::Clusters::Descriptor::Structs::DeviceTypeStruct::DecodableType & value) -{ - DataModelLogger::LogString(label, indent, "{"); - { - CHIP_ERROR err = LogValue("DeviceType", indent + 1, value.deviceType); - if (err != CHIP_NO_ERROR) - { - DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'DeviceType'"); - return err; - } - } - { - CHIP_ERROR err = LogValue("Revision", indent + 1, value.revision); - if (err != CHIP_NO_ERROR) - { - DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'Revision'"); - return err; - } - } - DataModelLogger::LogString(indent, "}"); - - return CHIP_NO_ERROR; -} - CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, const chip::app::Clusters::Descriptor::Structs::SemanticTagStruct::DecodableType & value) { @@ -3321,39 +3354,6 @@ CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, return CHIP_NO_ERROR; } -CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, - const chip::app::Clusters::ServiceArea::Structs::HomeLocationStruct::DecodableType & value) -{ - DataModelLogger::LogString(label, indent, "{"); - { - CHIP_ERROR err = LogValue("LocationName", indent + 1, value.locationName); - if (err != CHIP_NO_ERROR) - { - DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'LocationName'"); - return err; - } - } - { - CHIP_ERROR err = LogValue("FloorNumber", indent + 1, value.floorNumber); - if (err != CHIP_NO_ERROR) - { - DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'FloorNumber'"); - return err; - } - } - { - CHIP_ERROR err = LogValue("AreaType", indent + 1, value.areaType); - if (err != CHIP_NO_ERROR) - { - DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'AreaType'"); - return err; - } - } - DataModelLogger::LogString(indent, "}"); - - return CHIP_NO_ERROR; -} - CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, const chip::app::Clusters::ServiceArea::Structs::LocationInfoStruct::DecodableType & value) { @@ -4764,6 +4764,122 @@ CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, return CHIP_NO_ERROR; } +CHIP_ERROR +DataModelLogger::LogValue(const char * label, size_t indent, + const chip::app::Clusters::EcosystemInformation::Structs::EcosystemLocationStruct::DecodableType & value) +{ + DataModelLogger::LogString(label, indent, "{"); + { + CHIP_ERROR err = LogValue("UniqueLocationID", indent + 1, value.uniqueLocationID); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'UniqueLocationID'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("LocationDescriptor", indent + 1, value.locationDescriptor); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'LocationDescriptor'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("LocationDescriptorLastEdit", indent + 1, value.locationDescriptorLastEdit); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'LocationDescriptorLastEdit'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("FabricIndex", indent + 1, value.fabricIndex); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'FabricIndex'"); + return err; + } + } + DataModelLogger::LogString(indent, "}"); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR +DataModelLogger::LogValue(const char * label, size_t indent, + const chip::app::Clusters::EcosystemInformation::Structs::EcosystemDeviceStruct::DecodableType & value) +{ + DataModelLogger::LogString(label, indent, "{"); + { + CHIP_ERROR err = LogValue("DeviceName", indent + 1, value.deviceName); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'DeviceName'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("DeviceNameLastEdit", indent + 1, value.deviceNameLastEdit); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'DeviceNameLastEdit'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("BridgedEndpoint", indent + 1, value.bridgedEndpoint); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'BridgedEndpoint'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("OriginalEndpoint", indent + 1, value.originalEndpoint); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'OriginalEndpoint'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("DeviceTypes", indent + 1, value.deviceTypes); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'DeviceTypes'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("UniqueLocationIDs", indent + 1, value.uniqueLocationIDs); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'UniqueLocationIDs'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("UniqueLocationIDsLastEdit", indent + 1, value.uniqueLocationIDsLastEdit); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'UniqueLocationIDsLastEdit'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("FabricIndex", indent + 1, value.fabricIndex); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'FabricIndex'"); + return err; + } + } + DataModelLogger::LogString(indent, "}"); + + return CHIP_NO_ERROR; +} + CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, const chip::app::Clusters::UnitTesting::Structs::SimpleStruct::DecodableType & value) { @@ -17627,6 +17743,61 @@ CHIP_ERROR DataModelLogger::LogAttribute(const chip::app::ConcreteDataAttributeP } break; } + case EcosystemInformation::Id: { + switch (path.mAttributeId) + { + case EcosystemInformation::Attributes::RemovedOn::Id: { + chip::app::DataModel::Nullable value; + ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); + return DataModelLogger::LogValue("RemovedOn", 1, value); + } + case EcosystemInformation::Attributes::DeviceDirectory::Id: { + chip::app::DataModel::DecodableList< + chip::app::Clusters::EcosystemInformation::Structs::EcosystemDeviceStruct::DecodableType> + value; + ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); + return DataModelLogger::LogValue("DeviceDirectory", 1, value); + } + case EcosystemInformation::Attributes::LocationDirectory::Id: { + chip::app::DataModel::DecodableList< + chip::app::Clusters::EcosystemInformation::Structs::EcosystemLocationStruct::DecodableType> + value; + ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); + return DataModelLogger::LogValue("LocationDirectory", 1, value); + } + case EcosystemInformation::Attributes::GeneratedCommandList::Id: { + chip::app::DataModel::DecodableList value; + ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); + return DataModelLogger::LogValue("GeneratedCommandList", 1, value); + } + case EcosystemInformation::Attributes::AcceptedCommandList::Id: { + chip::app::DataModel::DecodableList value; + ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); + return DataModelLogger::LogValue("AcceptedCommandList", 1, value); + } + case EcosystemInformation::Attributes::EventList::Id: { + chip::app::DataModel::DecodableList value; + ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); + return DataModelLogger::LogValue("EventList", 1, value); + } + case EcosystemInformation::Attributes::AttributeList::Id: { + chip::app::DataModel::DecodableList value; + ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); + return DataModelLogger::LogValue("AttributeList", 1, value); + } + case EcosystemInformation::Attributes::FeatureMap::Id: { + uint32_t value; + ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); + return DataModelLogger::LogValue("FeatureMap", 1, value); + } + case EcosystemInformation::Attributes::ClusterRevision::Id: { + uint16_t value; + ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); + return DataModelLogger::LogValue("ClusterRevision", 1, value); + } + } + break; + } case CommissionerControl::Id: { switch (path.mAttributeId) { diff --git a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h index 95cc96d293b574..7680e14c314316 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h @@ -32,6 +32,12 @@ static CHIP_ERROR LogValue(const char * label, size_t indent, static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::detail::Structs::MeasurementAccuracyStruct::DecodableType & value); +static CHIP_ERROR LogValue(const char * label, size_t indent, + const chip::app::Clusters::detail::Structs::HomeLocationStruct::DecodableType & value); + +static CHIP_ERROR LogValue(const char * label, size_t indent, + const chip::app::Clusters::detail::Structs::DeviceTypeStruct::DecodableType & value); + static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::detail::Structs::ApplicationStruct::DecodableType & value); @@ -44,9 +50,6 @@ static CHIP_ERROR LogValue(const char * label, size_t indent, static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::detail::Structs::OperationalStateStruct::DecodableType & value); -static CHIP_ERROR LogValue(const char * label, size_t indent, - const chip::app::Clusters::Descriptor::Structs::DeviceTypeStruct::DecodableType & value); - static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::Descriptor::Structs::SemanticTagStruct::DecodableType & value); @@ -267,9 +270,6 @@ static CHIP_ERROR LogValue(const char * label, size_t indent, static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::DoorLock::Structs::CredentialStruct::DecodableType & value); -static CHIP_ERROR LogValue(const char * label, size_t indent, - const chip::app::Clusters::ServiceArea::Structs::HomeLocationStruct::DecodableType & value); - static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::ServiceArea::Structs::LocationInfoStruct::DecodableType & value); @@ -381,6 +381,13 @@ static CHIP_ERROR LogValue(const char * label, size_t indent, static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::ContentControl::Structs::RatingNameStruct::DecodableType & value); +static CHIP_ERROR +LogValue(const char * label, size_t indent, + const chip::app::Clusters::EcosystemInformation::Structs::EcosystemLocationStruct::DecodableType & value); + +static CHIP_ERROR LogValue(const char * label, size_t indent, + const chip::app::Clusters::EcosystemInformation::Structs::EcosystemDeviceStruct::DecodableType & value); + static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::UnitTesting::Structs::SimpleStruct::DecodableType & value); diff --git a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h index 17451309d1e621..8ebe6735d29dd0 100644 --- a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h @@ -157,6 +157,7 @@ | AccountLogin | 0x050E | | ContentControl | 0x050F | | ContentAppObserver | 0x0510 | +| EcosystemInformation | 0x0750 | | CommissionerControl | 0x0751 | | ElectricalMeasurement | 0x0B04 | | UnitTesting | 0xFFF1FC05| @@ -163290,6 +163291,800 @@ class SubscribeAttributeContentAppObserverClusterRevision : public SubscribeAttr } }; +#endif // MTR_ENABLE_PROVISIONAL +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL +/*----------------------------------------------------------------------------*\ +| Cluster EcosystemInformation | 0x0750 | +|------------------------------------------------------------------------------| +| Commands: | | +|------------------------------------------------------------------------------| +| Attributes: | | +| * RemovedOn | 0x0000 | +| * DeviceDirectory | 0x0001 | +| * LocationDirectory | 0x0002 | +| * GeneratedCommandList | 0xFFF8 | +| * AcceptedCommandList | 0xFFF9 | +| * EventList | 0xFFFA | +| * AttributeList | 0xFFFB | +| * FeatureMap | 0xFFFC | +| * ClusterRevision | 0xFFFD | +|------------------------------------------------------------------------------| +| Events: | | +\*----------------------------------------------------------------------------*/ + +#if MTR_ENABLE_PROVISIONAL + +/* + * Attribute RemovedOn + */ +class ReadEcosystemInformationRemovedOn : public ReadAttribute { +public: + ReadEcosystemInformationRemovedOn() + : ReadAttribute("removed-on") + { + } + + ~ReadEcosystemInformationRemovedOn() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::AttributeId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::RemovedOn::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReadAttribute (0x%08" PRIX32 ") on endpoint %u", endpointId, clusterId, attributeId); + + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + [cluster readAttributeRemovedOnWithCompletion:^(NSNumber * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.RemovedOn response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + LogNSError("EcosystemInformation RemovedOn read Error", error); + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + return CHIP_NO_ERROR; + } +}; + +class SubscribeAttributeEcosystemInformationRemovedOn : public SubscribeAttribute { +public: + SubscribeAttributeEcosystemInformationRemovedOn() + : SubscribeAttribute("removed-on") + { + } + + ~SubscribeAttributeEcosystemInformationRemovedOn() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::CommandId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::RemovedOn::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReportAttribute (0x%08" PRIX32 ") on endpoint %u", clusterId, attributeId, endpointId); + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(mMinInterval) maxInterval:@(mMaxInterval)]; + if (mKeepSubscriptions.HasValue()) { + params.replaceExistingSubscriptions = !mKeepSubscriptions.Value(); + } + if (mFabricFiltered.HasValue()) { + params.filterByFabric = mFabricFiltered.Value(); + } + if (mAutoResubscribe.HasValue()) { + params.resubscribeAutomatically = mAutoResubscribe.Value(); + } + [cluster subscribeAttributeRemovedOnWithParams:params + subscriptionEstablished:^() { mSubscriptionEstablished = YES; } + reportHandler:^(NSNumber * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.RemovedOn response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + + return CHIP_NO_ERROR; + } +}; + +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + +/* + * Attribute DeviceDirectory + */ +class ReadEcosystemInformationDeviceDirectory : public ReadAttribute { +public: + ReadEcosystemInformationDeviceDirectory() + : ReadAttribute("device-directory") + { + } + + ~ReadEcosystemInformationDeviceDirectory() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::AttributeId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::DeviceDirectory::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReadAttribute (0x%08" PRIX32 ") on endpoint %u", endpointId, clusterId, attributeId); + + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + __auto_type * params = [[MTRReadParams alloc] init]; + if (mFabricFiltered.HasValue()) { + params.filterByFabric = mFabricFiltered.Value(); + } + [cluster readAttributeDeviceDirectoryWithParams:params completion:^(NSArray * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.DeviceDirectory response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + LogNSError("EcosystemInformation DeviceDirectory read Error", error); + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + return CHIP_NO_ERROR; + } +}; + +class SubscribeAttributeEcosystemInformationDeviceDirectory : public SubscribeAttribute { +public: + SubscribeAttributeEcosystemInformationDeviceDirectory() + : SubscribeAttribute("device-directory") + { + } + + ~SubscribeAttributeEcosystemInformationDeviceDirectory() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::CommandId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::DeviceDirectory::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReportAttribute (0x%08" PRIX32 ") on endpoint %u", clusterId, attributeId, endpointId); + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(mMinInterval) maxInterval:@(mMaxInterval)]; + if (mKeepSubscriptions.HasValue()) { + params.replaceExistingSubscriptions = !mKeepSubscriptions.Value(); + } + if (mFabricFiltered.HasValue()) { + params.filterByFabric = mFabricFiltered.Value(); + } + if (mAutoResubscribe.HasValue()) { + params.resubscribeAutomatically = mAutoResubscribe.Value(); + } + [cluster subscribeAttributeDeviceDirectoryWithParams:params + subscriptionEstablished:^() { mSubscriptionEstablished = YES; } + reportHandler:^(NSArray * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.DeviceDirectory response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + + return CHIP_NO_ERROR; + } +}; + +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + +/* + * Attribute LocationDirectory + */ +class ReadEcosystemInformationLocationDirectory : public ReadAttribute { +public: + ReadEcosystemInformationLocationDirectory() + : ReadAttribute("location-directory") + { + } + + ~ReadEcosystemInformationLocationDirectory() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::AttributeId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::LocationDirectory::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReadAttribute (0x%08" PRIX32 ") on endpoint %u", endpointId, clusterId, attributeId); + + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + __auto_type * params = [[MTRReadParams alloc] init]; + if (mFabricFiltered.HasValue()) { + params.filterByFabric = mFabricFiltered.Value(); + } + [cluster readAttributeLocationDirectoryWithParams:params completion:^(NSArray * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.LocationDirectory response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + LogNSError("EcosystemInformation LocationDirectory read Error", error); + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + return CHIP_NO_ERROR; + } +}; + +class SubscribeAttributeEcosystemInformationLocationDirectory : public SubscribeAttribute { +public: + SubscribeAttributeEcosystemInformationLocationDirectory() + : SubscribeAttribute("location-directory") + { + } + + ~SubscribeAttributeEcosystemInformationLocationDirectory() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::CommandId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::LocationDirectory::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReportAttribute (0x%08" PRIX32 ") on endpoint %u", clusterId, attributeId, endpointId); + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(mMinInterval) maxInterval:@(mMaxInterval)]; + if (mKeepSubscriptions.HasValue()) { + params.replaceExistingSubscriptions = !mKeepSubscriptions.Value(); + } + if (mFabricFiltered.HasValue()) { + params.filterByFabric = mFabricFiltered.Value(); + } + if (mAutoResubscribe.HasValue()) { + params.resubscribeAutomatically = mAutoResubscribe.Value(); + } + [cluster subscribeAttributeLocationDirectoryWithParams:params + subscriptionEstablished:^() { mSubscriptionEstablished = YES; } + reportHandler:^(NSArray * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.LocationDirectory response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + + return CHIP_NO_ERROR; + } +}; + +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + +/* + * Attribute GeneratedCommandList + */ +class ReadEcosystemInformationGeneratedCommandList : public ReadAttribute { +public: + ReadEcosystemInformationGeneratedCommandList() + : ReadAttribute("generated-command-list") + { + } + + ~ReadEcosystemInformationGeneratedCommandList() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::AttributeId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::GeneratedCommandList::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReadAttribute (0x%08" PRIX32 ") on endpoint %u", endpointId, clusterId, attributeId); + + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + [cluster readAttributeGeneratedCommandListWithCompletion:^(NSArray * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.GeneratedCommandList response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + LogNSError("EcosystemInformation GeneratedCommandList read Error", error); + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + return CHIP_NO_ERROR; + } +}; + +class SubscribeAttributeEcosystemInformationGeneratedCommandList : public SubscribeAttribute { +public: + SubscribeAttributeEcosystemInformationGeneratedCommandList() + : SubscribeAttribute("generated-command-list") + { + } + + ~SubscribeAttributeEcosystemInformationGeneratedCommandList() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::CommandId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::GeneratedCommandList::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReportAttribute (0x%08" PRIX32 ") on endpoint %u", clusterId, attributeId, endpointId); + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(mMinInterval) maxInterval:@(mMaxInterval)]; + if (mKeepSubscriptions.HasValue()) { + params.replaceExistingSubscriptions = !mKeepSubscriptions.Value(); + } + if (mFabricFiltered.HasValue()) { + params.filterByFabric = mFabricFiltered.Value(); + } + if (mAutoResubscribe.HasValue()) { + params.resubscribeAutomatically = mAutoResubscribe.Value(); + } + [cluster subscribeAttributeGeneratedCommandListWithParams:params + subscriptionEstablished:^() { mSubscriptionEstablished = YES; } + reportHandler:^(NSArray * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.GeneratedCommandList response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + + return CHIP_NO_ERROR; + } +}; + +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + +/* + * Attribute AcceptedCommandList + */ +class ReadEcosystemInformationAcceptedCommandList : public ReadAttribute { +public: + ReadEcosystemInformationAcceptedCommandList() + : ReadAttribute("accepted-command-list") + { + } + + ~ReadEcosystemInformationAcceptedCommandList() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::AttributeId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::AcceptedCommandList::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReadAttribute (0x%08" PRIX32 ") on endpoint %u", endpointId, clusterId, attributeId); + + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + [cluster readAttributeAcceptedCommandListWithCompletion:^(NSArray * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.AcceptedCommandList response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + LogNSError("EcosystemInformation AcceptedCommandList read Error", error); + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + return CHIP_NO_ERROR; + } +}; + +class SubscribeAttributeEcosystemInformationAcceptedCommandList : public SubscribeAttribute { +public: + SubscribeAttributeEcosystemInformationAcceptedCommandList() + : SubscribeAttribute("accepted-command-list") + { + } + + ~SubscribeAttributeEcosystemInformationAcceptedCommandList() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::CommandId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::AcceptedCommandList::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReportAttribute (0x%08" PRIX32 ") on endpoint %u", clusterId, attributeId, endpointId); + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(mMinInterval) maxInterval:@(mMaxInterval)]; + if (mKeepSubscriptions.HasValue()) { + params.replaceExistingSubscriptions = !mKeepSubscriptions.Value(); + } + if (mFabricFiltered.HasValue()) { + params.filterByFabric = mFabricFiltered.Value(); + } + if (mAutoResubscribe.HasValue()) { + params.resubscribeAutomatically = mAutoResubscribe.Value(); + } + [cluster subscribeAttributeAcceptedCommandListWithParams:params + subscriptionEstablished:^() { mSubscriptionEstablished = YES; } + reportHandler:^(NSArray * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.AcceptedCommandList response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + + return CHIP_NO_ERROR; + } +}; + +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + +/* + * Attribute EventList + */ +class ReadEcosystemInformationEventList : public ReadAttribute { +public: + ReadEcosystemInformationEventList() + : ReadAttribute("event-list") + { + } + + ~ReadEcosystemInformationEventList() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::AttributeId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::EventList::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReadAttribute (0x%08" PRIX32 ") on endpoint %u", endpointId, clusterId, attributeId); + + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + [cluster readAttributeEventListWithCompletion:^(NSArray * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.EventList response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + LogNSError("EcosystemInformation EventList read Error", error); + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + return CHIP_NO_ERROR; + } +}; + +class SubscribeAttributeEcosystemInformationEventList : public SubscribeAttribute { +public: + SubscribeAttributeEcosystemInformationEventList() + : SubscribeAttribute("event-list") + { + } + + ~SubscribeAttributeEcosystemInformationEventList() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::CommandId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::EventList::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReportAttribute (0x%08" PRIX32 ") on endpoint %u", clusterId, attributeId, endpointId); + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(mMinInterval) maxInterval:@(mMaxInterval)]; + if (mKeepSubscriptions.HasValue()) { + params.replaceExistingSubscriptions = !mKeepSubscriptions.Value(); + } + if (mFabricFiltered.HasValue()) { + params.filterByFabric = mFabricFiltered.Value(); + } + if (mAutoResubscribe.HasValue()) { + params.resubscribeAutomatically = mAutoResubscribe.Value(); + } + [cluster subscribeAttributeEventListWithParams:params + subscriptionEstablished:^() { mSubscriptionEstablished = YES; } + reportHandler:^(NSArray * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.EventList response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + + return CHIP_NO_ERROR; + } +}; + +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + +/* + * Attribute AttributeList + */ +class ReadEcosystemInformationAttributeList : public ReadAttribute { +public: + ReadEcosystemInformationAttributeList() + : ReadAttribute("attribute-list") + { + } + + ~ReadEcosystemInformationAttributeList() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::AttributeId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::AttributeList::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReadAttribute (0x%08" PRIX32 ") on endpoint %u", endpointId, clusterId, attributeId); + + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + [cluster readAttributeAttributeListWithCompletion:^(NSArray * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.AttributeList response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + LogNSError("EcosystemInformation AttributeList read Error", error); + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + return CHIP_NO_ERROR; + } +}; + +class SubscribeAttributeEcosystemInformationAttributeList : public SubscribeAttribute { +public: + SubscribeAttributeEcosystemInformationAttributeList() + : SubscribeAttribute("attribute-list") + { + } + + ~SubscribeAttributeEcosystemInformationAttributeList() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::CommandId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::AttributeList::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReportAttribute (0x%08" PRIX32 ") on endpoint %u", clusterId, attributeId, endpointId); + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(mMinInterval) maxInterval:@(mMaxInterval)]; + if (mKeepSubscriptions.HasValue()) { + params.replaceExistingSubscriptions = !mKeepSubscriptions.Value(); + } + if (mFabricFiltered.HasValue()) { + params.filterByFabric = mFabricFiltered.Value(); + } + if (mAutoResubscribe.HasValue()) { + params.resubscribeAutomatically = mAutoResubscribe.Value(); + } + [cluster subscribeAttributeAttributeListWithParams:params + subscriptionEstablished:^() { mSubscriptionEstablished = YES; } + reportHandler:^(NSArray * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.AttributeList response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + + return CHIP_NO_ERROR; + } +}; + +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + +/* + * Attribute FeatureMap + */ +class ReadEcosystemInformationFeatureMap : public ReadAttribute { +public: + ReadEcosystemInformationFeatureMap() + : ReadAttribute("feature-map") + { + } + + ~ReadEcosystemInformationFeatureMap() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::AttributeId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::FeatureMap::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReadAttribute (0x%08" PRIX32 ") on endpoint %u", endpointId, clusterId, attributeId); + + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + [cluster readAttributeFeatureMapWithCompletion:^(NSNumber * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.FeatureMap response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + LogNSError("EcosystemInformation FeatureMap read Error", error); + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + return CHIP_NO_ERROR; + } +}; + +class SubscribeAttributeEcosystemInformationFeatureMap : public SubscribeAttribute { +public: + SubscribeAttributeEcosystemInformationFeatureMap() + : SubscribeAttribute("feature-map") + { + } + + ~SubscribeAttributeEcosystemInformationFeatureMap() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::CommandId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::FeatureMap::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReportAttribute (0x%08" PRIX32 ") on endpoint %u", clusterId, attributeId, endpointId); + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(mMinInterval) maxInterval:@(mMaxInterval)]; + if (mKeepSubscriptions.HasValue()) { + params.replaceExistingSubscriptions = !mKeepSubscriptions.Value(); + } + if (mFabricFiltered.HasValue()) { + params.filterByFabric = mFabricFiltered.Value(); + } + if (mAutoResubscribe.HasValue()) { + params.resubscribeAutomatically = mAutoResubscribe.Value(); + } + [cluster subscribeAttributeFeatureMapWithParams:params + subscriptionEstablished:^() { mSubscriptionEstablished = YES; } + reportHandler:^(NSNumber * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.FeatureMap response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + + return CHIP_NO_ERROR; + } +}; + +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + +/* + * Attribute ClusterRevision + */ +class ReadEcosystemInformationClusterRevision : public ReadAttribute { +public: + ReadEcosystemInformationClusterRevision() + : ReadAttribute("cluster-revision") + { + } + + ~ReadEcosystemInformationClusterRevision() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::AttributeId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::ClusterRevision::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReadAttribute (0x%08" PRIX32 ") on endpoint %u", endpointId, clusterId, attributeId); + + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + [cluster readAttributeClusterRevisionWithCompletion:^(NSNumber * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.ClusterRevision response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + LogNSError("EcosystemInformation ClusterRevision read Error", error); + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + return CHIP_NO_ERROR; + } +}; + +class SubscribeAttributeEcosystemInformationClusterRevision : public SubscribeAttribute { +public: + SubscribeAttributeEcosystemInformationClusterRevision() + : SubscribeAttribute("cluster-revision") + { + } + + ~SubscribeAttributeEcosystemInformationClusterRevision() + { + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + constexpr chip::ClusterId clusterId = chip::app::Clusters::EcosystemInformation::Id; + constexpr chip::CommandId attributeId = chip::app::Clusters::EcosystemInformation::Attributes::ClusterRevision::Id; + + ChipLogProgress(chipTool, "Sending cluster (0x%08" PRIX32 ") ReportAttribute (0x%08" PRIX32 ") on endpoint %u", clusterId, attributeId, endpointId); + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterEcosystemInformation alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(mMinInterval) maxInterval:@(mMaxInterval)]; + if (mKeepSubscriptions.HasValue()) { + params.replaceExistingSubscriptions = !mKeepSubscriptions.Value(); + } + if (mFabricFiltered.HasValue()) { + params.filterByFabric = mFabricFiltered.Value(); + } + if (mAutoResubscribe.HasValue()) { + params.resubscribeAutomatically = mAutoResubscribe.Value(); + } + [cluster subscribeAttributeClusterRevisionWithParams:params + subscriptionEstablished:^() { mSubscriptionEstablished = YES; } + reportHandler:^(NSNumber * _Nullable value, NSError * _Nullable error) { + NSLog(@"EcosystemInformation.ClusterRevision response %@", [value description]); + if (error == nil) { + RemoteDataModelLogger::LogAttributeAsJSON(@(endpointId), @(clusterId), @(attributeId), value); + } else { + RemoteDataModelLogger::LogAttributeErrorAsJSON(@(endpointId), @(clusterId), @(attributeId), error); + } + SetCommandExitStatus(error); + }]; + + return CHIP_NO_ERROR; + } +}; + #endif // MTR_ENABLE_PROVISIONAL #endif // MTR_ENABLE_PROVISIONAL #if MTR_ENABLE_PROVISIONAL @@ -196257,6 +197052,59 @@ void registerClusterContentAppObserver(Commands & commands) commands.RegisterCluster(clusterName, clusterCommands); #endif // MTR_ENABLE_PROVISIONAL } +void registerClusterEcosystemInformation(Commands & commands) +{ +#if MTR_ENABLE_PROVISIONAL + using namespace chip::app::Clusters::EcosystemInformation; + + const char * clusterName = "EcosystemInformation"; + + commands_list clusterCommands = { + make_unique(Id), // + make_unique(Id), // + make_unique(Id), // + make_unique(Id), // +#if MTR_ENABLE_PROVISIONAL + make_unique(), // + make_unique(), // +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + make_unique(), // + make_unique(), // +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + make_unique(), // + make_unique(), // +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + make_unique(), // + make_unique(), // +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + make_unique(), // + make_unique(), // +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + make_unique(), // + make_unique(), // +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + make_unique(), // + make_unique(), // +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + make_unique(), // + make_unique(), // +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + make_unique(), // + make_unique(), // +#endif // MTR_ENABLE_PROVISIONAL + }; + + commands.RegisterCluster(clusterName, clusterCommands); +#endif // MTR_ENABLE_PROVISIONAL +} void registerClusterCommissionerControl(Commands & commands) { #if MTR_ENABLE_PROVISIONAL @@ -197113,6 +197961,7 @@ void registerClusters(Commands & commands) registerClusterAccountLogin(commands); registerClusterContentControl(commands); registerClusterContentAppObserver(commands); + registerClusterEcosystemInformation(commands); registerClusterCommissionerControl(commands); registerClusterElectricalMeasurement(commands); registerClusterUnitTesting(commands); From 7d26280fc68780ae799a3325dd1db78b20d38874 Mon Sep 17 00:00:00 2001 From: Yufeng Wang Date: Fri, 26 Jul 2024 06:13:58 +0200 Subject: [PATCH 24/49] Implements the Commissioner Control Cluster in Matter SDK (#34375) Co-authored-by: Terence Hampson Co-authored-by: Boris Zbarsky Co-authored-by: Restyled.io --- .../fabric-bridge-app.matter | 66 +++++ .../fabric-bridge-app.zap | 158 +++++++++++ .../commissioner-control-server.cpp | 268 ++++++++++++++++++ .../commissioner-control-server.h | 182 ++++++++++++ 4 files changed, 674 insertions(+) create mode 100644 src/app/clusters/commissioner-control-server/commissioner-control-server.cpp create mode 100644 src/app/clusters/commissioner-control-server/commissioner-control-server.h diff --git a/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.matter b/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.matter index 7f3de425528f64..46bd51d578e6ee 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.matter +++ b/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.matter @@ -1334,6 +1334,57 @@ cluster GroupKeyManagement = 63 { fabric command access(invoke: administer) KeySetReadAllIndices(): KeySetReadAllIndicesResponse = 4; } +/** Supports the ability for clients to request the commissioning of themselves or other nodes onto a fabric which the cluster server can commission onto. */ +provisional cluster CommissionerControl = 1873 { + revision 1; + + bitmap SupportedDeviceCategoryBitmap : bitmap32 { + kFabricSynchronization = 0x1; + } + + fabric_sensitive info event access(read: manage) CommissioningRequestResult = 0 { + int64u requestId = 0; + node_id clientNodeId = 1; + enum8 statusCode = 2; + fabric_idx fabricIndex = 254; + } + + readonly attribute access(read: manage) SupportedDeviceCategoryBitmap supportedDeviceCategories = 0; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct RequestCommissioningApprovalRequest { + int64u requestId = 0; + vendor_id vendorId = 1; + int16u productId = 2; + optional char_string<64> label = 3; + } + + request struct CommissionNodeRequest { + int64u requestId = 0; + int16u responseTimeoutSeconds = 1; + optional octet_string ipAddress = 2; + optional int16u port = 3; + } + + response struct ReverseOpenCommissioningWindow = 2 { + int16u commissioningTimeout = 0; + octet_string PAKEPasscodeVerifier = 1; + int16u discriminator = 2; + int32u iterations = 3; + octet_string<32> salt = 4; + } + + /** This command is sent by a client to request approval for a future CommissionNode call. */ + command access(invoke: manage) RequestCommissioningApproval(RequestCommissioningApprovalRequest): DefaultSuccess = 0; + /** This command is sent by a client to request that the server begins commissioning a previously approved request. */ + command access(invoke: manage) CommissionNode(CommissionNodeRequest): ReverseOpenCommissioningWindow = 1; +} + endpoint 0 { device type ma_rootdevice = 22, version 1; @@ -1647,6 +1698,21 @@ endpoint 0 { handle command KeySetReadAllIndices; handle command KeySetReadAllIndicesResponse; } + + server cluster CommissionerControl { + emits event CommissioningRequestResult; + ram attribute supportedDeviceCategories default = 0; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 1; + + handle command RequestCommissioningApproval; + handle command CommissionNode; + handle command ReverseOpenCommissioningWindow; + } } endpoint 1 { device type ma_aggregator = 14, version 1; diff --git a/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.zap b/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.zap index cbf31a039b85ee..6345745cde4fce 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.zap +++ b/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.zap @@ -4003,6 +4003,164 @@ "reportableChange": 0 } ] + }, + { + "name": "Commissioner Control", + "code": 1873, + "mfgCode": null, + "define": "COMMISSIONER_CONTROL_CLUSTER", + "side": "server", + "enabled": 1, + "apiMaturity": "provisional", + "commands": [ + { + "name": "RequestCommissioningApproval", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "CommissionNode", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "ReverseOpenCommissioningWindow", + "code": 2, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "SupportedDeviceCategories", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "SupportedDeviceCategoryBitmap", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EventList", + "code": 65530, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ], + "events": [ + { + "name": "CommissioningRequestResult", + "code": 0, + "mfgCode": null, + "side": "server", + "included": 1 + } + ] } ] }, diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp new file mode 100644 index 00000000000000..98616f9e0e281b --- /dev/null +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp @@ -0,0 +1,268 @@ +/* + * + * 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 "commissioner-control-server.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace chip; +using namespace chip::app; + +using chip::Protocols::InteractionModel::Status; + +namespace { + +NodeId GetNodeId(const CommandHandler * commandObj) +{ + auto descriptor = commandObj->GetSubjectDescriptor(); + + if (descriptor.authMode != Access::AuthMode::kCase) + { + return kUndefinedNodeId; + } + return descriptor.subject; +} + +void AddReverseOpenCommissioningWindowResponse(CommandHandler * commandObj, const ConcreteCommandPath & path, + const Clusters::CommissionerControl::CommissioningWindowParams & params) +{ + Clusters::CommissionerControl::Commands::ReverseOpenCommissioningWindow::Type response; + response.commissioningTimeout = params.commissioningTimeout; + response.discriminator = params.discriminator; + response.iterations = params.iterations; + response.PAKEPasscodeVerifier = params.PAKEPasscodeVerifier; + response.salt = params.salt; + + commandObj->AddResponse(path, response); +} + +void RunDeferredCommissionNode(intptr_t commandArg) +{ + auto * info = reinterpret_cast(commandArg); + + Clusters::CommissionerControl::Delegate * delegate = + Clusters::CommissionerControl::CommissionerControlServer::Instance().GetDelegate(); + + if (delegate != nullptr) + { + CHIP_ERROR err = delegate->ReverseCommissionNode(info->params, info->ipAddress.GetIPAddress(), info->port); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "ReverseCommissionNode error: %" CHIP_ERROR_FORMAT, err.Format()); + } + } + else + { + ChipLogError(Zcl, "No delegate available for ReverseCommissionNode"); + } + + delete info; +} + +} // namespace + +namespace chip { +namespace app { +namespace Clusters { +namespace CommissionerControl { + +CommissionerControlServer CommissionerControlServer::sInstance; + +CommissionerControlServer & CommissionerControlServer::Instance() +{ + return sInstance; +} + +CHIP_ERROR CommissionerControlServer::Init(Delegate & delegate) +{ + mDelegate = &delegate; + return CHIP_NO_ERROR; +} + +Status CommissionerControlServer::GetSupportedDeviceCategoriesValue( + EndpointId endpoint, BitMask * supportedDeviceCategories) const +{ + Status status = Attributes::SupportedDeviceCategories::Get(endpoint, supportedDeviceCategories); + if (status != Status::Success) + { + ChipLogProgress(Zcl, "CommissionerControl: reading supportedDeviceCategories, err:0x%x", to_underlying(status)); + } + return status; +} + +Status +CommissionerControlServer::SetSupportedDeviceCategoriesValue(EndpointId endpoint, + const BitMask supportedDeviceCategories) +{ + Status status = Status::Success; + + if ((status = Attributes::SupportedDeviceCategories::Set(endpoint, supportedDeviceCategories)) != Status::Success) + { + ChipLogProgress(Zcl, "CommissionerControl: writing supportedDeviceCategories, err:0x%x", to_underlying(status)); + return status; + } + + return status; +} + +CHIP_ERROR +CommissionerControlServer::GenerateCommissioningRequestResultEvent(const Events::CommissioningRequestResult::Type & result) +{ + EventNumber eventNumber; + CHIP_ERROR error = LogEvent(result, kRootEndpointId, eventNumber); + if (CHIP_NO_ERROR != error) + { + ChipLogError(Zcl, "CommissionerControl: Unable to emit CommissioningRequestResult event: %" CHIP_ERROR_FORMAT, + error.Format()); + } + + return error; +} + +} // namespace CommissionerControl +} // namespace Clusters +} // namespace app +} // namespace chip + +bool emberAfCommissionerControlClusterRequestCommissioningApprovalCallback( + app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, + const Clusters::CommissionerControl::Commands::RequestCommissioningApproval::DecodableType & commandData) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + Status status = Status::Success; + + ChipLogProgress(Zcl, "Received command to request commissioning approval"); + + auto sourceNodeId = GetNodeId(commandObj); + + // Check if the command is executed via a CASE session + if (sourceNodeId == kUndefinedNodeId) + { + ChipLogError(Zcl, "Commissioning approval request not executed via CASE session, failing with UNSUPPORTED_ACCESS"); + commandObj->AddStatus(commandPath, Status::UnsupportedAccess); + return true; + } + + auto fabricIndex = commandObj->GetAccessingFabricIndex(); + auto requestId = commandData.requestId; + auto vendorId = commandData.vendorId; + auto productId = commandData.productId; + + // The label assigned from commandData need to be stored in CommissionerControl::Delegate which ensure that the backing buffer + // of it has a valid lifespan during fabric sync setup process. + auto & label = commandData.label; + + // Create a CommissioningApprovalRequest struct and populate it with the command data + Clusters::CommissionerControl::CommissioningApprovalRequest request = { .requestId = requestId, + .vendorId = vendorId, + .productId = productId, + .clientNodeId = sourceNodeId, + .fabricIndex = fabricIndex, + .label = label }; + + Clusters::CommissionerControl::Delegate * delegate = + Clusters::CommissionerControl::CommissionerControlServer::Instance().GetDelegate(); + + VerifyOrExit(delegate != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + + // Handle commissioning approval request + err = delegate->HandleCommissioningApprovalRequest(request); + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "emberAfCommissionerControlClusterRequestCommissioningApprovalCallback error: %" CHIP_ERROR_FORMAT, + err.Format()); + status = StatusIB(err).mStatus; + } + + commandObj->AddStatus(commandPath, status); + return true; +} + +bool emberAfCommissionerControlClusterCommissionNodeCallback( + app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, + const Clusters::CommissionerControl::Commands::CommissionNode::DecodableType & commandData) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + ChipLogProgress(Zcl, "Received command to commission node"); + + auto sourceNodeId = GetNodeId(commandObj); + + // Check if the command is executed via a CASE session + if (sourceNodeId == kUndefinedNodeId) + { + ChipLogError(Zcl, "Commission node request not executed via CASE session, failing with UNSUPPORTED_ACCESS"); + commandObj->AddStatus(commandPath, Status::UnsupportedAccess); + return true; + } + + auto requestId = commandData.requestId; + + auto commissionNodeInfo = std::make_unique(); + + Clusters::CommissionerControl::Delegate * delegate = + Clusters::CommissionerControl::CommissionerControlServer::Instance().GetDelegate(); + + VerifyOrExit(delegate != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + + // Set IP address and port in the CommissionNodeInfo struct + commissionNodeInfo->port = commandData.port; + err = commissionNodeInfo->ipAddress.SetIPAddress(commandData.ipAddress); + SuccessOrExit(err == CHIP_NO_ERROR); + + // Validate the commission node command. + err = delegate->ValidateCommissionNodeCommand(sourceNodeId, requestId); + SuccessOrExit(err == CHIP_NO_ERROR); + + // Populate the parameters for the commissioning window + err = delegate->GetCommissioningWindowParams(commissionNodeInfo->params); + SuccessOrExit(err == CHIP_NO_ERROR); + + // Add the response for the commissioning window. + AddReverseOpenCommissioningWindowResponse(commandObj, commandPath, commissionNodeInfo->params); + + // Schedule the deferred reverse commission node task + DeviceLayer::PlatformMgr().ScheduleWork(RunDeferredCommissionNode, reinterpret_cast(commissionNodeInfo.release())); + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "emberAfCommissionerControlClusterCommissionNodeCallback error: %" CHIP_ERROR_FORMAT, err.Format()); + commandObj->AddStatus(commandPath, StatusIB(err).mStatus); + } + + return true; +} + +void MatterCommissionerControlPluginServerInitCallback() +{ + ChipLogProgress(Zcl, "Initializing Commissioner Control cluster."); +} diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.h b/src/app/clusters/commissioner-control-server/commissioner-control-server.h new file mode 100644 index 00000000000000..5e2422f5ebe018 --- /dev/null +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.h @@ -0,0 +1,182 @@ +/* + * + * 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. + */ +#pragma once + +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace CommissionerControl { + +// Spec indicates that IP Address is either 4 or 16 bytes. +static constexpr size_t kIpAddressBufferSize = 16; + +struct CommissioningApprovalRequest +{ + uint64_t requestId; + VendorId vendorId; + uint16_t productId; + NodeId clientNodeId; + FabricIndex fabricIndex; + Optional label; +}; + +struct CommissioningWindowParams +{ + uint32_t iterations; + uint16_t commissioningTimeout; + uint16_t discriminator; + ByteSpan PAKEPasscodeVerifier; + ByteSpan salt; +}; + +class ProtectedIPAddress +{ +public: + const Optional GetIPAddress() { return ipAddress; } + + CHIP_ERROR SetIPAddress(const Optional & address) + { + if (!address.HasValue()) + { + ipAddress.ClearValue(); + return CHIP_NO_ERROR; + } + + const ByteSpan & addressSpan = address.Value(); + size_t addressLength = addressSpan.size(); + if (addressLength != 4 && addressLength != 16) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + memcpy(ipAddressBuffer, addressSpan.data(), addressLength); + ipAddress.SetValue(ByteSpan(ipAddressBuffer, addressLength)); + return CHIP_NO_ERROR; + } + +private: + Optional ipAddress; + uint8_t ipAddressBuffer[kIpAddressBufferSize]; +}; + +struct CommissionNodeInfo +{ + CommissioningWindowParams params; + ProtectedIPAddress ipAddress; + Optional port; +}; + +class Delegate +{ +public: + /** + * @brief Handle a commissioning approval request. + * + * This command is sent by a client to request approval for a future CommissionNode call. + * The server SHALL always return SUCCESS to a correctly formatted RequestCommissioningApproval + * command, and then send a CommissioningRequestResult event once the result is ready. + * + * @param request The commissioning approval request to handle. + * @return CHIP_ERROR indicating the success or failure of the operation. + */ + virtual CHIP_ERROR HandleCommissioningApprovalRequest(const CommissioningApprovalRequest & request) = 0; + + /** + * @brief Validate a commission node command. + * + * This command is sent by a client to request that the server begins commissioning a previously + * approved request. + * + * The server SHALL return FAILURE if the CommissionNode command is not sent from the same + * NodeId as the RequestCommissioningApproval or if the provided RequestId to CommissionNode + * does not match the value provided to RequestCommissioningApproval. + * + * The validation SHALL fail if the client Node ID is kUndefinedNodeId, such as getting the NodeID from + * a group or PASE session. + * + * @param clientNodeId The NodeId of the client. + * @param requestId The request ID to validate. + * @return CHIP_ERROR indicating the success or failure of the operation. + */ + virtual CHIP_ERROR ValidateCommissionNodeCommand(NodeId clientNodeId, uint64_t requestId) = 0; + + /** + * @brief Get the parameters for the commissioning window. + * + * This method is called to retrieve the parameters needed for the commissioning window. + * + * @param[out] outParams The parameters for the commissioning window. + * @return CHIP_ERROR indicating the success or failure of the operation. + */ + virtual CHIP_ERROR GetCommissioningWindowParams(CommissioningWindowParams & outParams) = 0; + + /** + * @brief Reverse the commission node process. + * + * When received within the timeout specified by CommissionNode, the client SHALL open a + * commissioning window on the node which the client called RequestCommissioningApproval to + * have commissioned. + * + * @param params The parameters for the commissioning window. + * @param ipAddress Optional IP address for the commissioning window. + * @param port Optional port for the commissioning window. + * @return CHIP_ERROR indicating the success or failure of the operation. + */ + virtual CHIP_ERROR ReverseCommissionNode(const CommissioningWindowParams & params, const Optional & ipAddress, + const Optional & port) = 0; + + virtual ~Delegate() = default; +}; + +class CommissionerControlServer +{ +public: + static CommissionerControlServer & Instance(); + + CHIP_ERROR Init(Delegate & delegate); + + Delegate * GetDelegate() { return mDelegate; } + + Protocols::InteractionModel::Status + GetSupportedDeviceCategoriesValue(EndpointId endpoint, + BitMask * supportedDeviceCategories) const; + + Protocols::InteractionModel::Status + SetSupportedDeviceCategoriesValue(EndpointId endpoint, const BitMask supportedDeviceCategories); + + /** + * @brief + * Called after the server return SUCCESS to a correctly formatted RequestCommissioningApproval command. + */ + CHIP_ERROR GenerateCommissioningRequestResultEvent(const Events::CommissioningRequestResult::Type & result); + +private: + CommissionerControlServer() = default; + ~CommissionerControlServer() = default; + + static CommissionerControlServer sInstance; + + Delegate * mDelegate = nullptr; +}; + +} // namespace CommissionerControl +} // namespace Clusters +} // namespace app +} // namespace chip From 6180583d70cb1728109ef5d1ec855d42e1124f1f Mon Sep 17 00:00:00 2001 From: cdj <45139296+DejinChen@users.noreply.github.com> Date: Fri, 26 Jul 2024 12:41:05 +0800 Subject: [PATCH 25/49] Added python testing for secondary network interface (#34307) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added python testing for secondary network interface * Restyled by autopep8 * Restyled by isort * Modified test steps * Use NumNetworkCommissioning in test * Fix CI error. * Update src/python_testing/TC_CNET_1_4.py Co-authored-by: René Josefsen <69624991+ReneJosefsen@users.noreply.github.com> * Update src/python_testing/TC_CNET_1_4.py Co-authored-by: René Josefsen <69624991+ReneJosefsen@users.noreply.github.com> * check response instead of reading on endpoint 0 * modified step description * Modified step description. * Update steps. * Update steps. --------- Co-authored-by: Restyled.io Co-authored-by: René Josefsen <69624991+ReneJosefsen@users.noreply.github.com> --- .github/workflows/tests.yaml | 1 + src/python_testing/TC_CNET_1_4.py | 106 ++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 src/python_testing/TC_CNET_1_4.py diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 554ee752ff2a08..592227758c035e 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -503,6 +503,7 @@ jobs: scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_ACE_1_5.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_AccessChecker.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_CGEN_2_4.py' + scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_CNET_1_4.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_DA_1_2.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_DA_1_5.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_DA_1_7.py' diff --git a/src/python_testing/TC_CNET_1_4.py b/src/python_testing/TC_CNET_1_4.py new file mode 100644 index 00000000000000..d7560c4fd03942 --- /dev/null +++ b/src/python_testing/TC_CNET_1_4.py @@ -0,0 +1,106 @@ +# +# 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. +# + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${ALL_CLUSTERS_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# === END CI TEST ARGUMENTS === + +import logging + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts + +kRootEndpointId = 0 +kSecondaryNetworkInterfaceDeviceTypeId = 0x0019 + + +class TC_CNET_1_4(MatterBaseTest): + def steps_TC_CNET_1_4(self): + return [TestStep(1, "TH is commissioned", is_commissioning=True), + TestStep(2, 'TH performs a wildcard read of the FeatureMap attribute on Network Commissioning clusters across all endpoints, and saves the response as `NetworkCommissioningResponse`'), + TestStep(3, 'If `NetworkCommissioningResponse` does not contain any entries for Network Commissioning cluster, skip remaining steps and end test case'), + TestStep(4, 'If `NetworkCommissioningResponse` contains only a single entry for Network Commissioning cluster on Endpoint 0, skip remaining steps and end test case. Verify `NetworkCommissioningResponse` contains an entry for Network Commissioning cluster on Endpoint 0'), + TestStep(5, 'TH reads from the DUT the Descriptor Cluster DeviceTypeList attribute on each endpoint from the `NetworkCommissioningResponse` (except for Endpoint 0), verify that the Secondary Network Interface device type id (0x0019) is listed in the DeviceTypeList'), + TestStep(6, 'TH reads from the DUT the General Commissioning Cluster SupportsConcurrentConnection attribute, verify that it is true')] + + def def_TC_CNET_1_4(self): + return '[TC-CNET-1.4] Verification for Secondary Network Interface [DUT-Server]' + + def pics_TC_CNET_1_4(self): + return ['CNET.S'] + + # Override default timeout. + @property + def default_timeout(self) -> int: + return 200 + + @async_test_body + async def test_TC_CNET_1_4(self): + # Commissioning is already done + self.step(1) + + self.step(2) + # Read FeatureMap attribute with wildcard endpoint + NetworkCommissioningResponse = await self.default_controller.ReadAttribute(self.dut_node_id, [(Clusters.NetworkCommissioning.Attributes.FeatureMap)], fabricFiltered=True) + + self.step(3) + NumNetworkCommissioning = len(NetworkCommissioningResponse) + if NumNetworkCommissioning == 0: + logging.info('No endpoint has Network Commissioning Cluster, skipping remaining steps') + self.skip_all_remaining_steps(4) + return + + self.step(4) + endpoints = [] + for endpoint, _ in NetworkCommissioningResponse.items(): + endpoints.append(endpoint) + if kRootEndpointId not in endpoints: + asserts.assert_true(False, "There is no Network Commissioning Cluster on endpoint 0") + + if NumNetworkCommissioning == 1: + logging.info('Only endpoint 0 has Network Commissioning Cluster, skipping remaining steps') + self.skip_all_remaining_steps(5) + return + + self.step(5) + for endpoint in endpoints: + if endpoint == kRootEndpointId: + continue + device_type_list = await self.read_single_attribute_check_success(cluster=Clusters.Descriptor, + attribute=Clusters.Descriptor.Attributes.DeviceTypeList, endpoint=endpoint) + required_device_types = {kSecondaryNetworkInterfaceDeviceTypeId} + found_device_types = {device.deviceType for device in device_type_list} + asserts.assert_true(required_device_types.intersection(found_device_types), + "Network Commissioning Cluster is not on Root Node or Secondary Network Interface") + + self.step(6) + concurrent_connection = await self.read_single_attribute_check_success(cluster=Clusters.GeneralCommissioning, + attribute=Clusters.GeneralCommissioning.Attributes.SupportsConcurrentConnection) + asserts.assert_true(concurrent_connection, "The device does not support concurrent connection commissioning") + + +if __name__ == "__main__": + default_matter_test_main() From 42d03d5e7ebe1d2243be81ea7145a9ce016771bf Mon Sep 17 00:00:00 2001 From: Shubham Patil Date: Fri, 26 Jul 2024 11:46:22 +0530 Subject: [PATCH 26/49] [ESP32] Fix compiling examples with insights (#34472) --- config/esp32/components/chip/CMakeLists.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/config/esp32/components/chip/CMakeLists.txt b/config/esp32/components/chip/CMakeLists.txt index d0c1fc4463f442..f27b29baf91ff9 100644 --- a/config/esp32/components/chip/CMakeLists.txt +++ b/config/esp32/components/chip/CMakeLists.txt @@ -472,7 +472,15 @@ if (CONFIG_SEC_CERT_DAC_PROVIDER) endif() if (CONFIG_ENABLE_ESP_INSIGHTS_TRACE) - idf_component_get_property(esp_insights_lib espressif__esp_insights COMPONENT_LIB) + idf_build_get_property(build_components BUILD_COMPONENTS) + # esp_insights can be used as an independent component or through component manager so, + # We should check and add the right component. + if("espressif__esp_insights" IN_LIST build_components) + idf_component_get_property(esp_insights_lib espressif__esp_insights COMPONENT_LIB) + elseif("esp_insights" IN_LIST build_components) + idf_component_get_property(esp_insights_lib esp_insights COMPONENT_LIB) + endif() + list(APPEND chip_libraries $) endif() From a5124a9e5b8fdaed6d7304d406c0edb2cf4a49dd Mon Sep 17 00:00:00 2001 From: PeterC1965 <101805108+PeterC1965@users.noreply.github.com> Date: Fri, 26 Jul 2024 08:15:50 +0100 Subject: [PATCH 27/49] Add DEM test scripts (#34234) * Add DEM test scripts * Restyled by autopep8 * Restyled by isort * Renamed DEMBaseTest.py to TC_DEMTestBase.py so that it's grouped with the TC_DEM scripts. Also split the expected result in the TestSteps() into 3rd 'expected' arg. Corrected some missing Verification steps. * Updated DEM_2.4 with corrections to match latest test plan. Also corrected PICS and description from DEM 2.3 * Restyled by isort * TC_DEM_2_5.py updated with TestSteps and corrections to match latest test plan * Reformatted TC_DEM_2_9.py * Changed PICS for DEM_2_5.py to have it as a single boolean - unsure on the correct format. * Updated DEM_2_6.py test steps in new format and corrected Test description and PICS * Fixed extra " in DEM_2_6 * Reformatted TC_DEM_2_7.py - corrected PICS and description * Corrected Feature 2 for DEM_2_6 * Corrected TC number for DEM_2_6 * Updated TC_DEM_2_8 test steps, description and pics * Restyled by isort * Apply review comments from James Harrow * Apply review comments from James Harrow * Restyled by autopep8 * Restyled by isort * Apply review comments from James Harrow * Fix python lint error * Apply review comments from James Harrow * Restyled by autopep8 * Added # === BEGIN CI TEST ARGUMENTS banners to TC_DEM python scripts * Specify --featureSet option on runner command line * Address review comments from Rob Bultman * Address review comments from Rob Bultman * Restyled by isort * Update src/python_testing/TC_DEM_2_5.py Co-authored-by: Carolina Lopes <116589288+ccruzagralopes@users.noreply.github.com> * Update TC_DEM_2_6.py - changed PICS conformance to comma separated list * Update TC_DEM_2_7.py - changed PICS conformance to comma separated list * Update TC_DEM_2_9.py - changed PICS conformance to comma separated list * Update TC_DEM_2_8.py - changed PICS conformance to comma separated list * Enabled DEM Test scripts as part of CI --------- Co-authored-by: Restyled.io Co-authored-by: James Harrow Co-authored-by: jamesharrow <93921463+jamesharrow@users.noreply.github.com> Co-authored-by: Carolina Lopes <116589288+ccruzagralopes@users.noreply.github.com> --- .github/workflows/tests.yaml | 8 + src/python_testing/TC_DEMTestBase.py | 242 ++++++++++++++++++ src/python_testing/TC_DEM_2_2.py | 11 +- src/python_testing/TC_DEM_2_3.py | 304 ++++++++++++++++++++++ src/python_testing/TC_DEM_2_4.py | 364 +++++++++++++++++++++++++++ src/python_testing/TC_DEM_2_5.py | 306 ++++++++++++++++++++++ src/python_testing/TC_DEM_2_6.py | 290 +++++++++++++++++++++ src/python_testing/TC_DEM_2_7.py | 345 +++++++++++++++++++++++++ src/python_testing/TC_DEM_2_8.py | 307 ++++++++++++++++++++++ src/python_testing/TC_DEM_2_9.py | 120 +++++++++ 10 files changed, 2290 insertions(+), 7 deletions(-) create mode 100644 src/python_testing/TC_DEMTestBase.py create mode 100644 src/python_testing/TC_DEM_2_3.py create mode 100644 src/python_testing/TC_DEM_2_4.py create mode 100644 src/python_testing/TC_DEM_2_5.py create mode 100644 src/python_testing/TC_DEM_2_6.py create mode 100644 src/python_testing/TC_DEM_2_7.py create mode 100644 src/python_testing/TC_DEM_2_8.py create mode 100644 src/python_testing/TC_DEM_2_9.py diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 592227758c035e..d17e5db8a2a541 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -513,6 +513,14 @@ jobs: scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_DRLK_2_3.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_DeviceBasicComposition.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_DeviceConformance.py' + scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_DEM_2_2.py' + scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_DEM_2_3.py' + scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_DEM_2_4.py' + scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_DEM_2_5.py' + scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_DEM_2_6.py' + scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_DEM_2_7.py' + scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_DEM_2_8.py' + scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_DEM_2_9.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_EEM_2_1.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_EEM_2_2.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_EEM_2_3.py' diff --git a/src/python_testing/TC_DEMTestBase.py b/src/python_testing/TC_DEMTestBase.py new file mode 100644 index 00000000000000..db53c36f8cd1c9 --- /dev/null +++ b/src/python_testing/TC_DEMTestBase.py @@ -0,0 +1,242 @@ +# +# 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 utc_time_in_matter_epoch +from mobly import asserts + +logger = logging.getLogger(__name__) + + +class DEMTestBase: + + async def read_dem_attribute_expect_success(self, endpoint: int = None, attribute: str = ""): + cluster = Clusters.Objects.DeviceEnergyManagement + full_attr = getattr(cluster.Attributes, attribute) + logging.info(f"endpoint {endpoint} full_attr {full_attr}") + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=full_attr) + + async def check_dem_attribute(self, attribute, expected_value, endpoint: int = None): + value = await self.read_dem_attribute_expect_success(endpoint=endpoint, attribute=attribute) + asserts.assert_equal(value, expected_value, + f"Unexpected '{attribute}' value - expected {expected_value}, was {value}") + + async def send_power_adjustment_command(self, power: int, duration: int, + cause: Clusters.Objects.DeviceEnergyManagement.Enums.CauseEnum, + endpoint: int = None, timedRequestTimeoutMs: int = 3000, + expected_status: Status = Status.Success): + try: + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.PowerAdjustRequest( + power=power, + duration=duration, + cause=cause), + endpoint=endpoint, + timedRequestTimeoutMs=timedRequestTimeoutMs) + + asserts.assert_equal(expected_status, Status.Success) + + except InteractionModelError as e: + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") + + async def send_cancel_power_adjustment_command(self, endpoint: int = None, timedRequestTimeoutMs: int = 3000, + expected_status: Status = Status.Success): + try: + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.CancelPowerAdjustRequest(), + endpoint=endpoint, + timedRequestTimeoutMs=timedRequestTimeoutMs) + + asserts.assert_equal(expected_status, Status.Success) + + except InteractionModelError as e: + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") + + async def send_start_time_adjust_request_command(self, requestedStartTime: int, + cause: Clusters.Objects.DeviceEnergyManagement.Enums.CauseEnum, + endpoint: int = None, timedRequestTimeoutMs: int = 3000, + expected_status: Status = Status.Success): + try: + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.StartTimeAdjustRequest( + requestedStartTime=requestedStartTime, + cause=cause), + endpoint=endpoint, + timedRequestTimeoutMs=timedRequestTimeoutMs) + + asserts.assert_equal(expected_status, Status.Success) + + except InteractionModelError as e: + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") + + async def send_start_time_adjust_clear_command(self, + endpoint: int = None, timedRequestTimeoutMs: int = 3000, + expected_status: Status = Status.Success): + try: + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.StartTimeAdjustClear(), # StartTimeAdjustmentClear(), + endpoint=endpoint, + timedRequestTimeoutMs=timedRequestTimeoutMs) + + asserts.assert_equal(expected_status, Status.Success) + + except InteractionModelError as e: + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") + + async def send_cancel_request_command(self, + endpoint: int = None, timedRequestTimeoutMs: int = 3000, + expected_status: Status = Status.Success): + try: + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.CancelRequest(), + endpoint=endpoint, + timedRequestTimeoutMs=timedRequestTimeoutMs) + + asserts.assert_equal(expected_status, Status.Success) + + except InteractionModelError as e: + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") + + async def send_pause_request_command(self, duration: int, cause: + Clusters.Objects.DeviceEnergyManagement.Enums.AdjustmentCauseEnum, + endpoint: int = None, timedRequestTimeoutMs: int = 3000, + expected_status: Status = Status.Success): + try: + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.PauseRequest( + duration=duration, + cause=cause), + endpoint=endpoint, + timedRequestTimeoutMs=timedRequestTimeoutMs) + + asserts.assert_equal(expected_status, Status.Success) + + except InteractionModelError as e: + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") + + async def send_resume_request_command(self, endpoint: int = None, timedRequestTimeoutMs: int = 3000, + expected_status: Status = Status.Success): + try: + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.ResumeRequest(), + endpoint=endpoint, + timedRequestTimeoutMs=timedRequestTimeoutMs) + + asserts.assert_equal(expected_status, Status.Success) + + except InteractionModelError as e: + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") + + async def send_modify_forecast_request_command(self, forecastID: int, + slotAdjustments: list[Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct], + cause: Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum, + endpoint: int = None, timedRequestTimeoutMs: int = 3000, + expected_status: Status = Status.Success): + try: + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.ModifyForecastRequest(forecastID=forecastID, + slotAdjustments=slotAdjustments, + cause=cause), + endpoint=endpoint, + timedRequestTimeoutMs=timedRequestTimeoutMs) + + asserts.assert_equal(expected_status, Status.Success) + + except InteractionModelError as e: + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") + + async def send_request_constraint_based_forecast(self, constraintList: list[Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct], + cause: Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum, + endpoint: int = None, timedRequestTimeoutMs: int = 3000, + expected_status: Status = Status.Success): + try: + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.RequestConstraintBasedForecast(constraints=constraintList, + cause=cause), + endpoint=endpoint, + timedRequestTimeoutMs=timedRequestTimeoutMs) + + asserts.assert_equal(expected_status, Status.Success) + + except InteractionModelError as e: + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") + + def print_forecast(self, forecast): + for index, slot in enumerate(forecast.slots): + logging.info( + f" [{index}] MinDuration: {slot.minDuration} MaxDuration: {slot.maxDuration} DefaultDuration: {slot.defaultDuration}") + logging.info(f" ElapseSlotTime: {slot.elapsedSlotTime} RemainingSlotTime: {slot.remainingSlotTime}") + logging.info( + f" SlotIsPausable: {slot.slotIsPausable} MinPauseDuration: {slot.minPauseDuration} MaxPauseDuration: {slot.maxPauseDuration}") + logging.info(f" ManufacturerESAState: {slot.manufacturerESAState}") + logging.info(f" NominalPower: {slot.nominalPower} MinPower: {slot.minPower} MaxPower: {slot.maxPower}") + logging.info(f" MinPowerAdjustment: {slot.minPowerAdjustment} MaxPowerAdjustment: {slot.maxPowerAdjustment}") + logging.info( + f" MinDurationAdjustment: {slot.minDurationAdjustment} MaxDurationAdjustment: {slot.maxDurationAdjustment}") + if slot.costs is not None: + for cost_index, cost in enumerate(slot): + logging.info( + f" Cost: [{cost_index}] CostType:{cost.costType} Value: {cost.value} DecimalPoints: {cost.decimalPoints} Currency: {cost.currency}") + + def get_current_utc_time_in_seconds(self): + microseconds_in_second = 1000000 + return int(utc_time_in_matter_epoch()/microseconds_in_second) + + async def send_test_event_trigger_power_adjustment(self): + await self.send_test_event_triggers(eventTrigger=0x0098000000000000) + + async def send_test_event_trigger_power_adjustment_clear(self): + await self.send_test_event_triggers(eventTrigger=0x0098000000000001) + + async def send_test_event_trigger_user_opt_out_local(self): + await self.send_test_event_triggers(eventTrigger=0x0098000000000002) + + async def send_test_event_trigger_user_opt_out_grid(self): + await self.send_test_event_triggers(eventTrigger=0x0098000000000003) + + async def send_test_event_trigger_user_opt_out_clear_all(self): + await self.send_test_event_triggers(eventTrigger=0x0098000000000004) + + async def send_test_event_trigger_start_time_adjustment(self): + await self.send_test_event_triggers(eventTrigger=0x0098000000000005) + + async def send_test_event_trigger_start_time_adjustment_clear(self): + await self.send_test_event_triggers(eventTrigger=0x0098000000000006) + + async def send_test_event_trigger_pausable(self): + await self.send_test_event_triggers(eventTrigger=0x0098000000000007) + + async def send_test_event_trigger_pausable_next_slot(self): + await self.send_test_event_triggers(eventTrigger=0x0098000000000008) + + async def send_test_event_trigger_pausable_clear(self): + await self.send_test_event_triggers(eventTrigger=0x0098000000000009) + + async def send_test_event_trigger_forecast_adjustment(self): + await self.send_test_event_triggers(eventTrigger=0x009800000000000A) + + async def send_test_event_trigger_forecast_adjustment_next_slot(self): + await self.send_test_event_triggers(eventTrigger=0x009800000000000B) + + async def send_test_event_trigger_forecast_adjustment_clear(self): + await self.send_test_event_triggers(eventTrigger=0x009800000000000C) + + async def send_test_event_trigger_constraint_based_adjustment(self): + await self.send_test_event_triggers(eventTrigger=0x009800000000000D) + + async def send_test_event_trigger_constraint_based_adjustment_clear(self): + await self.send_test_event_triggers(eventTrigger=0x009800000000000E) + + async def send_test_event_trigger_forecast(self): + await self.send_test_event_triggers(eventTrigger=0x009800000000000F) + + async def send_test_event_trigger_forecast_clear(self): + await self.send_test_event_triggers(eventTrigger=0x0098000000000010) diff --git a/src/python_testing/TC_DEM_2_2.py b/src/python_testing/TC_DEM_2_2.py index dc81cbcc0aa9d2..0c32992d934174 100644 --- a/src/python_testing/TC_DEM_2_2.py +++ b/src/python_testing/TC_DEM_2_2.py @@ -196,7 +196,7 @@ async def test_TC_DEM_2_2(self): await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kNoOptOut) self.step("4") - await self.send_power_adjustment_command(power=max_power, + await self.send_power_adjustment_command(power=powerAdjustCapabilityStruct.powerAdjustCapability[0].maxPower, duration=powerAdjustCapabilityStruct.powerAdjustCapability[0].minDuration, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization) @@ -218,7 +218,6 @@ async def test_TC_DEM_2_2(self): self.step("5a") powerAdjustCapabilityStruct = await self.read_dem_attribute_expect_success(attribute="PowerAdjustmentCapability") - asserts.assert_greater_equal(len(powerAdjustCapabilityStruct.powerAdjustCapability), 1) asserts.assert_equal(powerAdjustCapabilityStruct.cause, Clusters.DeviceEnergyManagement.Enums.PowerAdjustReasonEnum.kNoAdjustment) @@ -254,7 +253,7 @@ async def test_TC_DEM_2_2(self): self.step("11") start = datetime.datetime.now() - await self.send_power_adjustment_command(power=powerAdjustCapabilityStruct.powerAdjustCapability[0].maxPower, + await self.send_power_adjustment_command(power=powerAdjustCapabilityStruct.powerAdjustCapability[0].minPower, duration=min_duration, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization) @@ -262,13 +261,12 @@ async def test_TC_DEM_2_2(self): self.step("11a") powerAdjustCapabilityStruct = await self.read_dem_attribute_expect_success(attribute="PowerAdjustmentCapability") - asserts.assert_greater_equal(len(powerAdjustCapabilityStruct.powerAdjustCapability), 1) asserts.assert_equal(powerAdjustCapabilityStruct.cause, Clusters.DeviceEnergyManagement.Enums.PowerAdjustReasonEnum.kLocalOptimizationAdjustment) self.step("12") await self.send_power_adjustment_command(power=powerAdjustCapabilityStruct.powerAdjustCapability[0].maxPower, - duration=min_duration, + duration=powerAdjustCapabilityStruct.powerAdjustCapability[0].minDuration, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization) # Wait 5 seconds for an event not to be reported @@ -279,7 +277,6 @@ async def test_TC_DEM_2_2(self): self.step("12b") powerAdjustCapabilityStruct = await self.read_dem_attribute_expect_success(attribute="PowerAdjustmentCapability") - asserts.assert_greater_equal(len(powerAdjustCapabilityStruct.powerAdjustCapability), 1) asserts.assert_equal(powerAdjustCapabilityStruct.cause, Clusters.DeviceEnergyManagement.Enums.PowerAdjustReasonEnum.kGridOptimizationAdjustment) @@ -294,7 +291,7 @@ async def test_TC_DEM_2_2(self): self.step("14") await self.send_power_adjustment_command(power=max_power, - duration=max_duration, + duration=powerAdjustCapabilityStruct.powerAdjustCapability[0].maxDuration, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) diff --git a/src/python_testing/TC_DEM_2_3.py b/src/python_testing/TC_DEM_2_3.py new file mode 100644 index 00000000000000..6bdd81c710a914 --- /dev/null +++ b/src/python_testing/TC_DEM_2_3.py @@ -0,0 +1,304 @@ +# +# 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. + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${ENERGY_MANAGEMENT_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json --enable-key 000102030405060708090a0b0c0d0e0f --featureSet 0x7a +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --hex-arg enableKey:000102030405060708090a0b0c0d0e0f --endpoint 1 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# === END CI TEST ARGUMENTS === + +import logging + +import chip.clusters as Clusters +from chip.clusters.Types import NullValue +from chip.interaction_model import Status +from matter_testing_support import EventChangeCallback, MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts +from TC_DEMTestBase import DEMTestBase + +logger = logging.getLogger(__name__) + + +class TC_DEM_2_3(MatterBaseTest, DEMTestBase): + """Implementation of test case TC_DEM_2_3.""" + + def desc_TC_DEM_2_3(self) -> str: + """Returns a description of this test""" + return "4.1.3. [TC-DEM-2.3] Start Time Adjustment feature functionality with DUT as Server" + + def pics_TC_DEM_2_3(self): + """Return the PICS definitions associated with this test.""" + pics = [ + "DEM.S.F03", # Depends on F03(StartTimeAdjustment) + ] + return pics + + def steps_TC_DEM_2_3(self) -> list[TestStep]: + steps = [ + TestStep("1", "Commissioning, already done", + is_commissioning=True), + TestStep("2", "TH reads TestEventTriggersEnabled attribute from General Diagnostics Cluster.", + "Verify that TestEventTriggersEnabled attribute has a value of 1 (True)"), + TestStep("3", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Start Time Adjustment Test Event", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("3a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("3b", "TH reads Forecast attribute.", + "Value has to include EarliestStartTime<=StartTime, LatestEndTime>=EndTime, and ForecastUpdateReason=Internal Optimization"), + TestStep("3c", "TH reads OptOutState attribute.", + "Verify value is 0x00 (NoOptOut)"), + TestStep("4", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Local Optimization Test Event", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("4a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("4b", "TH reads OptOutState attribute.", + "Verify value is 0x01 (LocalOptOut)"), + TestStep("5", "TH sends StartTimeAdjustRequest with RequestedStartTime=EarliestStartTime from Forecast, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("5a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("5b", "TH reads Forecast attribute.", + "Value has to be unchanged from step 3b"), + TestStep("6", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Test Event Clear", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("6a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("6b", "TH reads OptOutState attribute.", + "Verify value is 0x00 (NoOptOut)"), + TestStep("7", "TH sends StartTimeAdjustRequest with RequestedStartTime=EarliestStartTime from Forecast, Cause=LocalOptimization.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("7a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("7b", "TH reads Forecast attribute.", + "Value has to include EarliestStartTime=StartTime, LatestEndTime>=EndTime, and ForecastUpdateReason=Local Optimization"), + TestStep("8", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Local Optimization Test Event", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("8a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("8b", "TH reads OptOutState attribute.", + "Verify value is 0x01 (LocalOptOut)"), + TestStep("8c", "TH reads Forecast attribute.", + "Value has to include EarliestStartTime<=StartTime, LatestEndTime>=EndTime, and ForecastUpdateReason=Internal Optimization"), + TestStep("9", "TH sends StartTimeAdjustRequest with RequestedStartTime=StartTime+(LatestEndTime-EndTime) from Forecast, Cause=GridOptimization.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("9a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("9b", "TH reads Forecast attribute.", + "Value has to include EarliestStartTime<=StartTime, LatestEndTime=EndTime, and ForecastUpdateReason=Grid Optimization"), + TestStep("10", "TH sends CancelRequest.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("10a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("10b", "TH reads Forecast attribute.", + "Value has to include EarliestStartTime<=StartTime, LatestEndTime>=EndTime, and ForecastUpdateReason=Internal Optimization"), + TestStep("11", "TH sends StartTimeAdjustRequest with RequestedStartTime=EarliestStartTime-1 from Forecast, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("11a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("11b", "TH reads Forecast attribute.", + "Value has to include StartTime and EndTime unchanged from step 10b"), + TestStep("12", "TH sends StartTimeAdjustRequest with RequestedStartTime=StartTime+(LatestEndTime-EndTime)+1 from Forecast, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("12a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("12b", "TH reads Forecast attribute.", + "Value has to include StartTime and EndTime unchanged from step 10b"), + TestStep("13", "TH sends CancelRequest.", + "Verify DUT responds with status INVALID_IN_STATE(0xcb)"), + TestStep("14", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Start Time Adjustment Test Event Clear", + "Verify DUT responds with status SUCCESS(0x00)"), + ] + + return steps + + @async_test_body + async def test_TC_DEM_2_3(self): + + logging.info(Clusters.Objects.DeviceEnergyManagement.Attributes.FeatureMap) + + self.step("1") + # Commission DUT - already done + + # Subscribe to Events and when they are sent push them to a queue for checking later + events_callback = EventChangeCallback(Clusters.DeviceEnergyManagement) + await events_callback.start(self.default_controller, + self.dut_node_id, + self.matter_test_config.endpoint) + + self.step("2") + await self.check_test_event_triggers_enabled() + + self.step("3") + await self.send_test_event_trigger_start_time_adjustment() + + self.step("3a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("3b") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + + asserts.assert_not_equal(forecast, NullValue) + asserts.assert_less_equal(forecast.earliestStartTime, forecast.startTime, + f"Expected forecast earliestStartTime {forecast.earliestStartTime} to be <= startTime {forecast.startTime}") + asserts.assert_greater_equal(forecast.latestEndTime, forecast.endTime, + f"Expected forecast latestEndTime {forecast.latestEndTime} to be >= endTime {forecast.endTime}") + asserts.assert_equal(forecast.forecastUpdateReason, Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization, + f"Expected forecast forecastUpdateReason {forecast.forecastUpdateReason} to be == Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization") + + self.print_forecast(forecast) + + self.step("3c") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kNoOptOut) + + self.step("4") + await self.send_test_event_trigger_user_opt_out_local() + + self.step("4a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("4b") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kLocalOptOut) + + self.step("5") + await self.send_start_time_adjust_request_command(requestedStartTime=forecast.earliestStartTime, + cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, + expected_status=Status.ConstraintError) + + self.step("5a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("5b") + forecast2 = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast, forecast2, + f"Expected same forcast {forecast} to be == {forecast2}") + + self.step("6") + await self.send_test_event_trigger_user_opt_out_clear_all() + + self.step("6a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("6b") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kNoOptOut) + + self.step("7") + await self.send_start_time_adjust_request_command(requestedStartTime=forecast.earliestStartTime, + cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization) + + self.step("7a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("7b") + forecast3 = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast3.earliestStartTime, forecast3.startTime, + f"Expected earliestStartTime {forecast3.earliestStartTime} to be == startTime {forecast3.startTime}") + asserts.assert_greater_equal(forecast3.latestEndTime, forecast3.endTime, + f"Expected latestEndTime {forecast3.latestEndTime} to be >= endTime {forecast3.endTime}") + asserts.assert_equal(forecast3.forecastUpdateReason, Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kLocalOptimization, + f"Expected forecastUpdateReason {forecast3.forecastUpdateReason} to be == LocalOptimization {Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kLocalOptimization}") + + self.step("8") + await self.send_test_event_trigger_user_opt_out_local() + + self.step("8a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("8b") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kLocalOptOut) + + self.step("8c") + forecast4 = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_less_equal(forecast4.earliestStartTime, forecast4.startTime, + f"Expected earliestStartTime {forecast4.earliestStartTime} to be <= startTime {forecast4.startTime}") + asserts.assert_greater_equal(forecast4.latestEndTime, forecast4.endTime, + f"Expected forecast latestEndTime {forecast4.latestEndTime} to be >= endTime {forecast4.endTime}") + asserts.assert_equal(forecast4.forecastUpdateReason, Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization, + f"Expected forecastUpdateReason {forecast4.forecastUpdateReason} to be == InternalOptimization {Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization}") + + self.step("9") + await self.send_start_time_adjust_request_command(requestedStartTime=forecast4.startTime+forecast4.latestEndTime - forecast4.endTime, + cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization) + + self.step("9a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("9b") + forecast5 = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_less_equal(forecast5.earliestStartTime, forecast5.startTime, + f"Expected earliestStartTime {forecast5.earliestStartTime} to be <= startTime {forecast5.startTime}") + asserts.assert_equal(forecast5.latestEndTime, forecast5.endTime, + f"Expected latestEndTime {forecast5.latestEndTime} to be == endTime {forecast5.endTime}") + asserts.assert_equal(forecast5.forecastUpdateReason, Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kGridOptimization, + f"Expected forecastUpdateReason {forecast5.forecastUpdateReason} to be == GridOptimization {Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kGridOptimization}") + + self.step("10") + await self.send_cancel_request_command() + + self.step("10a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("10b") + forecast6 = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_less_equal(forecast6.earliestStartTime, forecast6.startTime, + f"Expected earliestStartTime {forecast6.earliestStartTime} to be <= startTime {forecast6.startTime}") + asserts.assert_greater_equal(forecast6.latestEndTime, forecast6.endTime, + f"Expected latestEndTime {forecast6.latestEndTime} to be >= endTime {forecast6.endTime}") + asserts.assert_equal(forecast6.forecastUpdateReason, Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization, + f"Expected forecastUpdateReason {forecast6.forecastUpdateReason} to be == InternalOptimization {Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization}") + + self.step("11") + await self.send_start_time_adjust_request_command(requestedStartTime=forecast6.earliestStartTime - 1, + cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, + expected_status=Status.ConstraintError) + self.step("11a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("11b") + forecast7 = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast6.startTime, forecast7.startTime, + f"Expected old startTime {forecast6.startTime} to be == startTime {forecast7.startTime}") + asserts.assert_equal(forecast6.endTime, forecast7.endTime, + f"Expected old endTime {forecast6.endTime} to be == endTime {forecast7.endTime}") + + self.step("12") + await self.send_start_time_adjust_request_command(requestedStartTime=forecast7.startTime+(forecast7.latestEndTime-forecast7.endTime)+1, + cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, + expected_status=Status.ConstraintError) + self.step("12a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("12b") + forecast8 = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast7.startTime, forecast8.startTime, + f"Expected old startTime {forecast7.startTime} to be == startTime {forecast8.startTime}") + asserts.assert_equal(forecast7.endTime, forecast8.endTime, + f"Expected old endTime {forecast7.endTime} to be == endTime {forecast8.endTime}") + + self.step("13") + await self.send_cancel_request_command(expected_status=Status.InvalidInState) + + self.step("14") + await self.send_test_event_trigger_start_time_adjustment_clear() + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_DEM_2_4.py b/src/python_testing/TC_DEM_2_4.py new file mode 100644 index 00000000000000..390889fc9f2d28 --- /dev/null +++ b/src/python_testing/TC_DEM_2_4.py @@ -0,0 +1,364 @@ +# +# 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. + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${ENERGY_MANAGEMENT_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json --enable-key 000102030405060708090a0b0c0d0e0f --featureSet 0x7a +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --hex-arg enableKey:000102030405060708090a0b0c0d0e0f --endpoint 1 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# === END CI TEST ARGUMENTS === + +import logging +import time + +import chip.clusters as Clusters +from chip.clusters.Types import NullValue +from chip.interaction_model import Status +from matter_testing_support import EventChangeCallback, MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts +from TC_DEMTestBase import DEMTestBase + +logger = logging.getLogger(__name__) + + +class TC_DEM_2_4(MatterBaseTest, DEMTestBase): + """Implementation of test case TC_DEM_2_4.""" + + def desc_TC_DEM_2_4(self) -> str: + """Returns a description of this test""" + return "4.1.3. [TC-DEM-2.4] Pausable feature functionality with DUT as Server" + + def pics_TC_DEM_2_4(self): + """Return the PICS definitions associated with this test.""" + pics = [ + "DEM.S.F04", # Depends on F04(Pausable) + ] + return pics + + def steps_TC_DEM_2_4(self) -> list[TestStep]: + steps = [ + TestStep("1", "Commissioning, already done", + is_commissioning=True), + TestStep("2", "TH reads TestEventTriggersEnabled attribute from General Diagnostics Cluster.", + "Verify that TestEventTriggersEnabled attribute has a value of 1 (True)"), + TestStep("3", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Pausable Test Event", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("3a", "TH reads ESAState.", + "Verify value is 0x01 (Online)"), + TestStep("3b", "TH reads Forecast.", + "Value has to include IsPausable=True, slot[0].SlotIsPausable=True, slot[0].MinPauseDuration>1, slot[0].MaxPauseDuration>1, slot[1].SlotIsPausable=False, ActiveSlotNumber=0, and ForecastUpdateReason=Internal Optimization"), + TestStep("3c", "TH reads OptOutState.", + "Verify value is 0x00 (NoOptOut)"), + TestStep("4", "TH sends PauseRequest with Duration=Forecast.slots[0].MinPauseDuration-1, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("4a", "TH reads ESAState.", + "Verify value is 0x01 (Online)"), + TestStep("5", "TH sends PauseRequest with Duration=Forecast.slots[0].MaxPauseDuration+1, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("5a", "TH reads ESAState.", + "Verify value is 0x01 (Online)"), + TestStep("6", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Grid Optimization Test Event", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("6a", "TH reads ESAState.", + "Verify value is 0x01 (Online)"), + TestStep("6b", "TH reads OptOutState.", + "Verify value is 0x02 (GridOptOut)"), + TestStep("7", "TH sends PauseRequest with Duration=Forecast.slots[0].MinPauseDuration, Cause=GridOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("7a", "TH reads ESAState.", + "Verify value is 0x01 (Online)"), + TestStep("8", "TH sends PauseRequest with Duration=Forecast.slots[0].MinPauseDuration, Cause=LocalOptimization.", + "Verify DUT responds with status SUCCESS(0x00) and event DEM.S.E02(Paused) sent"), + TestStep("8a", "TH reads ESAState.", + "Verify value is 0x05 (Paused)"), + TestStep("9", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Local Optimization Test Event.", + "Verify DUT responds with status SUCCESS(0x00) and event DEM.S.E03(Resumed) sent with Cause=3 (UserOptOut)"), + TestStep("9a", "TH reads ESAState.", + "Verify value is 0x01 (Online)"), + TestStep("9b", "TH reads OptOutState.", + "Verify value is 0x03 (OptOut)"), + TestStep("9c", "TH reads Forecast.", + "Value has to include ForecastUpdateReason=Internal Optimization"), + TestStep("10", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Test Event Clear", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("10a", "TH reads ESAState.", + "Verify value is 0x01 (Online)"), + TestStep("10b", "TH reads OptOutState.", + "Verify value is 0x00 (NoOptOut)"), + TestStep("11", "TH sends PauseRequest with Duration=Forecast.slots[0].MinPauseDuration, Cause=LocalOptimization.", + "Verify DUT responds with status SUCCESS(0x00) and event DEM.S.E02(Paused) sent"), + TestStep("11a", "TH reads ESAState.", + "Verify value is 0x05 (Paused)"), + TestStep("11b", "TH reads Forecast.", + "Value has to include ForecastUpdateReason=Local Optimization"), + TestStep("12", "TH sends ResumeRequest.", + "Verify DUT responds with status SUCCESS(0x00) and event DEM.S.E03(Resumed) sent with Cause=4 (Cancelled)"), + TestStep("12a", "TH reads ESAState.", + "Verify value is 0x01 (Online)"), + TestStep("12b", "TH reads Forecast.", + "Value has to include IsPausable=True, slots[0].SlotIsPausable=True, slots[0].MinPauseDuration>1, slots[0].MaxPauseDuration>1, slots[1].SlotIsPausable=False, ActiveSlotNumber=0, ForecastUpdateReason=Internal Optimization"), + TestStep("13", "TH sends PauseRequest with Duration=Forecast.slots[0].MinPauseDuration, Cause=LocalOptimization.", + "Verify DUT responds with status SUCCESS(0x00) and event DEM.S.E02(Paused) sent"), + TestStep("13a", "TH reads ESAState.", + "Verify value is 0x05 (Paused)"), + TestStep("13b", "TH reads Forecast.", + "Value has to include ForecastUpdateReason=Local Optimization"), + TestStep("14", "TH sends ResumeRequest.", + "Verify DUT responds with status SUCCESS(0x00) and event DEM.S.E03(Resumed) sent with Cause=4 (Cancelled)"), + TestStep("14a", "TH reads ESAState.", + "Verify value is 0x01 (Online)"), + TestStep("15", "TH sends PauseRequest with Duration=Forecast.slots[0].MinPauseDuration, Cause=LocalOptimization.", + "Verify DUT responds with status SUCCESS(0x00) and event DEM.S.E02(Paused) sent"), + TestStep("15a", "TH reads ESAState.", + "Verify value is 0x05 (Paused)"), + TestStep("16", "Wait for minPauseDuration.", + "Event DEM.S.E03(Resumed) sent with Cause=0 (NormalCompletion)"), + TestStep("16a", "TH reads ESAState.", + "Verify value is 0x01 (Online)"), + TestStep("17", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Pausable Test Event Next Slot.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("17a", "TH reads ESAState.", + "Verify value is 0x01 (Online)"), + TestStep("17b", "TH reads Forecast.", + "Value has to include ActiveSlotNumber=1"), + TestStep("18", "TH sends PauseRequest with Duration=Forecast.slots[0].MinPauseDuration, Cause=LocalOptimization.", + "Verify DUT responds with status FAILURE(0x01)"), + TestStep("18a", "TH reads ESAState.", + "Verify value is 0x01 (Online)"), + TestStep("19", "TH sends ResumeRequest.", + "Verify DUT responds with status INVALID_IN_STATE(0xcb)"), + TestStep("20", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Pausable Test Event Clear.", + "Verify DUT responds with status SUCCESS(0x00)"), + ] + + return steps + + @async_test_body + async def test_TC_DEM_2_4(self): + + logging.info(Clusters.Objects.DeviceEnergyManagement.Attributes.FeatureMap) + + self.step("1") + # Commission DUT - already done + + # Subscribe to Events and when they are sent push them to a queue for checking later + events_callback = EventChangeCallback(Clusters.DeviceEnergyManagement) + await events_callback.start(self.default_controller, + self.dut_node_id, + self.matter_test_config.endpoint) + + self.step("2") + await self.check_test_event_triggers_enabled() + + self.step("3") + await self.send_test_event_trigger_pausable() + + self.step("3a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("3b") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + + asserts.assert_not_equal(forecast, NullValue) + asserts.assert_equal(forecast.isPausable, True) + asserts.assert_greater(forecast.slots[0].minPauseDuration, 1) + asserts.assert_greater(forecast.slots[0].maxPauseDuration, 1) + asserts.assert_equal(forecast.slots[0].slotIsPausable, True) + asserts.assert_equal(forecast.slots[1].slotIsPausable, False) + asserts.assert_equal(forecast.activeSlotNumber, 0) + + asserts.assert_less_equal(forecast.earliestStartTime, forecast.startTime, + f"Expected forecast earliestStartTime {forecast.earliestStartTime} to be <= startTime {forecast.startTime}") + asserts.assert_greater_equal(forecast.latestEndTime, forecast.endTime, + f"Expected forecast latestEndTime {forecast.latestEndTime} to be >= endTime {forecast.endTime}") + asserts.assert_equal(forecast.forecastUpdateReason, Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization, + f"Expected forecast forecastUpdateReason {forecast.forecastUpdateReason} to be == Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization") + self.print_forecast(forecast) + + self.step("3c") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kNoOptOut) + + self.step("4") + await self.send_pause_request_command(forecast.slots[0].minPauseDuration - 1, + Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, + expected_status=Status.ConstraintError) + + self.step("4a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("5") + await self.send_pause_request_command(forecast.slots[0].maxPauseDuration + 1, + Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, + expected_status=Status.ConstraintError) + + self.step("5a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("6") + await self.send_test_event_trigger_user_opt_out_grid() + + self.step("6a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("6b") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kGridOptOut) + + self.step("7") + await self.send_pause_request_command(forecast.slots[0].minPauseDuration, + Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, + expected_status=Status.ConstraintError) + + self.step("7a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("8") + await self.send_pause_request_command(forecast.slots[0].minPauseDuration, + Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization) + + event_data = events_callback.wait_for_event_report(Clusters.DeviceEnergyManagement.Events.Paused) + + self.step("8a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kPaused) + + self.step("9") + await self.send_test_event_trigger_user_opt_out_local() + event_data = events_callback.wait_for_event_report(Clusters.DeviceEnergyManagement.Events.Resumed) + asserts.assert_equal(event_data.cause, Clusters.DeviceEnergyManagement.Enums.CauseEnum.kUserOptOut) + + self.step("9a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("9b") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kOptOut) + + self.step("9c") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + + asserts.assert_not_equal(forecast, NullValue) + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization) + + self.step("10") + await self.send_test_event_trigger_user_opt_out_clear_all() + + self.step("10a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("10b") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kNoOptOut) + + self.step("11") + await self.send_pause_request_command(forecast.slots[0].minPauseDuration, + Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization) + event_data = events_callback.wait_for_event_report(Clusters.DeviceEnergyManagement.Events.Paused) + + self.step("11a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kPaused) + + self.step("11b") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kLocalOptimization) + + self.step("12") + await self.send_resume_request_command() + event_data = events_callback.wait_for_event_report(Clusters.DeviceEnergyManagement.Events.Resumed) + asserts.assert_equal(event_data.cause, Clusters.DeviceEnergyManagement.Enums.CauseEnum.kCancelled) + + self.step("12a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("12b") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.isPausable, True) + asserts.assert_greater(forecast.slots[0].minPauseDuration, 1) + asserts.assert_greater(forecast.slots[0].maxPauseDuration, 1) + asserts.assert_equal(forecast.slots[0].slotIsPausable, True) + asserts.assert_equal(forecast.slots[1].slotIsPausable, False) + asserts.assert_equal(forecast.activeSlotNumber, 0) + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization) + + self.step("13") + await self.send_pause_request_command(forecast.slots[0].minPauseDuration, + Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization) + event_data = events_callback.wait_for_event_report(Clusters.DeviceEnergyManagement.Events.Paused) + + self.step("13a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kPaused) + + self.step("13b") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kLocalOptimization) + + self.step("14") + await self.send_resume_request_command() + event_data = events_callback.wait_for_event_report(Clusters.DeviceEnergyManagement.Events.Resumed) + asserts.assert_equal(event_data.cause, Clusters.DeviceEnergyManagement.Enums.CauseEnum.kCancelled) + + self.step("14a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("15") + await self.send_pause_request_command(forecast.slots[0].minPauseDuration, + Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization) + event_data = events_callback.wait_for_event_report(Clusters.DeviceEnergyManagement.Events.Paused) + + self.step("15a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kPaused) + + self.step("16") + logging.info(f"Sleeping for forecast.slots[0].minPauseDuration {forecast.slots[0].minPauseDuration}s") + time.sleep(forecast.slots[0].minPauseDuration) + event_data = events_callback.wait_for_event_report(Clusters.DeviceEnergyManagement.Events.Resumed) + asserts.assert_equal(event_data.cause, Clusters.DeviceEnergyManagement.Enums.CauseEnum.kNormalCompletion) + + self.step("16a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("17") + await self.send_test_event_trigger_pausable_next_slot() + + self.step("17a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("17b") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.activeSlotNumber, 1) + + self.step("18") + await self.send_pause_request_command(forecast.slots[0].minPauseDuration, + Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, + expected_status=Status.Failure) + + self.step("18a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("19") + await self.send_resume_request_command(expected_status=Status.InvalidInState) + + self.step("20") + await self.send_test_event_trigger_user_opt_out_clear_all() + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_DEM_2_5.py b/src/python_testing/TC_DEM_2_5.py new file mode 100644 index 00000000000000..85d3cd779b22f4 --- /dev/null +++ b/src/python_testing/TC_DEM_2_5.py @@ -0,0 +1,306 @@ +# +# 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. +# pylint: disable=invalid-name + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${ENERGY_MANAGEMENT_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json --enable-key 000102030405060708090a0b0c0d0e0f --featureSet 0x7a +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --hex-arg enableKey:000102030405060708090a0b0c0d0e0f --endpoint 1 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# === END CI TEST ARGUMENTS === + +"""Define Matter test case TC_DEM_2_5.""" + + +import logging + +import chip.clusters as Clusters +from chip.interaction_model import Status +from matter_testing_support import EventChangeCallback, MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts +from TC_DEMTestBase import DEMTestBase + +logger = logging.getLogger(__name__) + + +class TC_DEM_2_5(MatterBaseTest, DEMTestBase): + """Implementation of test case TC_DEM_2_5.""" + + def desc_TC_DEM_2_5(self) -> str: + """Return a description of this test.""" + return "4.1.3. [TC-DEM-2.5] Forecast Adjustment with Power Forecast Reporting feature functionality with DUT as Server" + + def pics_TC_DEM_2_5(self): + """Return the PICS definitions associated with this test.""" + pics = [ + # Depends on Feature 05 (ForecastAdjustment) & Feature 01 (PowerForecastReporting) + "DEM.S.F05", "DEM.S.F01" + ] + return pics + + def steps_TC_DEM_2_5(self) -> list[TestStep]: + """Execute the test steps.""" + steps = [ + TestStep("1", "Commissioning, already done", + is_commissioning=True), + TestStep("2", "TH reads TestEventTriggersEnabled attribute from General Diagnostics Cluster.", + "Verify that TestEventTriggersEnabled attribute has a value of 1 (True)"), + TestStep("3", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Forecast Adjustment Test Event", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("3a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("3b", "TH reads Forecast attribute.", + "Value has to include slots[0].MinPowerAdjustment, slots[0].MaxPowerAdjustment, slots[0].MinDurationAdjustment, slots[0].MaxDurationAdjustment"), + TestStep("3c", "TH reads OptOutState attribute.", + "Verify value is 0x00 (NoOptOut)"), + TestStep("4", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID+1, SlotAdjustments[0].{SlotIndex=0, NominalPower=Forecast.Slots[0].MinPowerAdjustment, Duration=Forecast.Slots[0].MaxDurationAdjustment}, Cause=GridOptimization.", + "Verify DUT responds with status FAILURE(0x01)"), + TestStep("5", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=4, NominalPower=Forecast.Slots[0].MinPowerAdjustment, Duration=Forecast.Slots[0].MaxDurationAdjustment}, Cause=GridOptimization.", + "Verify DUT responds with status FAILURE(0x01)"), + TestStep("6", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, NominalPower=Forecast.Slots[0].MinPowerAdjustment-1, Duration=Forecast.Slots[0].MaxDurationAdjustment}, Cause=GridOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("7", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, NominalPower=Forecast.Slots[0].MaxPowerAdjustment+1, Duration=Forecast.Slots[0].MinDurationAdjustment}, Cause=GridOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("8", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, NominalPower=Forecast.Slots[0].MinPowerAdjustment, Duration=Forecast.Slots[0].MaxDurationAdjustment+1}, Cause=GridOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("9", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, NominalPower=Forecast.Slots[0].MaxPowerAdjustment, Duration=Forecast.Slots[0].MinDurationAdjustment-1}, Cause=GridOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("10", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, NominalPower=Forecast.Slots[0].MinPowerAdjustment, Duration=Forecast.Slots[0].MaxDurationAdjustment}, SlotAdjustments[1].{SlotIndex=4, NominalPower=Forecast.Slots[0].MinPowerAdjustment, Duration=Forecast.Slots[0].MaxDurationAdjustment}, Cause=GridOptimization.", + "Verify DUT responds with status FAILURE(0x01)"), + TestStep("11", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Local Optimization Test Event", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("11a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("11b", "TH reads OptOutState attribute.", + "Verify value is 0x02 (LocalOptOut)"), + TestStep("12", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, NominalPower=Forecast.Slots[0].MinPowerAdjustment, Duration=Forecast.Slots[0].MaxDurationAdjustment}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("13", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, NominalPower=Forecast.Slots[0].MinPowerAdjustment, Duration=Forecast.Slots[0].MaxDurationAdjustment}, Cause=GridOptimization.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("13a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=GridOptimization"), + TestStep("14", "TH sends CancelRequest.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("14a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=InternalOptimization"), + TestStep("15", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, NominalPower=Forecast.Slots[0].MinPowerAdjustment, Duration=Forecast.Slots[0].MaxDurationAdjustment}, Cause=GridOptimization.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("15a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=GridOptimization"), + TestStep("16", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Grid Optimization Test Event", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("16a", "TH reads OptOutState attribute.", + "Verify value is 0x03 (OptOut)"), + TestStep("16b", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=Internal Optimization"), + TestStep("17", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, NominalPower=Forecast.Slots[0].MinPowerAdjustment, Duration=Forecast.Slots[0].MaxDurationAdjustment}, Cause=GridOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("18", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Test Event Clear", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("18a", "TH reads OptOutState attribute.", + "Verify value is 0x00 (NoOptOut)"), + TestStep("19", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, NominalPower=Forecast.Slots[0].MaxPowerAdjustment, Duration=Forecast.Slots[0].MinDurationAdjustment}, Cause=LocalOptimization.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("19a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=LocalOptimization"), + TestStep("20", "TH sends CancelRequest.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("20a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=InternalOptimization"), + TestStep("21", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Forecast Adjustment Test Event Next Slot", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("22", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, NominalPower=Forecast.Slots[0].MaxPowerAdjustment, Duration=Forecast.Slots[0].MinDurationAdjustment}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("23", "TH sends CancelRequest.", + "Verify DUT responds with status INVALID_IN_STATE(0xcb)"), + TestStep("24", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Forecast Adjustment Test Event Clear", + "Verify DUT responds with status SUCCESS(0x00)"), + ] + + return steps + + @async_test_body + async def test_TC_DEM_2_5(self): + # pylint: disable=too-many-locals, too-many-statements + """Run the test steps.""" + self.step("1") + # Commission DUT - already done + + # Subscribe to Events and when they are sent push them to a queue for checking later + events_callback = EventChangeCallback(Clusters.DeviceEnergyManagement) + await events_callback.start(self.default_controller, + self.dut_node_id, + self.matter_test_config.endpoint) + + self.step("2") + await self.check_test_event_triggers_enabled() + + self.step("3") + await self.send_test_event_trigger_forecast_adjustment() + + self.step("3a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("3b") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + + asserts.assert_is_not_none(forecast.slots[0].minPowerAdjustment) + asserts.assert_is_not_none(forecast.slots[0].maxPowerAdjustment) + asserts.assert_is_not_none(forecast.slots[0].minDurationAdjustment) + asserts.assert_is_not_none(forecast.slots[0].maxDurationAdjustment) + + self.step("3c") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kNoOptOut) + + self.step("4") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, nominalPower=forecast.slots[0].minPowerAdjustment, duration=forecast.slots[0].maxDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID + 1, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.Failure) + + self.step("5") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=4, nominalPower=forecast.slots[0].minPowerAdjustment, duration=forecast.slots[0].maxDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.Failure) + + self.step("6") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, nominalPower=forecast.slots[0].minPowerAdjustment - 1, duration=forecast.slots[0].maxDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.ConstraintError) + + self.step("7") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, nominalPower=forecast.slots[0].maxPowerAdjustment + 1, duration=forecast.slots[0].minDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.ConstraintError) + + self.step("8") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, nominalPower=forecast.slots[0].minPowerAdjustment, duration=forecast.slots[0].maxDurationAdjustment + 1)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.ConstraintError) + + self.step("9") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, nominalPower=forecast.slots[0].maxPowerAdjustment, duration=forecast.slots[0].minDurationAdjustment - 1)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.ConstraintError) + + self.step("10") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct(slotIndex=0, nominalPower=forecast.slots[0].minPowerAdjustment, duration=forecast.slots[0].maxDurationAdjustment), + Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct(slotIndex=4, nominalPower=forecast.slots[0].minPowerAdjustment, duration=forecast.slots[0].maxDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.Failure) + + self.step("11") + await self.send_test_event_trigger_user_opt_out_local() + + self.step("11a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("11b") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kLocalOptOut) + + self.step("12") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, nominalPower=forecast.slots[0].minPowerAdjustment, duration=forecast.slots[0].maxDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("13") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, nominalPower=forecast.slots[0].minPowerAdjustment, duration=forecast.slots[0].maxDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.Success) + + self.step("13a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kGridOptimization) + + self.step("14") + await self.send_cancel_request_command() + + self.step("14a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization) + + self.step("15") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, nominalPower=forecast.slots[0].minPowerAdjustment, duration=forecast.slots[0].maxDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.Success) + + self.step("15a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kGridOptimization) + + self.step("16") + await self.send_test_event_trigger_user_opt_out_grid() + + self.step("16a") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kOptOut) + + self.step("16b") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization) + + self.step("17") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, nominalPower=forecast.slots[0].minPowerAdjustment, duration=forecast.slots[0].maxDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.ConstraintError) + + self.step("18") + await self.send_test_event_trigger_user_opt_out_clear_all() + + self.step("18a") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kNoOptOut) + + self.step("19") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, nominalPower=forecast.slots[0].minPowerAdjustment, duration=forecast.slots[0].minDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.Success) + + self.step("19a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kLocalOptimization) + + self.step("20") + await self.send_cancel_request_command() + + self.step("20a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization) + + self.step("21") + await self.send_test_event_trigger_forecast_adjustment_next_slot() + + self.step("22") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, nominalPower=forecast.slots[0].minPowerAdjustment, duration=forecast.slots[0].minDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("23") + await self.send_cancel_request_command(expected_status=Status.InvalidInState) + + self.step("24") + await self.send_test_event_trigger_forecast_adjustment_clear() + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_DEM_2_6.py b/src/python_testing/TC_DEM_2_6.py new file mode 100644 index 00000000000000..7390436effd937 --- /dev/null +++ b/src/python_testing/TC_DEM_2_6.py @@ -0,0 +1,290 @@ +# +# 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. +# pylint: disable=invalid-name + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${ENERGY_MANAGEMENT_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json --enable-key 000102030405060708090a0b0c0d0e0f --featureSet 0x7c +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --hex-arg enableKey:000102030405060708090a0b0c0d0e0f --endpoint 1 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# === END CI TEST ARGUMENTS === + +"""Define Matter test case TC_DEM_2_6.""" + + +import logging + +import chip.clusters as Clusters +from chip.interaction_model import Status +from matter_testing_support import EventChangeCallback, MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts +from TC_DEMTestBase import DEMTestBase + +logger = logging.getLogger(__name__) + + +class TC_DEM_2_6(MatterBaseTest, DEMTestBase): + """Implementation of test case TC_DEM_2_6.""" + + def desc_TC_DEM_2_6(self) -> str: + """Return a description of this test.""" + return "4.1.3. [TC-DEM-2.6] Forecast Adjustment with State Forecast Reporting feature functionality with DUT as Server" + + def pics_TC_DEM_2_6(self): + """Return the PICS definitions associated with this test.""" + pics = [ + # Depends on Feature 05 (ForecastAdjustment) & Feature 02 (StateForecastReporting) + "DEM.S.F05", "DEM.S.F02" + ] + return pics + + def steps_TC_DEM_2_6(self) -> list[TestStep]: + """Execute the test steps.""" + steps = [ + TestStep("1", "Commissioning, already done", + is_commissioning=True), + TestStep("2", "TH reads TestEventTriggersEnabled attribute from General Diagnostics Cluster.", + "Verify that TestEventTriggersEnabled attribute has a value of 1 (True)"), + TestStep("3", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Forecast Adjustment Test Event", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("3a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("3b", "TH reads Forecast attribute.", + "Value has to include slots[0].MinDurationAdjustment, slots[0].MaxDurationAdjustment"), + TestStep("3c", "TH reads OptOutState attribute.", + "Verify value is 0x00 (NoOptOut)"), + TestStep("4", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID+1, SlotAdjustments[0].{SlotIndex=0, Duration=Forecast.Slots[0].MaxDurationAdjustment}, Cause=GridOptimization.", + "Verify DUT responds with status FAILURE(0x01)"), + TestStep("5", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=4, Duration=Forecast.Slots[0].MaxDurationAdjustment}, Cause=GridOptimization.", + "Verify DUT responds with status FAILURE(0x01)"), + TestStep("6", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, Duration=Forecast.Slots[0].MaxDurationAdjustment+1}, Cause=GridOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("7", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, Duration=Forecast.Slots[0].MinDurationAdjustment-1}, Cause=GridOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("8", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, Duration=Forecast.Slots[0].MaxDurationAdjustment}, SlotAdjustments[1].{SlotIndex=4, Duration=Forecast.Slots[0].MaxDurationAdjustment}, Cause=GridOptimization.", + "Verify DUT responds with status FAILURE(0x01)"), + TestStep("9", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Local Optimization Test Event", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("9a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("9b", "TH reads OptOutState attribute.", + "Verify value is 0x02 (LocalOptOut)"), + TestStep("10", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, Duration=Forecast.Slots[0].MaxDurationAdjustment}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("11", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, Duration=Forecast.Slots[0].MaxDurationAdjustment}, Cause=GridOptimization.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("11a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=GridOptimization"), + TestStep("12", "TH sends CancelRequest.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("12a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=InternalOptimization"), + TestStep("13", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, Duration=Forecast.Slots[0].MaxDurationAdjustment}, Cause=GridOptimization.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("13a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=GridOptimization"), + TestStep("14", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Grid Optimization Test Event", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("14a", "TH reads OptOutState attribute.", + "Verify value is 0x03 (OptOut)"), + TestStep("14b", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=Internal Optimization"), + TestStep("15", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, Duration=Forecast.Slots[0].MaxDurationAdjustment}, Cause=GridOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("16", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Test Event Clear", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("16a", "TH reads OptOutState attribute.", + "Verify value is 0x00 (NoOptOut)"), + TestStep("17", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, Duration=Forecast.Slots[0].MinDurationAdjustment}, Cause=LocalOptimization.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("17a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=LocalOptimization"), + TestStep("18", "TH sends CancelRequest.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("18a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=InternalOptimization"), + TestStep("19", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Forecast Adjustment Test Event Next Slot", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("20", "TH sends ModifyForecastRequest with ForecastID=Forecast.ForecastID, SlotAdjustments[0].{SlotIndex=0, Duration=Forecast.Slots[0].MinDurationAdjustment}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("21", "TH sends CancelRequest.", + "Verify DUT responds with status INVALID_IN_STATE(0xcb)"), + TestStep("22", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Forecast Adjustment Test Event Clear", + "Verify DUT responds with status SUCCESS(0x00)"), + ] + + return steps + + @async_test_body + async def test_TC_DEM_2_6(self): + # pylint: disable=too-many-locals, too-many-statements + """Run the test steps.""" + self.step("1") + # Commission DUT - already done + + # Subscribe to Events and when they are sent push them to a queue for checking later + events_callback = EventChangeCallback(Clusters.DeviceEnergyManagement) + await events_callback.start(self.default_controller, + self.dut_node_id, + self.matter_test_config.endpoint) + + self.step("2") + await self.check_test_event_triggers_enabled() + + self.step("3") + await self.send_test_event_trigger_forecast_adjustment() + + self.step("3a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("3b") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_is_not_none(forecast.slots[0].minDurationAdjustment) + asserts.assert_is_not_none(forecast.slots[0].maxDurationAdjustment) + + self.step("3c") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kNoOptOut) + + self.step("4") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, duration=forecast.slots[0].maxDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID + 1, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.Failure) + + self.step("5") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=4, duration=forecast.slots[0].maxDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.Failure) + + self.step("6") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, duration=forecast.slots[0].maxDurationAdjustment + 1)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.ConstraintError) + + self.step("7") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, duration=forecast.slots[0].minDurationAdjustment - 1)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.ConstraintError) + + self.step("8") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct(slotIndex=0, duration=forecast.slots[0].maxDurationAdjustment), + Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct(slotIndex=4, duration=forecast.slots[0].maxDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.Failure) + + self.step("9") + await self.send_test_event_trigger_user_opt_out_local() + + self.step("9a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("9b") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kLocalOptOut) + + self.step("10") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, duration=forecast.slots[0].maxDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("11") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, duration=forecast.slots[0].maxDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.Success) + + self.step("11a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + logging.info(forecast) + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kGridOptimization) + + self.step("12") + await self.send_cancel_request_command() + + self.step("12a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization) + + self.step("13") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, duration=forecast.slots[0].maxDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.Success) + + self.step("13a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kGridOptimization) + + self.step("14") + await self.send_test_event_trigger_user_opt_out_grid() + + self.step("14a") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kOptOut) + + self.step("14b") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization) + + self.step("15") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, duration=forecast.slots[0].maxDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.ConstraintError) + + self.step("16") + await self.send_test_event_trigger_user_opt_out_clear_all() + + self.step("16a") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kNoOptOut) + + self.step("17") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, duration=forecast.slots[0].minDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.Success) + + self.step("17a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kLocalOptimization) + + self.step("18") + await self.send_cancel_request_command() + + self.step("18a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization) + + self.step("19") + await self.send_test_event_trigger_forecast_adjustment_next_slot() + + self.step("20") + slotAdjustments = [Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct( + slotIndex=0, duration=forecast.slots[0].minDurationAdjustment)] + await self.send_modify_forecast_request_command(forecast.forecastID, slotAdjustments, Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("21") + await self.send_cancel_request_command(expected_status=Status.InvalidInState) + + self.step("22") + await self.send_test_event_trigger_forecast_adjustment_clear() + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_DEM_2_7.py b/src/python_testing/TC_DEM_2_7.py new file mode 100644 index 00000000000000..fa7dba14a53315 --- /dev/null +++ b/src/python_testing/TC_DEM_2_7.py @@ -0,0 +1,345 @@ +# +# 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. +# pylint: disable=invalid-name + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${ENERGY_MANAGEMENT_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json --enable-key 000102030405060708090a0b0c0d0e0f --featureSet 0x7a +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --hex-arg enableKey:000102030405060708090a0b0c0d0e0f --endpoint 1 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# === END CI TEST ARGUMENTS === + +"""Define Matter test case TC_DEM_2_7.""" + + +import logging + +import chip.clusters as Clusters +from chip.interaction_model import Status +from matter_testing_support import EventChangeCallback, MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts +from TC_DEMTestBase import DEMTestBase + +logger = logging.getLogger(__name__) + + +class TC_DEM_2_7(MatterBaseTest, DEMTestBase): + """Implementation of test case TC_DEM_2_7.""" + + def desc_TC_DEM_2_7(self) -> str: + """Return a description of this test.""" + return "4.1.3. [TC-DEM-2.7] Constraints-based Adjustment with Power Forecast Reporting feature functionality with DUT as Server" + + def pics_TC_DEM_2_7(self): + """Return the PICS definitions associated with this test.""" + pics = [ + # Depends on Feature 06 (ConstraintBasedAdjustment) & Feature 01 (PowerForecastReporting) + "DEM.S.F06", "DEM.S.F01" + ] + return pics + + def steps_TC_DEM_2_7(self) -> list[TestStep]: + """Execute the test steps.""" + steps = [ + TestStep("1", "Commissioning, already done", + is_commissioning=True), + TestStep("2", "TH reads TestEventTriggersEnabled attribute from General Diagnostics Cluster.", + "Verify value is 1 (True)"), + TestStep("3", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Constraints-based Adjustment Test Event.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("3a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("3b", "TH reads Forecast attribute.", + "Value has to include valid slots[0].NominalPower, slots[0].MinPower, slots[0].MaxPower, slots[0].NominalEnergy"), + TestStep("3c", "TH reads OptOutState attribute.", + "Verify value is 0x00 (NoOptOut)"), + TestStep("4", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=now()-10, Duration=20, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("5", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=now()+10, Duration=20, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, constraints[1].{StartTime=now()+20, Duration=20, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, constraints[2].{StartTime=now()+40, Duration=20, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("6", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=now()+10, Duration=20, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, constraints[1].{StartTime=now()+30, Duration=20, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, constraints[2].{StartTime=now()+40, Duration=20, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("7", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=now()+30, Duration=20, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, constraints[1].{StartTime=now()+10, Duration=20, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, constraints[2].{StartTime=now()+50, Duration=20, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("8", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=now()+10, Duration=20, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, constraints[1].{StartTime=now()+50, Duration=20, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, constraints[2].{StartTime=now()+30, Duration=20, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("9", "TH reads AbsMaxPower attribute attribute.", + "Save the value"), + TestStep("9a", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=Forecast.StartTime, Duration=Forecast.Slots[0].DefaultDuration, NominalPower=AbsMaxPower+1, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("10", "TH reads AbsMinPower attribute attribute.", + "Save the value"), + TestStep("10a", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=Forecast.StartTime, Duration=Forecast.Slots[0].DefaultDuration, NominalPower=AbsMinPower-1, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("11", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=Forecast.StartTime, Duration=Forecast.Slots[0].DefaultDuration, NominalPower=Forecast.Slots[0].NominalPower}, Cause=LocalOptimization.", + "Verify DUT responds with status InvalidCommand"), + TestStep("12", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=Forecast.StartTime, Duration=Forecast.Slots[0].DefaultDuration, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, Cause=LocalOptimization.", + "Verify DUT responds with status InvalidCommand"), + TestStep("13", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Local Optimization Test Event.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("13a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("13b", "TH reads OptOutState attribute.", + "Verify value is 0x02 (LocalOptOut)"), + TestStep("14", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=Forecast.StartTime, Duration=Forecast.Slots[0].DefaultDuration, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("15", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=Forecast.StartTime, Duration=Forecast.Slots[0].DefaultDuration, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, Cause=GridOptimization.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("15a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=GridOptimization"), + TestStep("16", "TH sends CancelRequest.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("16a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=InternalOptimization"), + TestStep("17", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=Forecast.StartTime, Duration=Forecast.Slots[0].DefaultDuration, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, Cause=GridOptimization.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("17a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=GridOptimization"), + TestStep("18", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Grid Optimization Test Event.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("18a", "TH reads OptOutState attribute.", + "Verify value is 0x03 (OptOut)"), + TestStep("18b", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=InternalOptimization"), + TestStep("19", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=Forecast.StartTime, Duration=Forecast.Slots[0].DefaultDuration, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, Cause=GridOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("20", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Test Event Clear.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("20a", "TH reads OptOutState attribute.", + "Verify value is 0x00 (NoOptOut)"), + TestStep("21", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=Forecast.StartTime, Duration=Forecast.Slots[0].DefaultDuration, NominalPower=Forecast.Slots[0].NominalPower, MaximumEnergy=Forecast.Slots[0].NominalEnergy}, Cause=LocalOptimization.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("21a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=LocalOptimization"), + TestStep("22", "TH sends CancelRequest.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("22a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=InternalOptimization"), + TestStep("23", "TH sends CancelRequest.", + "Verify DUT responds with status INVALID_IN_STATE(0xcb)"), + TestStep("24", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Constraints-based Adjustment Adjustment Test Event Clear.", + "Verify DUT responds with status SUCCESS(0x00)"), + ] + + return steps + + @async_test_body + async def test_TC_DEM_2_7(self): + # pylint: disable=too-many-locals, too-many-statements + """Run the test steps.""" + self.step("1") + # Commission DUT - already done + + # Subscribe to Events and when they are sent push them to a queue for checking later + events_callback = EventChangeCallback(Clusters.DeviceEnergyManagement) + await events_callback.start(self.default_controller, + self.dut_node_id, + self.matter_test_config.endpoint) + + self.step("2") + await self.check_test_event_triggers_enabled() + + self.step("3") + await self.send_test_event_trigger_constraint_based_adjustment() + + self.step("3a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("3b") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + logging.info(forecast) + asserts.assert_greater(forecast.slots[0].nominalPower, 0) + asserts.assert_greater(forecast.slots[0].minPower, 0) + asserts.assert_greater(forecast.slots[0].maxPower, 0) + asserts.assert_greater(forecast.slots[0].nominalEnergy, 0) + + self.step("3c") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kNoOptOut) + + self.step("4") + # Matter UTC is time since 00:00:00 1/1/2000 + now = self.get_current_utc_time_in_seconds() + + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now - 10, duration=20, + nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("5") + now = self.get_current_utc_time_in_seconds() + + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 10, duration=20, + nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy), + Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 20, duration=20, + nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy), + Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 40, duration=20, + nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("6") + now = self.get_current_utc_time_in_seconds() + + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 10, duration=20, + nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy), + Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 30, duration=20, + nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy), + Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 40, duration=20, + nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("7") + now = self.get_current_utc_time_in_seconds() + + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 30, duration=20, + nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy), + Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 10, duration=20, + nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy), + Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 50, duration=20, + nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("8") + now = self.get_current_utc_time_in_seconds() + + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 10, duration=20, + nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy), + Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 50, duration=20, + nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy), + Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 30, duration=20, + nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("9") + absMaxPower = await self.read_dem_attribute_expect_success(attribute="AbsMaxPower") + + self.step("9a") + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct( + startTime=forecast.startTime, duration=forecast.slots[0].defaultDuration, nominalPower=absMaxPower + 1, maximumEnergy=forecast.slots[0].nominalEnergy)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("10") + absMinPower = await self.read_dem_attribute_expect_success(attribute="AbsMinPower") + + self.step("10a") + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct( + startTime=forecast.startTime, duration=forecast.slots[0].defaultDuration, nominalPower=absMinPower - 1, maximumEnergy=forecast.slots[0].nominalEnergy)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("11") + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct( + startTime=forecast.startTime, duration=forecast.slots[0].defaultDuration, nominalPower=forecast.slots[0].nominalPower)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.InvalidCommand) + + self.step("12") + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct( + startTime=forecast.startTime, duration=forecast.slots[0].defaultDuration, maximumEnergy=forecast.slots[0].nominalEnergy)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.InvalidCommand) + + self.step("13") + await self.send_test_event_trigger_user_opt_out_local() + + self.step("13a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("13b") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kLocalOptOut) + + self.step("14") + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct( + startTime=forecast.startTime, duration=forecast.slots[0].defaultDuration, nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("15") + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct( + startTime=forecast.startTime, duration=forecast.slots[0].defaultDuration, nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.Success) + + self.step("15a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kGridOptimization) + + self.step("16") + await self.send_cancel_request_command() + + self.step("16a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization) + + self.step("17") + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct( + startTime=forecast.startTime, duration=forecast.slots[0].defaultDuration, nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.Success) + + self.step("17a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kGridOptimization) + + self.step("18") + await self.send_test_event_trigger_user_opt_out_grid() + + self.step("18a") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kOptOut) + + self.step("18b") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization) + + self.step("19") + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct( + startTime=forecast.startTime, duration=forecast.slots[0].defaultDuration, nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.ConstraintError) + + self.step("20") + await self.send_test_event_trigger_user_opt_out_clear_all() + + self.step("20a") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kNoOptOut) + + self.step("21") + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct( + startTime=forecast.startTime, duration=forecast.slots[0].defaultDuration, nominalPower=forecast.slots[0].nominalPower, maximumEnergy=forecast.slots[0].nominalEnergy)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.Success) + + self.step("21a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kLocalOptimization) + + self.step("22") + await self.send_cancel_request_command() + + self.step("22a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization) + + self.step("23") + await self.send_cancel_request_command(expected_status=Status.InvalidInState) + + self.step("24") + await self.send_test_event_trigger_constraint_based_adjustment_clear() + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_DEM_2_8.py b/src/python_testing/TC_DEM_2_8.py new file mode 100644 index 00000000000000..773648c7e3ba8e --- /dev/null +++ b/src/python_testing/TC_DEM_2_8.py @@ -0,0 +1,307 @@ +# +# 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. +# pylint: disable=invalid-name + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${ENERGY_MANAGEMENT_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json --enable-key 000102030405060708090a0b0c0d0e0f --featureSet 0x7c +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --hex-arg enableKey:000102030405060708090a0b0c0d0e0f --endpoint 1 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# === END CI TEST ARGUMENTS === + +"""Define Matter test case TC_DEM_2_8.""" + + +import logging + +import chip.clusters as Clusters +from chip.interaction_model import Status +from matter_testing_support import EventChangeCallback, MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts +from TC_DEMTestBase import DEMTestBase + +logger = logging.getLogger(__name__) + + +class TC_DEM_2_8(MatterBaseTest, DEMTestBase): + """Implementation of test case TC_DEM_2_8.""" + + def desc_TC_DEM_2_8(self) -> str: + """Return a description of this test.""" + return "4.1.3. [TC-DEM-2.8] Constraints-based Adjustment with State Forecast Reporting feature functionality with DUT as Server" + + def pics_TC_DEM_2_8(self): + """Return the PICS definitions associated with this test.""" + pics = [ + # Depends on Feature 06 (ConstraintBasedAdjustment) & Feature 02 (StateForecastReporting) + "DEM.S.F06", "DEM.S.F02" + ] + return pics + + def steps_TC_DEM_2_8(self) -> list[TestStep]: + """Execute the test steps.""" + steps = [ + TestStep("1", "Commissioning, already done", + is_commissioning=True), + TestStep("2", "TH reads TestEventTriggersEnabled attribute from General Diagnostics Cluster.", + "Verify value is 1 (True)"), + TestStep("3", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Constraints-based Adjustment Test Event.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("3a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("3b", "TH reads Forecast attribute.", + "Value has to include valid slots[0].ManufacturerESAState"), + TestStep("3c", "TH reads OptOutState attribute.", + "Verify value is 0x00 (NoOptOut)"), + TestStep("4", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=now()-10, Duration=20, LoadControl=0}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("5", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=now()+10, Duration=20, LoadControl=0}, constraints[1].{StartTime=now()+20, Duration=20, LoadControl=0}, constraints[2].{StartTime=now()+50, Duration=20, LoadControl=0}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("6", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=now()+10, Duration=20, LoadControl=0}, constraints[1].{StartTime=now()+30, Duration=20, LoadControl=0}, constraints[2].{StartTime=now()+40, Duration=20, LoadControl=0}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("7", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=now()+30, Duration=20, LoadControl=0}, constraints[1].{StartTime=now()+10, Duration=20, LoadControl=0}, constraints[2].{StartTime=now()+50, Duration=20, LoadControl=0}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("8", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=now()+10, Duration=20, LoadControl=0}, constraints[1].{StartTime=now()+50, Duration=20, LoadControl=0}, constraints[2].{StartTime=now()+30, Duration=20, LoadControl=0}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("9", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=Forecast.StartTime, Duration=Forecast.Slots[0].DefaultDuration, LoadControl=101}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("10", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=Forecast.StartTime, Duration=Forecast.Slots[0].DefaultDuration, LoadControl=-101}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("11", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Local Optimization Test Event.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("11a", "TH reads ESAState attribute.", + "Verify value is 0x01 (Online)"), + TestStep("11b", "TH reads OptOutState attribute.", + "Verify value is 0x02 (LocalOptOut)"), + TestStep("12", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=Forecast.StartTime, Duration=Forecast.Slots[0].DefaultDuration, LoadControl=1}, Cause=LocalOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("13", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=Forecast.StartTime, Duration=Forecast.Slots[0].DefaultDuration, LoadControl=1}, Cause=GridOptimization.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("13a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=GridOptimization"), + TestStep("14", "TH sends CancelRequest.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("14a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=InternalOptimization"), + TestStep("15", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=Forecast.StartTime, Duration=Forecast.Slots[0].DefaultDuration, LoadControl=1}, Cause=GridOptimization.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("15a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=GridOptimization"), + TestStep("16", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Grid Optimization Test Event.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("16a", "TH reads OptOutState attribute.", + "Verify value is 0x03 (OptOut)"), + TestStep("16b", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=InternalOptimization"), + TestStep("17", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=Forecast.StartTime, Duration=Forecast.Slots[0].DefaultDuration, LoadControl=1}, Cause=GridOptimization.", + "Verify DUT responds with status CONSTRAINT_ERROR(0x87)"), + TestStep("18", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for User Opt-out Test Event Clear.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("18a", "TH reads OptOutState attribute.", + "Verify value is 0x00 (NoOptOut)"), + TestStep("19", "TH sends RequestConstraintBasedPowerForecast with constraints[0].{StartTime=Forecast.StartTime, Duration=Forecast.Slots[0].DefaultDuration, LoadControl=1}, Cause=LocalOptimization.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("19a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=LocalOptimization"), + TestStep("20", "TH sends CancelRequest.", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("20a", "TH reads Forecast attribute.", + "Value has to include ForecastUpdateReason=InternalOptimization"), + TestStep("21", "TH sends CancelRequest.", + "Verify DUT responds with status INVALID_IN_STATE(0xcb)"), + TestStep("22", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Constraints-based Adjustment Adjustment Test Event Clear.", + "Verify DUT responds with status SUCCESS(0x00)"), + ] + + return steps + + @async_test_body + async def test_TC_DEM_2_8(self): + # pylint: disable=too-many-locals, too-many-statements + """Run the test steps.""" + self.step("1") + # Commission DUT - already done + + # Subscribe to Events and when they are sent push them to a queue for checking later + events_callback = EventChangeCallback(Clusters.DeviceEnergyManagement) + await events_callback.start(self.default_controller, + self.dut_node_id, + self.matter_test_config.endpoint) + + self.step("2") + await self.check_test_event_triggers_enabled() + + self.step("3") + await self.send_test_event_trigger_constraint_based_adjustment() + + self.step("3a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("3b") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_is_not_none(forecast.slots[0].manufacturerESAState) + + self.step("3c") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kNoOptOut) + + self.step("4") + # Matter UTC is time since 00:00:00 1/1/2000 + now = self.get_current_utc_time_in_seconds() + + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct( + startTime=now - 10, duration=20, loadControl=0)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("5") + # Matter UTC is time since 00:00:00 1/1/2000 + now = self.get_current_utc_time_in_seconds() + + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 10, duration=20, loadControl=0), + Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 20, duration=20, loadControl=0), + Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 50, duration=20, loadControl=0)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("6") + # Matter UTC is time since 00:00:00 1/1/2000 + now = self.get_current_utc_time_in_seconds() + + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 10, duration=20, loadControl=0), + Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 30, duration=20, loadControl=0), + Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 40, duration=20, loadControl=0)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("7") + now = self.get_current_utc_time_in_seconds() + + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 30, duration=20, loadControl=0), + Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 10, duration=20, loadControl=0), + Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 50, duration=20, loadControl=0)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("8") + now = self.get_current_utc_time_in_seconds() + + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 10, duration=20, loadControl=0), + Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 50, duration=20, loadControl=0), + Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct(startTime=now + 30, duration=20, loadControl=0)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("9") + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct( + startTime=forecast.startTime, duration=forecast.slots[0].defaultDuration, loadControl=101)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("10") + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct( + startTime=forecast.startTime, duration=forecast.slots[0].defaultDuration, loadControl=-101)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("11") + await self.send_test_event_trigger_user_opt_out_local() + + self.step("11a") + await self.check_dem_attribute("ESAState", Clusters.DeviceEnergyManagement.Enums.ESAStateEnum.kOnline) + + self.step("11b") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kLocalOptOut) + + self.step("12") + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct( + startTime=forecast.startTime, duration=forecast.slots[0].defaultDuration, loadControl=1)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.ConstraintError) + + self.step("13") + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct( + startTime=forecast.startTime, duration=forecast.slots[0].defaultDuration, loadControl=1)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.Success) + + self.step("13a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kGridOptimization) + + self.step("14") + await self.send_cancel_request_command() + + self.step("14a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization) + + self.step("15") + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct( + startTime=forecast.startTime, duration=forecast.slots[0].defaultDuration, loadControl=1)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.Success) + + self.step("15a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kGridOptimization) + + self.step("16") + await self.send_test_event_trigger_user_opt_out_grid() + + self.step("16a") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kOptOut) + + self.step("16b") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization) + + self.step("17") + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct( + startTime=forecast.startTime, duration=forecast.slots[0].defaultDuration, loadControl=1)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kGridOptimization, expected_status=Status.ConstraintError) + + self.step("18") + await self.send_test_event_trigger_user_opt_out_clear_all() + + self.step("18a") + await self.check_dem_attribute("OptOutState", Clusters.DeviceEnergyManagement.Enums.OptOutStateEnum.kNoOptOut) + + self.step("19") + constraintList = [Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct( + startTime=forecast.startTime, duration=forecast.slots[0].defaultDuration, loadControl=1)] + await self.send_request_constraint_based_forecast(constraintList, cause=Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum.kLocalOptimization, expected_status=Status.Success) + + self.step("19a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kLocalOptimization) + + self.step("20") + await self.send_cancel_request_command() + + self.step("20a") + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_equal(forecast.forecastUpdateReason, + Clusters.DeviceEnergyManagement.Enums.ForecastUpdateReasonEnum.kInternalOptimization) + + self.step("21") + await self.send_cancel_request_command(expected_status=Status.InvalidInState) + + self.step("22") + await self.send_test_event_trigger_constraint_based_adjustment_clear() + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_DEM_2_9.py b/src/python_testing/TC_DEM_2_9.py new file mode 100644 index 00000000000000..8e60b69b481d93 --- /dev/null +++ b/src/python_testing/TC_DEM_2_9.py @@ -0,0 +1,120 @@ +# +# 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. +# pylint: disable=invalid-name + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${ENERGY_MANAGEMENT_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json --enable-key 000102030405060708090a0b0c0d0e0f --featureSet 0x7e +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --hex-arg enableKey:000102030405060708090a0b0c0d0e0f --endpoint 1 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# === END CI TEST ARGUMENTS === + +"""Define Matter test case TC_DEM_2_9.""" + + +import logging + +import chip.clusters as Clusters +from matter_testing_support import EventChangeCallback, MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts +from TC_DEMTestBase import DEMTestBase + +logger = logging.getLogger(__name__) + + +class TC_DEM_2_9(MatterBaseTest, DEMTestBase): + """Implementation of test case TC_DEM_2_9.""" + + def desc_TC_DEM_2_9(self) -> str: + """Return a description of this test.""" + return "4.1.3. [TC-DEM-2.2] Power or State Forecast Reporting feature functionality with DUT as Server" + + def pics_TC_DEM_2_9(self): + """Return the PICS definitions associated with this test.""" + pics = [ + # Depends on Feature 01 (PowerForecastReporting) | Feature 2 (StateForecastReporting) + "DEM.S.F01", "DEM.S.F02", + ] + return pics + + def steps_TC_DEM_2_9(self) -> list[TestStep]: + """Execute the test steps.""" + steps = [ + TestStep("1", "Commissioning, already done", + is_commissioning=True), + TestStep("2", "TH reads TestEventTriggersEnabled attribute from General Diagnostics Cluster.", + "Verify that TestEventTriggersEnabled attribute has a value of 1 (True)"), + TestStep("3", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Forecast Test Event", + "Verify DUT responds with status SUCCESS(0x00)"), + TestStep("3a", "TH reads Forecast attribute.", + "Value has to include a valid slots[0].ManufacturerESAState"), + TestStep("3b", "TH reads Forecast attribute.", + "Value has to include valid slots[0].NominalPower, slots[0].MinPower, slots[0].MaxPower, slots[0].NominalEnergy"), + TestStep("4", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.DEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.DEM.TEST_EVENT_TRIGGER for Forecast Test Event Clear", + "Verify DUT responds with status SUCCESS(0x00)"), + ] + + return steps + + @async_test_body + async def test_TC_DEM_2_9(self): + # pylint: disable=too-many-locals, too-many-statements + """Run the test steps.""" + self.step("1") + # Commission DUT - already done + + # Subscribe to Events and when they are sent push them to a queue for checking later + events_callback = EventChangeCallback(Clusters.DeviceEnergyManagement) + await events_callback.start(self.default_controller, + self.dut_node_id, + self.matter_test_config.endpoint) + + self.step("2") + await self.check_test_event_triggers_enabled() + + self.step("3") + await self.send_test_event_trigger_forecast() + + self.step("3a") + feature_map = await self.read_dem_attribute_expect_success(attribute="FeatureMap") + if feature_map & Clusters.DeviceEnergyManagement.Bitmaps.Feature.kStateForecastReporting: + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + asserts.assert_is_not_none(forecast.slots[0].manufacturerESAState) + else: + logging.info('Device does not support StateForecastReporting. Skipping step 3a') + + self.step("3b") + if feature_map & Clusters.DeviceEnergyManagement.Bitmaps.Feature.kPowerForecastReporting: + forecast = await self.read_dem_attribute_expect_success(attribute="Forecast") + + asserts.assert_is_not_none(forecast.slots[0].nominalPower) + asserts.assert_is_not_none(forecast.slots[0].minPower) + asserts.assert_is_not_none(forecast.slots[0].maxPower) + asserts.assert_is_not_none(forecast.slots[0].nominalEnergy) + else: + logging.info('Device does not support StateForecastReporting. Skipping step 3b') + + self.step("4") + await self.send_test_event_trigger_forecast_clear() + + +if __name__ == "__main__": + default_matter_test_main() From 07c85dcfbc397f007f4b1e39acac7f4c875ac41a Mon Sep 17 00:00:00 2001 From: cdj <45139296+DejinChen@users.noreply.github.com> Date: Fri, 26 Jul 2024 18:35:03 +0800 Subject: [PATCH 28/49] ESP32: added config for Wi-Fi, Thread, and Ethernet network drivers. (#34052) * ESP32: added config for Wi-Fi, Thread, and Ethernet network commissioning drivers * Update README.md * Restyled * Restyled by prettier-markdown * Fix CI --------- Co-authored-by: Restyled.io --- config/esp32/components/chip/CMakeLists.txt | 3 ++ config/esp32/components/chip/Kconfig | 54 +++++++++++++++++++ examples/all-clusters-app/esp32/README.md | 18 ++++--- .../esp32/sdkconfig.defaults | 3 ++ .../platform/esp32/common/Esp32AppServer.cpp | 18 +------ examples/shell/esp32/sdkconfig.defaults | 3 ++ src/platform/ESP32/CHIPDevicePlatformConfig.h | 15 ++++++ .../ESP32/ConnectivityManagerImpl.cpp | 34 ++++++++++++ 8 files changed, 125 insertions(+), 23 deletions(-) diff --git a/config/esp32/components/chip/CMakeLists.txt b/config/esp32/components/chip/CMakeLists.txt index f27b29baf91ff9..6ddfc4839123ff 100644 --- a/config/esp32/components/chip/CMakeLists.txt +++ b/config/esp32/components/chip/CMakeLists.txt @@ -224,6 +224,9 @@ endif() if (CONFIG_ENABLE_MATTER_OVER_THREAD) chip_gn_arg_append("chip_enable_openthread" "true") + if (CONFIG_THREAD_NETWORK_COMMISSIONING_DRIVER) + chip_gn_arg_append("chip_device_config_thread_network_endpoint_id" ${CONFIG_THREAD_NETWORK_ENDPOINT_ID}) + endif() else() chip_gn_arg_append("chip_enable_openthread" "false") endif() diff --git a/config/esp32/components/chip/Kconfig b/config/esp32/components/chip/Kconfig index c742ddb0336764..5cd2700bb972b6 100644 --- a/config/esp32/components/chip/Kconfig +++ b/config/esp32/components/chip/Kconfig @@ -157,6 +157,12 @@ menu "CHIP Core" help Option to enable/disable CHIP log level filtering APIs. + config ENABLE_CHIP_DATA_MODEL + bool "Enable CHIP data model" + default y + help + Option to enable/disable CHIP data model. + endmenu # "General Options" menu "Networking Options" @@ -1289,4 +1295,52 @@ menu "CHIP Device Layer" endmenu + menu "Network Commissioning Driver Endpoint Id" + config THREAD_NETWORK_COMMISSIONING_DRIVER + bool "Use the generic Thread network commissioning driver" + depends on ENABLE_MATTER_OVER_THREAD && ENABLE_CHIP_DATA_MODEL + default y + help + Option to enable/disable the use of generic Thread network commissioning driver. + + config THREAD_NETWORK_ENDPOINT_ID + int "Endpoint Id for Thread network" + depends on THREAD_NETWORK_COMMISSIONING_DRIVER + range 0 65534 + default 0 + help + The endpoint id for the generic Thread network commissioning driver. + + config WIFI_NETWORK_COMMISSIONING_DRIVER + bool "Use ESP Wi-Fi network commissioning driver" + depends on (ENABLE_WIFI_STATION || ENABLE_WIFI_AP) && ENABLE_CHIP_DATA_MODEL + default y + help + Option to enable/disable the use of ESP Wi-Fi network commissioning driver. + + config WIFI_NETWORK_ENDPOINT_ID + int "Endpoint Id for Wi-Fi network" + depends on WIFI_NETWORK_COMMISSIONING_DRIVER + range 0 65534 + default 0 + help + The endpoint id for the ESP Wi-Fi network commissioning driver. + + config ETHERNET_NETWORK_COMMISSIONING_DRIVER + bool "Use ESP Ethernet network commissioning driver" + depends on ENABLE_ETHERNET_TELEMETRY && ENABLE_CHIP_DATA_MODEL + default y + help + Option to enable/disable the use of ESP Ethernet network commissioning driver. + + config ETHERNET_NETWORK_ENDPOINT_ID + int "Endpoint Id for Ethernet network" + depends on ETHERNET_NETWORK_COMMISSIONING_DRIVER + range 0 65534 + default 0 + help + The endpoint id for the ESP Ethernet network commissioning driver. + + endmenu + endmenu diff --git a/examples/all-clusters-app/esp32/README.md b/examples/all-clusters-app/esp32/README.md index 60ee180c1337fd..0a3145dbc24da2 100644 --- a/examples/all-clusters-app/esp32/README.md +++ b/examples/all-clusters-app/esp32/README.md @@ -47,14 +47,20 @@ NetworkCommissioning Endpoint can be used to manage the driver of extra network interface. For ESP32-C6 DevKits, if `CHIP_DEVICE_CONFIG_ENABLE_WIFI` and -`CHIP_DEVICE_CONFIG_ENABLE_THREAD` are both enabled, the NetworkCommissioning -cluster in Endpoint 0 will be used for Thread network driver and the same -cluster on Endpoint 65534 will be used for Wi-Fi network driver. +`CHIP_DEVICE_CONFIG_ENABLE_THREAD` are both enabled, please set +`CONFIG_THREAD_NETWORK_ENDPOINT_ID` to 0 and set +`CONFIG_WIFI_NETWORK_ENDPOINT_ID` to 65534, which presents that the +NetworkCommissioning cluster in Endpoint 0 will be used for Thread network +driver and the same cluster on Endpoint 65534 will be used for Wi-Fi network +driver. Or vice versa. For ESP32-Ethernet-Kits, if `CHIP_DEVICE_CONFIG_ENABLE_WIFI` and -`CHIP_DEVICE_CONFIG_ENABLE_ETHERNET` are both enabled, the NetworkCommissioning -cluster in Endpoint 0 will be used for Ethernet network driver and the same -cluster on Endpoint 65534 will be used for Wi-Fi network driver. +`CHIP_DEVICE_CONFIG_ENABLE_ETHERNET` are both enabled, please set +`CONFIG_ETHERNET_NETWORK_ENDPOINT_ID` to 0 and set +`CONFIG_WIFI_NETWORK_ENDPOINT_ID` to 65534, which presents that the +NetworkCommissioning cluster in Endpoint 0 will be used for Ethernet network +driver and the same cluster on Endpoint 65534 will be used for Wi-Fi network +driver. Or vice versa. --- diff --git a/examples/persistent-storage/esp32/sdkconfig.defaults b/examples/persistent-storage/esp32/sdkconfig.defaults index f9a4c4bbcb05da..45488e615dece7 100644 --- a/examples/persistent-storage/esp32/sdkconfig.defaults +++ b/examples/persistent-storage/esp32/sdkconfig.defaults @@ -32,3 +32,6 @@ CONFIG_DEVICE_PRODUCT_ID=0x8009 # Enable HKDF in mbedtls CONFIG_MBEDTLS_HKDF_C=y + +# Disable CHIP data model +CONFIG_ENABLE_CHIP_DATA_MODEL=n diff --git a/examples/platform/esp32/common/Esp32AppServer.cpp b/examples/platform/esp32/common/Esp32AppServer.cpp index 1c30098e9010d5..8ee5d4d7a05a27 100644 --- a/examples/platform/esp32/common/Esp32AppServer.cpp +++ b/examples/platform/esp32/common/Esp32AppServer.cpp @@ -51,19 +51,6 @@ using namespace chip::DeviceLayer; static constexpr char TAG[] = "ESP32Appserver"; namespace { -#if CHIP_DEVICE_CONFIG_ENABLE_WIFI -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD || CHIP_DEVICE_CONFIG_ENABLE_ETHERNET -constexpr chip::EndpointId kNetworkCommissioningEndpointWiFi = 0xFFFE; -#else -constexpr chip::EndpointId kNetworkCommissioningEndpointWiFi = 0; -#endif -app::Clusters::NetworkCommissioning::Instance - sWiFiNetworkCommissioningInstance(kNetworkCommissioningEndpointWiFi, &(NetworkCommissioning::ESPWiFiDriver::GetInstance())); -#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI -#if CHIP_DEVICE_CONFIG_ENABLE_ETHERNET -static app::Clusters::NetworkCommissioning::Instance - sEthernetNetworkCommissioningInstance(0 /* Endpoint Id */, &(NetworkCommissioning::ESPEthernetDriver::GetInstance())); -#endif #if CONFIG_TEST_EVENT_TRIGGER_ENABLED static uint8_t sTestEventTriggerEnableKey[TestEventTriggerDelegate::kEnableKeyLength] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, @@ -150,14 +137,11 @@ void Esp32AppServer::Init(AppDelegate * sAppDelegate) chip::Server::GetInstance().Init(initParams); #if CHIP_DEVICE_CONFIG_ENABLE_WIFI - sWiFiNetworkCommissioningInstance.Init(); #ifdef CONFIG_ENABLE_CHIP_SHELL chip::Shell::SetWiFiDriver(&(chip::DeviceLayer::NetworkCommissioning::ESPWiFiDriver::GetInstance())); #endif #endif -#if CHIP_DEVICE_CONFIG_ENABLE_ETHERNET - sEthernetNetworkCommissioningInstance.Init(); -#endif + #if CHIP_DEVICE_CONFIG_ENABLE_THREAD if (chip::DeviceLayer::ConnectivityMgr().IsThreadProvisioned() && (chip::Server::GetInstance().GetFabricTable().FabricCount() != 0)) diff --git a/examples/shell/esp32/sdkconfig.defaults b/examples/shell/esp32/sdkconfig.defaults index a77fbf41167c0c..efd5d235a1cf54 100644 --- a/examples/shell/esp32/sdkconfig.defaults +++ b/examples/shell/esp32/sdkconfig.defaults @@ -41,3 +41,6 @@ CONFIG_ENABLE_CHIP_SHELL=y # Enable HKDF in mbedtls CONFIG_MBEDTLS_HKDF_C=y + +# Disable CHIP data model +CONFIG_ENABLE_CHIP_DATA_MODEL=n diff --git a/src/platform/ESP32/CHIPDevicePlatformConfig.h b/src/platform/ESP32/CHIPDevicePlatformConfig.h index e347df109d1e3a..dc4f380c262fb5 100644 --- a/src/platform/ESP32/CHIPDevicePlatformConfig.h +++ b/src/platform/ESP32/CHIPDevicePlatformConfig.h @@ -50,6 +50,9 @@ #ifdef CONFIG_ENABLE_MATTER_OVER_THREAD #define CHIP_DEVICE_CONFIG_ENABLE_THREAD CONFIG_ENABLE_MATTER_OVER_THREAD +#ifndef CONFIG_THREAD_NETWORK_COMMISSIONING_DRIVER +#define _NO_NETWORK_COMMISSIONING_DRIVER_ 1 +#endif #else #define CHIP_DEVICE_CONFIG_ENABLE_THREAD 0 #endif // CONFIG_ENABLE_MATTER_OVER_THREAD @@ -164,3 +167,15 @@ #define CHIP_DEVICE_CONFIG_BG_TASK_PRIORITY CONFIG_BG_CHIP_TASK_PRIORITY #define CHIP_DEVICE_CONFIG_BG_MAX_EVENT_QUEUE_SIZE CONFIG_BG_MAX_EVENT_QUEUE_SIZE #define CHIP_DEVICE_CONFIG_BG_TASK_STACK_SIZE CONFIG_BG_CHIP_TASK_STACK_SIZE + +#ifdef CONFIG_WIFI_NETWORK_COMMISSIONING_DRIVER +#define CHIP_DEVICE_CONFIG_WIFI_NETWORK_DRIVER CONFIG_WIFI_NETWORK_COMMISSIONING_DRIVER +#else +#define CHIP_DEVICE_CONFIG_WIFI_NETWORK_DRIVER 0 +#endif // CONFIG_WIFI_NETWORK_COMMISSIONING_DRIVER + +#ifdef CONFIG_ETHERNET_NETWORK_COMMISSIONING_DRIVER +#define CHIP_DEVICE_CONFIG_ETHERNET_NETWORK_DRIVER CONFIG_ETHERNET_NETWORK_COMMISSIONING_DRIVER +#else +#define CHIP_DEVICE_CONFIG_ETHERNET_NETWORK_DRIVER 0 +#endif // CONFIG_ETHERNET_NETWORK_COMMISSIONING_DRIVER diff --git a/src/platform/ESP32/ConnectivityManagerImpl.cpp b/src/platform/ESP32/ConnectivityManagerImpl.cpp index 85146257ebe985..2b042a5d5c2fcb 100644 --- a/src/platform/ESP32/ConnectivityManagerImpl.cpp +++ b/src/platform/ESP32/ConnectivityManagerImpl.cpp @@ -39,9 +39,11 @@ #include #endif +#include #include #include #include +#include #include using namespace ::chip; @@ -53,6 +55,18 @@ namespace chip { namespace DeviceLayer { ConnectivityManagerImpl ConnectivityManagerImpl::sInstance; + +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI && CHIP_DEVICE_CONFIG_WIFI_NETWORK_DRIVER +app::Clusters::NetworkCommissioning::Instance + sWiFiNetworkCommissioningInstance(CONFIG_WIFI_NETWORK_ENDPOINT_ID /* Endpoint Id */, + &(NetworkCommissioning::ESPWiFiDriver::GetInstance())); +#endif + +#if CHIP_DEVICE_CONFIG_ENABLE_ETHERNET && CHIP_DEVICE_CONFIG_ETHERNET_NETWORK_DRIVER +app::Clusters::NetworkCommissioning::Instance + sEthernetNetworkCommissioningInstance(CONFIG_ETHERNET_NETWORK_ENDPOINT_ID /* Endpoint Id */, + &(NetworkCommissioning::ESPEthernetDriver::GetInstance())); +#endif // ==================== ConnectivityManager Platform Internal Methods ==================== CHIP_ERROR ConnectivityManagerImpl::_Init() @@ -62,10 +76,30 @@ CHIP_ERROR ConnectivityManagerImpl::_Init() #endif #if CHIP_DEVICE_CONFIG_ENABLE_WIFI InitWiFi(); +#if CHIP_DEVICE_CONFIG_WIFI_NETWORK_DRIVER + sWiFiNetworkCommissioningInstance.Init(); +#endif // CHIP_DEVICE_CONFIG_WIFI_NETWORK_DRIVER #endif #if CHIP_DEVICE_CONFIG_ENABLE_ETHERNET InitEthernet(); +#if CHIP_DEVICE_CONFIG_ETHERNET_NETWORK_DRIVER + sEthernetNetworkCommissioningInstance.Init(); +#endif // CHIP_DEVICE_CONFIG_ETHERNET_NETWORK_DRIVER #endif + +#if defined(CONFIG_WIFI_NETWORK_ENDPOINT_ID) && defined(CONFIG_THREAD_NETWORK_ENDPOINT_ID) + static_assert(CONFIG_WIFI_NETWORK_ENDPOINT_ID != CONFIG_THREAD_NETWORK_ENDPOINT_ID, + "Wi-Fi network endpoint id and Thread network endpoint id should not be the same."); +#endif +#if defined(CONFIG_WIFI_NETWORK_ENDPOINT_ID) && defined(CONFIG_ETHERNET_NETWORK_ENDPOINT_ID) + static_assert(CONFIG_WIFI_NETWORK_ENDPOINT_ID != CONFIG_ETHERNET_NETWORK_ENDPOINT_ID, + "Wi-Fi network endpoint id and Ethernet network endpoint id should not be the same."); +#endif +#if defined(CONFIG_THREAD_NETWORK_ENDPOINT_ID) && defined(CONFIG_ETHERNET_NETWORK_ENDPOINT_ID) + static_assert(CONFIG_THREAD_NETWORK_ENDPOINT_ID != CONFIG_ETHERNET_NETWORK_ENDPOINT_ID, + "Thread network endpoint id and Ethernet network endpoint id should not be the same."); +#endif + return CHIP_NO_ERROR; } From 9ef5bbfd520d8847970e59b343ae50a655463749 Mon Sep 17 00:00:00 2001 From: jamesharrow <93921463+jamesharrow@users.noreply.github.com> Date: Fri, 26 Jul 2024 12:24:38 +0100 Subject: [PATCH 29/49] EVSE add get set clear targets support (#31407) * De-alphabetize list of files to avoid breaking GH action * Semi-realphabetize? * Restore strangely dropped events * Better BitMask handling * Change min/max on electrical measurements to be decimal instead of hex * Rename meas-and-sense to measurement-and-sensing.xml * Remove seemingly superfluous attribute requirements on Descriptor cluster on Electrical Measurement * Updates to electrical-power-measurement-server based on comments * Remove defaults from MeasurementAccuracyRangeStruct to match spec update * Restore side="server" to events * Move common enums and bitmaps to detail:: instead of detail::Enums and detail::Bitmaps; remove superfluous using statement * Assign ID to Electrical Sensor device type * Removed EPM and EEM from Root Node Device * Restyled formatting is different than clang-format * Re-add FeatureMap to attributeAccessInterfaceAttributes for EEM and EPM * Regen after merge * Added electrical-energy-measurement-server to CMakelist to fix linker issue. * Lock client on Electrical Sensor device type * Remove unneeded using statement now that Enums are in detail:: * Check for null iterators and error * Switch to ResourceExhausted from CHIP_ERROR_INTERNAL * Re-enabled EEM in energy management app and regen all after previous merge * Some refactoring to add EPM Instance into the EVSEManufacturer class to clean up containment. Added ability to fake voltage, power and current to the TE triggers. * Missed one file. * Fixed crash due to unassigned dg pointer. Power/Voltage/Current faking working too. * Touch file since restyled crashed * Restyled by gn * Restyled by isort * Add stub for EPM cluster * Reverted whitespace change * Did regen_all after merge from master to resolve conflicts. * Put back line of clusters which somehow got deleted accidentally. * Remerged ZAP file and regen all after resolving conflicts. * Fixes for Python tests * Correct name for Ember init callback * Formatting * Sync optional attributes list with .zap file for EPM * Add missing features to EPM stub * Revert FeatureMap in attributeAccessInterfaceAttributes * Allow FeatureMap in EEM constructor; add all-clusters-app EEM stub * Forgot zcl-with-test-extensions * Unregister EEM attribute access in destructor * Remove redundant returns to keep clang-tidy happy * Fix for issue mentioned in code review on EEM cluster limiting the number of endpoints it allows. * Refactoring to have a common EvseMain across all platforms to avoid making changes in multiple places * Added electrical-power-measurement-server to ESP32 CMakeLists.txt * Updated Matter device types to add EVSE * Open and saved energy-management-app.zap and regen_all * Removed duplicate ElectricalEnergyMeasurment class which was accidentally merged in. Fixed issue raised about ElectricalEnergyMeasurement array size not working on bridges. * Added support for test event triggers and handling of reading events into matter_testing_support. * Made TC_EEVSE_Utils.py use the matter_testing_support instead of its own local copy. * Restyled by isort * Cherry pick from Tweaks to EVSE Test plans (Issue #31460) * Changed the random value generation to make the values +/- and handle sign conversion to avoid compile warnings * Enabled cumulativeEnergyExported in Energy-management-app. * Added initial electrical power measurement 2.2 test case * Changed copyright date * Code review comment fixes. * Changed to c++ style cast * Fixed trailing whitepace * Added support for testing read of EEM attributes and change of values * Corrected EPM references in TC_EEM_2_2. Added TC_EEM_2_3 * Added periodic energy reporting, and new cumulativeEnergyReset attribute into energy-management-app.zap * Added periodic energy reading support and TC_EEM_2_3 to 2_5. * Python removed unused logging and EventChangeCallback * Updates to align to test plan PR #3949 * Added initial EEM_2_1 test script. * Added example of setting EEM Accuracy and EEM CumulativeEnergyReset structure - TC_EEM_2_1 now passes * Restyled by whitespace * Restyled * Removed extra spaces in TC_EEM_2_1.py * Removed unused EnergyManagementManager.cpp/.h * Fixed PowerMode = kAc * Initial TC_EPM_2_1.py script * Restyled by isort * Merged TC_EEVSE tests back in * Initialized NumberOfMeasurementTypes * Added EEM 2.1,2.2,2.3,2.4,2.5 and EPM 2.1,2.2 into CI workflow tests.yaml * Interim state - partially refactored how Measurement Structs are encoded similar to how ModeBase clusters are implemented. Needs tidy up. Will break all-clusters for now * Removed SetNumberOfMeasurementTypes since this can be derived from the ArraySize(kMeasurementAccuracies). Added more stringent checking in test script of measurementTypes and ranges. * Completed TC_EPM_2_1.py script * Corrected test plan spec reference. * Test EPM_2_1 now runs and passes. Allows checking that attributes are supported, and skips test if not. Validation of values ignores Nulls (which are allowed). Turned on Ranges attribute. * Revert unintended change to tests.yaml * Python test case code-review updates * Removed old range iterator. * Fixed lint issues and adjusted timings to match the test plan pr. * Fixed all-clusters electrical-power-measurement cluster by using the energy-management-app/common Delegate * Implemented HarmonicCurrents and HarmonicStructs (to return empty list for now) * Updated TC_EEVSE_2_3.py from more up to date branch. * Added basic set/get/clear targets framework based on earlier work. Not working * Removed files that were recently deleted upstream * Corrected PICS in TC_EEVSE_2_3.py and copyright date * Interim checkin with GetTargets not working * Initial GetTargets working with a constant static array to demonstrate the command response can be encoded ok * Removed unused EnergyEvseManager.cpp, Added EnergyEvseTargetStore.cpp * Added EVSETargets to DefaultStorageKeyAllocator.h * Reverted incorrect removal of EnergyEvseManager.cpp * Added override into EnergyEvseDelegate which resulted in strange errors on some platforms. * Added basic infrastructure for storing targets and reading them back. Compiles and runs * Store working * Store and Load seem to work. * Fixed Status change caused by upstream changes * Added helper function to compute day of week bitmap * Fix to evse-stub in all-clusters to add gEvseTargetsDelegate * Fixed build error on some platforms: cast of dayOfWeek to uint8_t * Refactoring of GetTargets command to build the response using dynamic memory * Updated evse-server.cpp/.h * Tidy up of unused CommonIterator in electrical-power-measurement-server.h * Get Targets reading back from file working * Starting to clean up - ran through Valgrind to check for memory leaks. * More clean up * Added Clear Targets support and initial implementation of SetTargets * Fixed crash when erasing entries - test reported PASS! * Attempt to fix Darwin complaint about unsigned int cast * Added logging of get targets response * Fixed platform specific logging compilation issue * Clean up of unused code * More clean up * Removed unused variable - Darwin check * clang checker updates * Refactored code to fix missing added energy since the TargetSoC could be optional and calling Next() would skip AddedEnergy * Added checking of GetTargetsResponse - PASSES * Almost working but need to resolve the Charging current and start time calculation in some scenarios. * Fixed PEP8 lint errors in TC_EVSE_2_3.py and TC_EEVSE_Utils.py taking advantage of new TestStep 3rd arg * Restyled by isort * Fixed ChipLogDetail %d errors on some platforms with a static_cast * Fixes in EVSE FindTargets to remove signed/unsigned comparison. Calculation of charging time was out by factor of 10. * Fixed TC_EEVSE_2_3.py to match test plan. Fixed EVSE FindNextTarget to handle targets if searching for future days. Now passes tests. * Fixed FindNextTarget to use epoch_s for NextChargeTargetTime and NextChargeStartTime with associated TC_EEVSE_2_3.py changes. Fixed bug when TargetSoC = 100% caused start time to be reported incorrectly. * Fix: When EVSE is not plugged in, or not enabled for charge we shouldn't compute a schedule. Also add callbacks to update schedule if the state changes. * Updated TC_EEM, TC_EPM, TC_EEVSE to take advantage of the new TestStep 3rd element. * Address comments from Boris Zbarsky * Fix lint error by adding entry for src/app/clusters/energy-evse-server/energy-evse-server.h * Revert "Fix lint error by adding entry for src/app/clusters/energy-evse-server/energy-evse-server.h" This reverts commit 7a60876809ce0af89a9edb00909122a27f922a5e. * Rework HandleGetTargets following https://github.com/project-chip/connectedhomeip/pull/33736 * Address comments from Boris Zbarsky * Update TC_EEVSE_2_3 to align with latest test spec * Remove the use of vector * Get all targets building again * Catch up from master + fix some lint and build errors * Restyled by whitespace * Restyled by clang-format * Restyled by autopep8 * Restyled by isort * Fixing regession-build error * Restyled by clang-format * Fixing regession-build error * Changed logic to not return NextChargeRequiredEnergy if not plugged in or not enabled for charging. * Removed max 80A current limit which aligns to the spec. Who knows how many Amps chargers will have in the future! * Bug fix - When disabling the mMaximumChargingCurrentLimitFromCommand was not being updated, so we didn't get attribute updates for MaximumChargeCurrent when toggling Enable-Disable-Enable. * Being into line with latest EVSE test spec although TODOs around the time of use trigger * Load the targets in EnergyEvseInit() * Made EvseTargetsEntryType -> evseTargetsEntryType (since it is a variable not a type) * Reformatting and correcting the cluster PICS to be EEVSE not DEM. * Reverted changes to python test cases which don't belong in this PR - See PR#34243 (makes this PR 10 files smaller). * Address review comments from James Harrow * Address review comments from James Harrow * Add energy unit tests * Fix test case to align with PR10027 * Move the EVSE targets key to be a private member of EnergyEvseTargetsStore * Move EvseTargetStore test harness to examples/energy-management-app/energy-management-common/tests * Move example tests to examples/BUILD.gn * Restyled by clang-format * Restyled by gn * Add a mechanism to enable the targets persistent data to be delete when the last fabric is deleted * Fix unused variable compilation issues * Restyled by whitespace * Restyled by clang-format * Adde TC_EEVSE_2_3.py to CI testing. * Added new test-runner info to top of Python TC_EEVSE_2_3.py * Remove assert from BUILD.gn so test harness can build * Put BEGIN CI TEST ARGUMENTS around runner test arguments * Fix compilation problem in unit test * Try and fix the zephyr build problems * Restyled by gn * Try and fix the zephyr build problems * Try and fix the zephyr build problems * Try and fix the zephyr build problems * Restyled by gn * Start addressing comments from Andrei * Continuing addressing comments from Andrei * Continuing addressing comments from Andrei * Restyled by clang-format * Fix a problem with the return code spotted by Boris * Update examples/energy-management-app/energy-management-common/src/ChargingTargetsMemMgr.cpp Co-authored-by: Andrei Litvin * Update examples/energy-management-app/energy-management-common/src/ChargingTargetsMemMgr.cpp Co-authored-by: Andrei Litvin * Update examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp Co-authored-by: Andrei Litvin * Update with further review comments from Andrei * Update with further review comments from Andrei * Update with further review comments from Andrei * Get all targets building * Restyled by whitespace * Restyled by clang-format * Update with further review comments from Andrei * Address further review comments from Andrei * Restyled by clang-format * Complete rename of Reset to ChargingTargetsMemMgr::PrepareDaySchedule * Fix potential memory leak * Add check in ChargingTargetsMemMgr::PrepareDaySchedule against a bad chargingTargetSchedulesIdx * Restyled by clang-format * Address final comments from Andrei * Restyled by gn * Removed whitespace in openiotsdk/CMakelists.txt * Fix merge issue * Fix CI build issue --------- Co-authored-by: Hasty Granbery Co-authored-by: Restyled.io Co-authored-by: pcoleman Co-authored-by: PeterC1965 <101805108+PeterC1965@users.noreply.github.com> Co-authored-by: Andrei Litvin --- .github/workflows/tests.yaml | 1 + BUILD.gn | 2 + examples/BUILD.gn | 32 ++ .../src/energy-evse-stub.cpp | 12 +- .../all-clusters-app/ameba/chip_main.cmake | 4 +- examples/all-clusters-app/asr/BUILD.gn | 2 + .../all-clusters-app/cc13x2x7_26x2x7/BUILD.gn | 0 .../all-clusters-app/cc13x4_26x4/BUILD.gn | 2 + .../all-clusters-app/infineon/psoc6/BUILD.gn | 2 + examples/all-clusters-app/linux/BUILD.gn | 2 + examples/all-clusters-app/mbed/CMakeLists.txt | 4 +- .../nrfconnect/CMakeLists.txt | 4 +- examples/all-clusters-app/nxp/mw320/BUILD.gn | 2 + .../openiotsdk/CMakeLists.txt | 4 +- .../all-clusters-app/telink/CMakeLists.txt | 4 +- examples/all-clusters-app/tizen/BUILD.gn | 2 + .../include/ChargingTargetsMemMgr.h | 156 ++++++ .../include/EVSEManufacturerImpl.h | 6 + .../include/EnergyEvseDelegateImpl.h | 72 ++- .../include/EnergyEvseTargetsStore.h | 123 +++++ .../src/ChargingTargetsMemMgr.cpp | 193 +++++++ .../src/EVSEManufacturerImpl.cpp | 223 +++++++- .../src/EnergyEvseDelegateImpl.cpp | 277 +++++++--- .../src/EnergyEvseEventTriggers.cpp | 17 +- .../src/EnergyEvseMain.cpp | 27 +- .../src/EnergyEvseManager.cpp | 11 + .../src/EnergyEvseTargetsStore.cpp | 489 ++++++++++++++++++ .../energy-management-common/tests/BUILD.gn | 44 ++ .../tests/TestEvseTargetsStorage.cpp | 219 ++++++++ examples/energy-management-app/linux/BUILD.gn | 21 +- examples/energy-management-app/linux/main.cpp | 6 +- examples/platform/linux/BUILD.gn | 8 +- examples/shell/shell_common/BUILD.gn | 2 + src/BUILD.gn | 9 + .../electrical-energy-measurement-server.cpp | 1 + .../electrical-power-measurement-server.h | 3 - .../EnergyEvseTestEventTriggerHandler.h | 4 + .../energy-evse-server/energy-evse-server.cpp | 137 ++++- .../energy-evse-server/energy-evse-server.h | 37 +- src/python_testing/TC_EEVSE_2_3.py | 454 ++++++++++++++++ src/python_testing/TC_EEVSE_Utils.py | 71 ++- 41 files changed, 2568 insertions(+), 121 deletions(-) create mode 100644 examples/BUILD.gn create mode 100644 examples/all-clusters-app/cc13x2x7_26x2x7/BUILD.gn create mode 100644 examples/energy-management-app/energy-management-common/include/ChargingTargetsMemMgr.h create mode 100644 examples/energy-management-app/energy-management-common/include/EnergyEvseTargetsStore.h create mode 100644 examples/energy-management-app/energy-management-common/src/ChargingTargetsMemMgr.cpp create mode 100644 examples/energy-management-app/energy-management-common/src/EnergyEvseTargetsStore.cpp create mode 100644 examples/energy-management-app/energy-management-common/tests/BUILD.gn create mode 100644 examples/energy-management-app/energy-management-common/tests/TestEvseTargetsStorage.cpp create mode 100644 src/python_testing/TC_EEVSE_2_3.py diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index d17e5db8a2a541..51832814ac548d 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -527,6 +527,7 @@ jobs: scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_EEM_2_4.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_EEM_2_5.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_EEVSE_2_2.py' + scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_EEVSE_2_3.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_EEVSE_2_4.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_EEVSE_2_5.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_EPM_2_1.py' diff --git a/BUILD.gn b/BUILD.gn index ddd893a4ca49d8..ecf710efdf8a15 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -146,6 +146,8 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") { if (chip_build_tests) { deps += [ "//src:tests" ] + deps += [ "//examples:example_tests" ] + if (current_os == "android" && current_toolchain == default_toolchain) { deps += [ "${chip_root}/build/chip/java/tests:java_build_test" ] } diff --git a/examples/BUILD.gn b/examples/BUILD.gn new file mode 100644 index 00000000000000..a8cb18422949e2 --- /dev/null +++ b/examples/BUILD.gn @@ -0,0 +1,32 @@ +# Copyright (c) 2020-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_root}/config/compiler/compiler.gni") +import("${chip_root}/build/chip/tests.gni") +import("${chip_root}/src/platform/device.gni") + +if (chip_build_tests) { + import("${chip_root}/build/chip/chip_test_group.gni") + + chip_test_group("example_tests") { + deps = [] + tests = [] + if (chip_device_platform == "linux" && current_os == "linux") { + tests += [ "${chip_root}/examples/energy-management-app/energy-management-common/tests" ] + } + } +} diff --git a/examples/all-clusters-app/all-clusters-common/src/energy-evse-stub.cpp b/examples/all-clusters-app/all-clusters-common/src/energy-evse-stub.cpp index 72fe588995784e..6acc81ac285b65 100644 --- a/examples/all-clusters-app/all-clusters-common/src/energy-evse-stub.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/energy-evse-stub.cpp @@ -23,14 +23,24 @@ using namespace chip::app::Clusters; using namespace chip::app::Clusters::EnergyEvse; static std::unique_ptr gDelegate; +static std::unique_ptr gEvseTargetsDelegate; static std::unique_ptr gInstance; void emberAfEnergyEvseClusterInitCallback(chip::EndpointId endpointId) { VerifyOrDie(endpointId == 1); // this cluster is only enabled for endpoint 1. + VerifyOrDie(!gDelegate); + VerifyOrDie(!gEvseTargetsDelegate); VerifyOrDie(!gInstance); - gDelegate = std::make_unique(); + gEvseTargetsDelegate = std::make_unique(); + if (!gEvseTargetsDelegate) + { + ChipLogError(AppServer, "Failed to allocate memory for EvseTargetsDelegate"); + return; + } + + gDelegate = std::make_unique(*gEvseTargetsDelegate); if (gDelegate) { gInstance = std::make_unique( diff --git a/examples/all-clusters-app/ameba/chip_main.cmake b/examples/all-clusters-app/ameba/chip_main.cmake index ce557a2bffaf30..e6e9ee19a87394 100644 --- a/examples/all-clusters-app/ameba/chip_main.cmake +++ b/examples/all-clusters-app/ameba/chip_main.cmake @@ -187,13 +187,15 @@ list( ${chip_dir}/examples/microwave-oven-app/microwave-oven-common/src/microwave-oven-device.cpp - ${chip_dir}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp + ${chip_dir}/examples/energy-management-app/energy-management-common/src/ChargingTargetsMemMgr.cpp ${chip_dir}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp ${chip_dir}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp ${chip_dir}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp ${chip_dir}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp ${chip_dir}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp ${chip_dir}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp + ${chip_dir}/examples/energy-management-app/energy-management-common/src/EnergyEvseTargetsStore.cpp + ${chip_dir}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp ${chip_dir}/examples/platform/ameba/route_hook/ameba_route_hook.c ${chip_dir}/examples/platform/ameba/route_hook/ameba_route_table.c diff --git a/examples/all-clusters-app/asr/BUILD.gn b/examples/all-clusters-app/asr/BUILD.gn index 6c9d334a1ed6c2..fc7ad9037b99de 100644 --- a/examples/all-clusters-app/asr/BUILD.gn +++ b/examples/all-clusters-app/asr/BUILD.gn @@ -82,12 +82,14 @@ asr_executable("clusters_app") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/smco-stub.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-modes-manager.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/ChargingTargetsMemMgr.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseTargetsStore.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp", "${examples_plat_dir}/ButtonHandler.cpp", "${examples_plat_dir}/CHIPDeviceManager.cpp", diff --git a/examples/all-clusters-app/cc13x2x7_26x2x7/BUILD.gn b/examples/all-clusters-app/cc13x2x7_26x2x7/BUILD.gn new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/examples/all-clusters-app/cc13x4_26x4/BUILD.gn b/examples/all-clusters-app/cc13x4_26x4/BUILD.gn index 45efe2d45e9bce..c1cb81a9722f7d 100644 --- a/examples/all-clusters-app/cc13x4_26x4/BUILD.gn +++ b/examples/all-clusters-app/cc13x4_26x4/BUILD.gn @@ -72,12 +72,14 @@ ti_simplelink_executable("all-clusters-app") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/smco-stub.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-modes-manager.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/ChargingTargetsMemMgr.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseTargetsStore.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp", "${chip_root}/examples/providers/DeviceInfoProviderImpl.cpp", "${chip_root}/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.cpp", diff --git a/examples/all-clusters-app/infineon/psoc6/BUILD.gn b/examples/all-clusters-app/infineon/psoc6/BUILD.gn index 2c0b0a6fee7dfa..9be06891342d7c 100644 --- a/examples/all-clusters-app/infineon/psoc6/BUILD.gn +++ b/examples/all-clusters-app/infineon/psoc6/BUILD.gn @@ -124,12 +124,14 @@ psoc6_executable("clusters_app") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/smco-stub.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-modes-manager.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/ChargingTargetsMemMgr.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseTargetsStore.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp", "${examples_plat_dir}/LEDWidget.cpp", "${examples_plat_dir}/init_psoc6Platform.cpp", diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index ca1149b25e7891..3564153a482cca 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -59,12 +59,14 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/tcc-mode.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/water-heater-mode.cpp", "${chip_root}/examples/all-clusters-app/linux/diagnostic-logs-provider-delegate-impl.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/ChargingTargetsMemMgr.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseTargetsStore.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/device-energy-management-mode.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/energy-evse-mode.cpp", diff --git a/examples/all-clusters-app/mbed/CMakeLists.txt b/examples/all-clusters-app/mbed/CMakeLists.txt index 9b490ff687d1e3..427517aff9f8e1 100644 --- a/examples/all-clusters-app/mbed/CMakeLists.txt +++ b/examples/all-clusters-app/mbed/CMakeLists.txt @@ -72,13 +72,15 @@ target_sources(${APP_TARGET} PRIVATE ${ALL_CLUSTERS_COMMON}/src/smco-stub.cpp ${ALL_CLUSTERS_COMMON}/src/static-supported-modes-manager.cpp ${ALL_CLUSTERS_COMMON}/src/static-supported-temperature-levels.cpp - ${ENERGY_MANAGEMENT_COMMON}/src/EnergyTimeUtils.cpp + ${ENERGY_MANAGEMENT_COMMON}/src/ChargingTargetsMemMgr.cpp ${ENERGY_MANAGEMENT_COMMON}/src/DeviceEnergyManagementDelegateImpl.cpp ${ENERGY_MANAGEMENT_COMMON}/src/DeviceEnergyManagementManager.cpp ${ENERGY_MANAGEMENT_COMMON}/src/EVSEManufacturerImpl.cpp ${ENERGY_MANAGEMENT_COMMON}/src/ElectricalPowerMeasurementDelegate.cpp ${ENERGY_MANAGEMENT_COMMON}/src/EnergyEvseDelegateImpl.cpp ${ENERGY_MANAGEMENT_COMMON}/src/EnergyEvseManager.cpp + ${ENERGY_MANAGEMENT_COMMON}/src/EnergyEvseTargetsStore.cpp + ${ENERGY_MANAGEMENT_COMMON}/src/EnergyTimeUtils.cpp ) chip_configure_data_model(${APP_TARGET} diff --git a/examples/all-clusters-app/nrfconnect/CMakeLists.txt b/examples/all-clusters-app/nrfconnect/CMakeLists.txt index c290003532a431..e11dcbd14356fc 100644 --- a/examples/all-clusters-app/nrfconnect/CMakeLists.txt +++ b/examples/all-clusters-app/nrfconnect/CMakeLists.txt @@ -62,13 +62,15 @@ target_sources(app PRIVATE ${ALL_CLUSTERS_COMMON_DIR}/src/air-quality-instance.cpp ${ALL_CLUSTERS_COMMON_DIR}/src/concentration-measurement-instances.cpp ${ALL_CLUSTERS_COMMON_DIR}/src/resource-monitoring-delegates.cpp - ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EnergyTimeUtils.cpp + ${ENERGY_MANAGEMENT_COMMON_DIR}/src/ChargingTargetsMemMgr.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/DeviceEnergyManagementDelegateImpl.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/DeviceEnergyManagementManager.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EVSEManufacturerImpl.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/ElectricalPowerMeasurementDelegate.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EnergyEvseDelegateImpl.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EnergyEvseManager.cpp + ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EnergyEvseTargetsStore.cpp + ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EnergyTimeUtils.cpp ${NRFCONNECT_COMMON}/util/LEDWidget.cpp) chip_configure_data_model(app diff --git a/examples/all-clusters-app/nxp/mw320/BUILD.gn b/examples/all-clusters-app/nxp/mw320/BUILD.gn index 83877a175993df..e5e3671143ee99 100644 --- a/examples/all-clusters-app/nxp/mw320/BUILD.gn +++ b/examples/all-clusters-app/nxp/mw320/BUILD.gn @@ -89,12 +89,14 @@ mw320_executable("shell_mw320") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/smco-stub.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-modes-manager.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/ChargingTargetsMemMgr.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseTargetsStore.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp", "${chip_root}/src/lib/shell/streamer_mw320.cpp", "binding-handler.cpp", diff --git a/examples/all-clusters-app/openiotsdk/CMakeLists.txt b/examples/all-clusters-app/openiotsdk/CMakeLists.txt index 1395cf170a8b79..9db62a3df783a1 100644 --- a/examples/all-clusters-app/openiotsdk/CMakeLists.txt +++ b/examples/all-clusters-app/openiotsdk/CMakeLists.txt @@ -65,13 +65,15 @@ target_sources(${APP_TARGET} ${ALL_CLUSTERS_COMMON}/src/resource-monitoring-delegates.cpp ${ALL_CLUSTERS_COMMON}/src/static-supported-modes-manager.cpp ${ALL_CLUSTERS_COMMON}/src/binding-handler.cpp - ${ENERGY_MANAGEMENT_COMMON}/src/EnergyTimeUtils.cpp + ${ENERGY_MANAGEMENT_COMMON}/src/ChargingTargetsMemMgr.cpp ${ENERGY_MANAGEMENT_COMMON}/src/DeviceEnergyManagementDelegateImpl.cpp ${ENERGY_MANAGEMENT_COMMON}/src/DeviceEnergyManagementManager.cpp ${ENERGY_MANAGEMENT_COMMON}/src/EVSEManufacturerImpl.cpp ${ENERGY_MANAGEMENT_COMMON}/src/ElectricalPowerMeasurementDelegate.cpp ${ENERGY_MANAGEMENT_COMMON}/src/EnergyEvseDelegateImpl.cpp ${ENERGY_MANAGEMENT_COMMON}/src/EnergyEvseManager.cpp + ${ENERGY_MANAGEMENT_COMMON}/src/EnergyEvseTargetsStore.cpp + ${ENERGY_MANAGEMENT_COMMON}/src/EnergyTimeUtils.cpp ) target_link_libraries(${APP_TARGET} diff --git a/examples/all-clusters-app/telink/CMakeLists.txt b/examples/all-clusters-app/telink/CMakeLists.txt index 149b38e3528cce..675ea4d9eb7e74 100644 --- a/examples/all-clusters-app/telink/CMakeLists.txt +++ b/examples/all-clusters-app/telink/CMakeLists.txt @@ -51,13 +51,15 @@ target_sources(app PRIVATE ${ALL_CLUSTERS_COMMON_DIR}/src/device-energy-management-stub.cpp ${ALL_CLUSTERS_COMMON_DIR}/src/energy-evse-stub.cpp ${ALL_CLUSTERS_COMMON_DIR}/src/resource-monitoring-delegates.cpp - ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EnergyTimeUtils.cpp + ${ENERGY_MANAGEMENT_COMMON_DIR}/src/ChargingTargetsMemMgr.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/DeviceEnergyManagementDelegateImpl.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/DeviceEnergyManagementManager.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EVSEManufacturerImpl.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/ElectricalPowerMeasurementDelegate.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EnergyEvseDelegateImpl.cpp ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EnergyEvseManager.cpp + ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EnergyEvseTargetsStore.cpp + ${ENERGY_MANAGEMENT_COMMON_DIR}/src/EnergyTimeUtils.cpp ${TELINK_COMMON}/common/src/mainCommon.cpp ${TELINK_COMMON}/common/src/AppTaskCommon.cpp ${TELINK_COMMON}/util/src/LEDManager.cpp diff --git a/examples/all-clusters-app/tizen/BUILD.gn b/examples/all-clusters-app/tizen/BUILD.gn index 9aff82b57b2876..855416c0446923 100644 --- a/examples/all-clusters-app/tizen/BUILD.gn +++ b/examples/all-clusters-app/tizen/BUILD.gn @@ -38,12 +38,14 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/smco-stub.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-modes-manager.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/ChargingTargetsMemMgr.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseTargetsStore.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp", ] diff --git a/examples/energy-management-app/energy-management-common/include/ChargingTargetsMemMgr.h b/examples/energy-management-app/energy-management-common/include/ChargingTargetsMemMgr.h new file mode 100644 index 00000000000000..66009cdd84ca0e --- /dev/null +++ b/examples/energy-management-app/energy-management-common/include/ChargingTargetsMemMgr.h @@ -0,0 +1,156 @@ +/* + * + * 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. + */ + +#pragma once + +#include +#include + +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace EnergyEvse { + +/* + * The full Target data structure defined as: + * + * DataModel::List chargingTargetSchedules; + * + * contains a list of ChargingTargetScheduleStructs which in turn contains a list of ChargingTargetStructs. + * This means that somewhere the following memory needs to be allocated in the case where + * the Target is at its maximum size: + * + * ChargingTargetStruct::Type mDailyChargingTargets[kEvseTargetsMaxNumberOfDays][kEvseTargetsMaxTargetsPerDay] + * + * This is 1680B. + * + * However it is likely the number of chargingTargets configured will be considerably less. To avoid + * allocating the maximum possible Target size, each List is allocated + * separately. This class handles that allocation. + * + * When iterating through the chargingTargetSchedules, an index in this list is kept and the + * ChargingTargetsMemMgr::PrepareDaySchedule must be called so this object knows which day schedule it is tracking. + * This will free any previous memory allocated for the day schedule in this object. + * + * There are then three usage cases: + * + * 1. When loading the Target from persistent storage. In this scenario, it is not known upfront + * how many chargingTargets are associated with this day schedule so ChargingTargetsMemMgr::AddChargingTarget() + * needs to be called as each individual chargingTarget is loaded from persistent data. + * + * Once the chargingTargets for the day schedule have been loaded, ChargingTargetsMemMgr::AllocAndCopy() is + * called to allocate the memory to store the chargingTargets and the chargingTargets are copied. + * + * 2. When updating a Target and a day schedule is unaffected, the chargingTargets associated with + * day schedule need copying. The following should be called: + * + * ChargingTargetsMemMgr::AllocAndCopy(const DataModel::List & chargingTargets) + * + * 3. When in SetTargets, a new list of chargingTargets needs to be added to a day schedule, the following + * should be called: + * + * ChargingTargetsMemMgr::AllocAndCopy(const DataModel::DecodableList & + * chargingTargets) + * + * Having allocated and copied the chargingTargets accordingly, they can be added to a + * DataModel::List(ChargingTargetsMemMgr::GetChargingTargets(), + * ChargingTargetsMemMgr::GetNumDailyChargingTargets()); + * + * All memory allocated by this object is released When the ChargingTargetsMemMgr destructor is called. + * + */ + +class ChargingTargetsMemMgr +{ +public: + ChargingTargetsMemMgr(); + ~ChargingTargetsMemMgr(); + + /** + * @brief This method prepares a new day schedule. Subsequent calls to GetChargingTargets + * and the AllocAndCopy methods below will reference this day schedule. + * + * @param chargingTargetSchedulesIdx - The new day schedule index + */ + void PrepareDaySchedule(uint16_t chargingTargetSchedulesIdx); + + /** + * @brief Called as each individual chargingTarget is loaded from persistent data. + * When loading the Target from persistent storage, it is not known upfront + * how many chargingTargets are associated with this day schedule so + * ChargingTargetsMemMgr::AddChargingTarget() needs to be called as each individual + * chargingTarget is loaded from persistent data. + * + * @param chargingTarget - The chargingTarget that will be added into the current day schedule + */ + void AddChargingTarget(const EnergyEvse::Structs::ChargingTargetStruct::Type & chargingTarget); + + /** + * @brief Called to allocate and copy the chargingTargets added via AddChargingTarget into the + * current day schedule as set by PrepareDaySchedule(). + */ + CHIP_ERROR AllocAndCopy(); + + /** + * @brief Called to allocate and copy the chargingTargets into the current day schedule as set + * set by PrepareDaySchedule(). + * If an attempt is made to add more than kEvseTargetsMaxTargetsPerDay chargingTargets + * for the current day schedule, then the chargingTarget is not added and an error message + * is printed. + * + * @param chargingTargets - The chargingTargets to add into the current day schedule + */ + CHIP_ERROR AllocAndCopy(const DataModel::List & chargingTargets); + + /** + * @brief Called to allocate and copy the chargingTargets into the current day schedule as set + * set by PrepareDaySchedule(). + * + * @param chargingTargets - The chargingTargets to add into the current day schedule + */ + CHIP_ERROR AllocAndCopy(const DataModel::DecodableList & chargingTargets); + + /** + * @brief Returns the list of chargingTargets associated with the current day schedule. + * + * @return The charging targets associated with the current day schedule. + */ + EnergyEvse::Structs::ChargingTargetStruct::Type * GetChargingTargets() const; + + /** + * @brief Returns the number of chargingTargets associated with current day schedule. + * + * @return Returns the number of chargingTargets associated with current day schedule. + */ + uint16_t GetNumDailyChargingTargets() const; + +private: + EnergyEvse::Structs::ChargingTargetStruct::Type * mpListOfDays[kEvseTargetsMaxNumberOfDays]; + EnergyEvse::Structs::ChargingTargetStruct::Type mDailyChargingTargets[kEvseTargetsMaxTargetsPerDay]; + uint16_t mChargingTargetSchedulesIdx; + uint16_t mNumDailyChargingTargets; +}; + +} // namespace EnergyEvse +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/energy-management-app/energy-management-common/include/EVSEManufacturerImpl.h b/examples/energy-management-app/energy-management-common/include/EVSEManufacturerImpl.h index 2faf81046cfdf2..b94220d11b28f3 100644 --- a/examples/energy-management-app/energy-management-common/include/EVSEManufacturerImpl.h +++ b/examples/energy-management-app/energy-management-common/include/EVSEManufacturerImpl.h @@ -129,6 +129,12 @@ class EVSEManufacturer : public DEMManufacturerDelegate */ static void ApplicationCallbackHandler(const EVSECbInfo * cb, intptr_t arg); + /** + * @brief Simple example to demonstrate how an EVSE can compute the start time + * and duration of a charging schedule + */ + CHIP_ERROR ComputeChargingSchedule(); + /** * @brief Allows a client application to initialise the Accuracy, Measurement types etc */ diff --git a/examples/energy-management-app/energy-management-common/include/EnergyEvseDelegateImpl.h b/examples/energy-management-app/energy-management-common/include/EnergyEvseDelegateImpl.h index aed42fa857968a..c43707d9d09589 100644 --- a/examples/energy-management-app/energy-management-common/include/EnergyEvseDelegateImpl.h +++ b/examples/energy-management-app/energy-management-common/include/EnergyEvseDelegateImpl.h @@ -20,6 +20,7 @@ #include "app/clusters/energy-evse-server/energy-evse-server.h" #include +#include #include #include @@ -31,6 +32,10 @@ namespace app { namespace Clusters { namespace EnergyEvse { +// A bitmap of all possible days (the union of the values in +// chip::app::Clusters::EnergyEvse::TargetDayOfWeekBitmap) +constexpr uint8_t kAllTargetDaysMask = 0x7f; + /* Local state machine Events to allow simpler handling of state transitions */ enum EVSEStateMachineEvent { @@ -100,8 +105,11 @@ class EvseSession class EnergyEvseDelegate : public EnergyEvse::Delegate { public: + EnergyEvseDelegate(EvseTargetsDelegate & aDelegate) : EnergyEvse::Delegate() { mEvseTargetsDelegate = &aDelegate; } ~EnergyEvseDelegate(); + EvseTargetsDelegate * GetEvseTargetsDelegate() { return mEvseTargetsDelegate; } + /** * @brief Called when EVSE cluster receives Disable command */ @@ -131,6 +139,37 @@ class EnergyEvseDelegate : public EnergyEvse::Delegate */ Status StartDiagnostics() override; + /** + * @brief Called when EVSE cluster receives the SetTargets command + */ + Status SetTargets( + const DataModel::DecodableList & chargingTargetSchedules) override; + + /** + * @brief Delegate should implement a handler for LoadTargets + * + * This needs to load any stored targets into memory and MUST be called before + * GetTargets is called. + */ + Status LoadTargets() override; + + /** + * @brief Called when EVSE cluster receives the GetTargets command + * + * NOTE: LoadTargets MUST be called GetTargets is called. + */ + Protocols::InteractionModel::Status + GetTargets(DataModel::List & chargingTargetSchedules) override; + + /** + * @brief Called when EVSE cluster receives ClearTargets command + */ + Status ClearTargets() override; + + /* Helper functions for managing targets*/ + Status + ValidateTargets(const DataModel::DecodableList & chargingTargetSchedules); + /** * @brief Called by EVSE Hardware to register a single callback handler */ @@ -153,6 +192,12 @@ class EnergyEvseDelegate : public EnergyEvse::Delegate */ Status ScheduleCheckOnEnabledTimeout(); + /** + * @brief Helper function to get know if the EV is plugged in based on state + * (regardless of if it is actually transferring energy) + */ + bool IsEvsePluggedIn(); + // ----------------------------------------------------------------- // Internal API to allow an EVSE to change its internal state etc Status HwSetMaxHardwareCurrentLimit(int64_t currentmA); @@ -209,9 +254,16 @@ class EnergyEvseDelegate : public EnergyEvse::Delegate /* PREF attributes */ DataModel::Nullable GetNextChargeStartTime() override; + CHIP_ERROR SetNextChargeStartTime(DataModel::Nullable newNextChargeStartTimeUtc); + DataModel::Nullable GetNextChargeTargetTime() override; + CHIP_ERROR SetNextChargeTargetTime(DataModel::Nullable newNextChargeTargetTimeUtc); + DataModel::Nullable GetNextChargeRequiredEnergy() override; + CHIP_ERROR SetNextChargeRequiredEnergy(DataModel::Nullable newNextChargeRequiredEnergyMilliWattH); + DataModel::Nullable GetNextChargeTargetSoC() override; + CHIP_ERROR SetNextChargeTargetSoC(DataModel::Nullable newValue); DataModel::Nullable GetApproximateEVEfficiency() override; CHIP_ERROR SetApproximateEVEfficiency(DataModel::Nullable) override; @@ -229,11 +281,11 @@ class EnergyEvseDelegate : public EnergyEvse::Delegate private: /* Constants */ - static constexpr int kDefaultMinChargeCurrent = 6000; /* 6A */ - static constexpr int kDefaultUserMaximumChargeCurrent = kMaximumChargeCurrent; /* 80A */ - static constexpr int kDefaultRandomizationDelayWindow = 600; /* 600s */ - static constexpr int kMaxVehicleIDBufSize = 32; - static constexpr int kPeriodicCheckIntervalRealTimeClockNotSynced = 30; + static constexpr int kDefaultMinChargeCurrent_mA = 6000; /* 6A */ + static constexpr int kDefaultUserMaximumChargeCurrent_mA = 80000; /* 80A */ + static constexpr int kDefaultRandomizationDelayWindow_sec = 600; /* 600s */ + static constexpr int kMaxVehicleIDBufSize = 32; + static constexpr int kPeriodicCheckIntervalRealTimeClockNotSynced_sec = 30; /* private variables for controlling the hardware - these are not attributes */ int64_t mMaxHardwareCurrentLimit = 0; /* Hardware current limit in mA */ @@ -250,6 +302,7 @@ class EnergyEvseDelegate : public EnergyEvse::Delegate EVSECallbackWrapper mCallbacks = { .handler = nullptr, .arg = 0 }; /* Wrapper to allow callbacks to be registered */ Status NotifyApplicationCurrentLimitChange(int64_t maximumChargeCurrent); Status NotifyApplicationStateChange(); + Status NotifyApplicationChargingPreferencesChange(); Status GetEVSEEnergyMeterValue(ChargingDischargingType meterType, int64_t & aMeterValue); /* Local State machine handling */ @@ -285,11 +338,11 @@ class EnergyEvseDelegate : public EnergyEvse::Delegate DataModel::Nullable mChargingEnabledUntil; // TODO Default to 0 to indicate disabled DataModel::Nullable mDischargingEnabledUntil; // TODO Default to 0 to indicate disabled int64_t mCircuitCapacity = 0; - int64_t mMinimumChargeCurrent = kDefaultMinChargeCurrent; + int64_t mMinimumChargeCurrent = kDefaultMinChargeCurrent_mA; int64_t mMaximumChargeCurrent = 0; int64_t mMaximumDischargeCurrent = 0; - int64_t mUserMaximumChargeCurrent = kDefaultUserMaximumChargeCurrent; // TODO update spec - uint32_t mRandomizationDelayWindow = kDefaultRandomizationDelayWindow; + int64_t mUserMaximumChargeCurrent = kDefaultUserMaximumChargeCurrent_mA; // TODO update spec + uint32_t mRandomizationDelayWindow = kDefaultRandomizationDelayWindow_sec; /* PREF attributes */ DataModel::Nullable mNextChargeStartTime; DataModel::Nullable mNextChargeTargetTime; @@ -309,6 +362,9 @@ class EnergyEvseDelegate : public EnergyEvse::Delegate /* Helper variable to hold meter val since last EnergyTransferStarted event */ int64_t mMeterValueAtEnergyTransferStart; + + /* Targets Delegate */ + EvseTargetsDelegate * mEvseTargetsDelegate = nullptr; }; } // namespace EnergyEvse diff --git a/examples/energy-management-app/energy-management-common/include/EnergyEvseTargetsStore.h b/examples/energy-management-app/energy-management-common/include/EnergyEvseTargetsStore.h new file mode 100644 index 00000000000000..97379f8470e0ae --- /dev/null +++ b/examples/energy-management-app/energy-management-common/include/EnergyEvseTargetsStore.h @@ -0,0 +1,123 @@ +/* + * + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace EnergyEvse { + +class EvseTargetsDelegate : public chip::FabricTable::Delegate +{ +public: + EvseTargetsDelegate(); + ~EvseTargetsDelegate(); + + CHIP_ERROR Init(PersistentStorageDelegate * targetStore); + + /** + * @brief Delegate should implement a handler for LoadTargets + * + * This needs to load any stored targets into memory + */ + CHIP_ERROR LoadTargets(); + + /** + * @brief This returns a reference to the existing targets + */ + const DataModel::List & GetTargets(); + + /** + * @brief Copies a ChargingTargetSchedule into our store + * + * @param [in] an entry from the SetTargets list containing: + * dayOfWeekForSequence and chargingTargets (list) + * + * This routine scans the existing targets to see if we have a day of week + * set that matches the new target dayOfWeek bits. If there is an existing + * matching day then it replaces the days existing targets with the new entry + */ + CHIP_ERROR SetTargets( + const DataModel::DecodableList & chargingTargetSchedulesChanges); + + /** + * @brief This deletes all targets and resets the list to empty + */ + CHIP_ERROR ClearTargets(); + + /** + * Part of the FabricTable::Delegate interface. Gets called when a fabric is deleted, such as on FabricTable::Delete(). + **/ + virtual void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override; + +private: + // This is the upper bound in bytes of the TLV storage required to store the chargingTargetSchedulesList + static uint16_t GetTlvSizeUpperBound(); + + CHIP_ERROR SaveTargets(DataModel::List & chargingTargetSchedulesList); + + // For debug purposes + void PrintTargets(const DataModel::List & chargingTargetSchedules); + +protected: + enum class TargetEntryTag : uint8_t + { + kTargetEntry = 1, + kDayOfWeek = 2, + kChargingTargetsList = 3, + kChargingTargetsStruct = 4, + kTargetTime = 5, + kTargetSoC = 6, + kAddedEnergy = 7, + }; + +private: + // Object to handle the allocation of memory for the chargingTargets + ChargingTargetsMemMgr mChargingTargets; + + // Need memory to store the ChargingTargetScheduleStruct as this is pointed to from a + // List + Structs::ChargingTargetScheduleStruct::Type mChargingTargetSchedulesArray[kEvseTargetsMaxNumberOfDays]; + + // The current Target definition + DataModel::List mChargingTargetSchedulesList; + + // Pointer to the PeristentStorage + PersistentStorageDelegate * mpTargetStore = nullptr; + + // Need a key to store the Charging Preference Targets which is a TLV of list of lists + static constexpr const char * spEvseTargetsKeyName = "g/ev/targ"; +}; + +} // namespace EnergyEvse +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/energy-management-app/energy-management-common/src/ChargingTargetsMemMgr.cpp b/examples/energy-management-app/energy-management-common/src/ChargingTargetsMemMgr.cpp new file mode 100644 index 00000000000000..1642b76882178b --- /dev/null +++ b/examples/energy-management-app/energy-management-common/src/ChargingTargetsMemMgr.cpp @@ -0,0 +1,193 @@ +/* + * + * 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 "ChargingTargetsMemMgr.h" + +using namespace chip; +using namespace chip::app; +using namespace chip::app::DataModel; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::EnergyEvse; + +ChargingTargetsMemMgr::ChargingTargetsMemMgr() : mChargingTargetSchedulesIdx(0), mNumDailyChargingTargets(0) +{ + memset(mpListOfDays, 0, sizeof(mpListOfDays)); +} + +ChargingTargetsMemMgr::~ChargingTargetsMemMgr() +{ + // Free all memory allocated for the charging targets + for (uint16_t idx = 0; idx < kEvseTargetsMaxNumberOfDays; idx++) + { + if (mpListOfDays[idx] != nullptr) + { + chip::Platform::Delete(mpListOfDays[idx]); + } + } +} + +void ChargingTargetsMemMgr::PrepareDaySchedule(uint16_t chargingTargetSchedulesIdx) +{ + // MUST be called for each entry in DataModel::List chargingTargetSchedules + mNumDailyChargingTargets = 0; + + // Should not occur but just to be safe + if (chargingTargetSchedulesIdx >= kEvseTargetsMaxNumberOfDays) + { + ChipLogError(AppServer, "PrepareDaySchedule bad chargingTargetSchedulesIdx %u", chargingTargetSchedulesIdx); + return; + } + + mChargingTargetSchedulesIdx = chargingTargetSchedulesIdx; + + // Free up any memory associated with this targetSchedule + if (mpListOfDays[mChargingTargetSchedulesIdx] != nullptr) + { + chip::Platform::MemoryFree(mpListOfDays[mChargingTargetSchedulesIdx]); + mpListOfDays[mChargingTargetSchedulesIdx] = nullptr; + } +} + +void ChargingTargetsMemMgr::AddChargingTarget(const EnergyEvse::Structs::ChargingTargetStruct::Type & chargingTarget) +{ + if (mNumDailyChargingTargets < kEvseTargetsMaxTargetsPerDay) + { + mDailyChargingTargets[mNumDailyChargingTargets++] = chargingTarget; + } + else + { + ChipLogError(AppServer, "AddChargingTarget: trying to add too many chargingTargets"); + } +} + +EnergyEvse::Structs::ChargingTargetStruct::Type * ChargingTargetsMemMgr::GetChargingTargets() const +{ + return mpListOfDays[mChargingTargetSchedulesIdx]; +} + +uint16_t ChargingTargetsMemMgr::GetNumDailyChargingTargets() const +{ + return mNumDailyChargingTargets; +} + +CHIP_ERROR ChargingTargetsMemMgr::AllocAndCopy() +{ + // NOTE: ChargingTargetsMemMgr::PrepareDaySchedule() must be called as specified in the class comments in + // ChargingTargetsMemMgr.h before this method can be called. + + VerifyOrDie(mpListOfDays[mChargingTargetSchedulesIdx] == nullptr); + + if (mNumDailyChargingTargets > 0) + { + // Allocate the memory first and then use placement new to initialise the memory of each element in the array + mpListOfDays[mChargingTargetSchedulesIdx] = static_cast( + chip::Platform::MemoryAlloc(sizeof(EnergyEvse::Structs::ChargingTargetStruct::Type) * mNumDailyChargingTargets)); + + VerifyOrReturnError(mpListOfDays[mChargingTargetSchedulesIdx] != nullptr, CHIP_ERROR_NO_MEMORY); + + for (uint16_t idx = 0; idx < mNumDailyChargingTargets; idx++) + { + // This will cause the ChargingTargetStruct constructor to be called and this element in the array + new (mpListOfDays[mChargingTargetSchedulesIdx] + idx) EnergyEvse::Structs::ChargingTargetStruct::Type(); + + // Now copy the chargingTarget + mpListOfDays[mChargingTargetSchedulesIdx][idx] = mDailyChargingTargets[idx]; + } + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR +ChargingTargetsMemMgr::AllocAndCopy(const DataModel::List & chargingTargets) +{ + // NOTE: ChargingTargetsMemMgr::PrepareDaySchedule() must be called as specified in the class comments in + // ChargingTargetsMemMgr.h before this method can be called. + + VerifyOrDie(mpListOfDays[mChargingTargetSchedulesIdx] == nullptr); + + mNumDailyChargingTargets = static_cast(chargingTargets.size()); + + if (mNumDailyChargingTargets > 0) + { + // Allocate the memory first and then use placement new to initialise the memory of each element in the array + mpListOfDays[mChargingTargetSchedulesIdx] = static_cast( + chip::Platform::MemoryAlloc(sizeof(EnergyEvse::Structs::ChargingTargetStruct::Type) * chargingTargets.size())); + + VerifyOrReturnError(mpListOfDays[mChargingTargetSchedulesIdx] != nullptr, CHIP_ERROR_NO_MEMORY); + + uint16_t idx = 0; + for (auto & chargingTarget : chargingTargets) + { + // This will cause the ChargingTargetStruct constructor to be called and this element in the array + new (mpListOfDays[mChargingTargetSchedulesIdx] + idx) EnergyEvse::Structs::ChargingTargetStruct::Type(); + + // Now copy the chargingTarget + mpListOfDays[mChargingTargetSchedulesIdx][idx] = chargingTarget; + + idx++; + } + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR +ChargingTargetsMemMgr::AllocAndCopy(const DataModel::DecodableList & chargingTargets) +{ + // NOTE: ChargingTargetsMemMgr::PrepareDaySchedule() must be called as specified in the class comments in + // ChargingTargetsMemMgr.h before this method can be called. + + VerifyOrDie(mpListOfDays[mChargingTargetSchedulesIdx] == nullptr); + + size_t numDailyChargingTargets = 0; + ReturnErrorOnFailure(chargingTargets.ComputeSize(&numDailyChargingTargets)); + + mNumDailyChargingTargets = static_cast(numDailyChargingTargets); + + if (mNumDailyChargingTargets > 0) + { + // Allocate the memory first and then use placement new to initialise the memory of each element in the array + mpListOfDays[mChargingTargetSchedulesIdx] = static_cast( + chip::Platform::MemoryAlloc(sizeof(EnergyEvse::Structs::ChargingTargetStruct::Type) * mNumDailyChargingTargets)); + + VerifyOrReturnError(mpListOfDays[mChargingTargetSchedulesIdx] != nullptr, CHIP_ERROR_NO_MEMORY); + + uint16_t idx = 0; + auto it = chargingTargets.begin(); + while (it.Next()) + { + // Check that the idx is still valid + VerifyOrReturnError(idx < mNumDailyChargingTargets, CHIP_ERROR_INCORRECT_STATE); + + auto & chargingTarget = it.GetValue(); + + // This will cause the ChargingTargetStruct constructor to be called and this element in the array + new (mpListOfDays[mChargingTargetSchedulesIdx] + idx) EnergyEvse::Structs::ChargingTargetStruct::Type(); + + // Now copy the chargingTarget + mpListOfDays[mChargingTargetSchedulesIdx][idx] = chargingTarget; + + idx++; + } + } + + return CHIP_NO_ERROR; +} diff --git a/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp b/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp index 9006477b3662d5..00ced85a9aa2b6 100644 --- a/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp +++ b/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp @@ -19,7 +19,9 @@ #include #include #include +#include #include +#include #include #include @@ -39,7 +41,6 @@ using namespace chip::app::Clusters; using namespace chip::app::Clusters::EnergyEvse; using namespace chip::app::Clusters::ElectricalPowerMeasurement; using namespace chip::app::Clusters::ElectricalEnergyMeasurement; -using namespace chip::app::Clusters::ElectricalEnergyMeasurement::Structs; using namespace chip::app::Clusters::PowerSource; using namespace chip::app::Clusters::PowerSource::Attributes; @@ -115,6 +116,217 @@ CHIP_ERROR EVSEManufacturer::Shutdown() return CHIP_NO_ERROR; } +CHIP_ERROR FindNextTarget(const BitMask dayOfWeekMap, uint16_t minutesPastMidnightNow_m, + uint16_t & targetTimeMinutesPastMidnight_m, DataModel::Nullable & targetSoC, + DataModel::Nullable & addedEnergy_mWh, bool bAllowTargetsInPast) +{ + EnergyEvse::Structs::ChargingTargetScheduleStruct::Type entry; + + uint16_t minTimeToTarget_m = 24 * 60; // 24 hours + bool bFound = false; + + EVSEManufacturer * mn = GetEvseManufacturer(); + VerifyOrReturnError(mn != nullptr, CHIP_ERROR_UNINITIALIZED); + + EnergyEvseDelegate * dg = mn->GetEvseDelegate(); + VerifyOrReturnError(dg != nullptr, CHIP_ERROR_UNINITIALIZED); + + const DataModel::List & chargingTargetSchedules = + dg->GetEvseTargetsDelegate()->GetTargets(); + for (auto & chargingTargetScheduleEntry : chargingTargetSchedules) + { + if (chargingTargetScheduleEntry.dayOfWeekForSequence.HasAny(dayOfWeekMap)) + { + // We've found today's schedule - iterate through the targets on this day + for (auto & chargingTarget : chargingTargetScheduleEntry.chargingTargets) + { + if ((chargingTarget.targetTimeMinutesPastMidnight < minutesPastMidnightNow_m) && (bAllowTargetsInPast == false)) + { + // This target is in the past so move to the next if there is one + continue; + } + + if (chargingTarget.targetTimeMinutesPastMidnight < minTimeToTarget_m) + { + // This is the earliest target found in the day's targets so far + bFound = true; + minTimeToTarget_m = chargingTarget.targetTimeMinutesPastMidnight; + + targetTimeMinutesPastMidnight_m = chargingTarget.targetTimeMinutesPastMidnight; + + if (chargingTarget.targetSoC.HasValue()) + { + targetSoC.SetNonNull(chargingTarget.targetSoC.Value()); + } + else + { + targetSoC.SetNull(); + } + + if (chargingTarget.addedEnergy.HasValue()) + { + addedEnergy_mWh.SetNonNull(chargingTarget.addedEnergy.Value()); + } + else + { + addedEnergy_mWh.SetNull(); + } + } + } + } + + if (bFound) + { + // Skip the rest of the search + break; + } + } + + return bFound ? CHIP_NO_ERROR : CHIP_ERROR_NOT_FOUND; +} + +/** + * @brief Simple example to demonstrate how an EVSE can compute the start time + * and duration of a charging schedule + */ +CHIP_ERROR EVSEManufacturer::ComputeChargingSchedule() +{ + CHIP_ERROR err; + EVSEManufacturer * mn = GetEvseManufacturer(); + VerifyOrReturnError(mn != nullptr, CHIP_ERROR_UNINITIALIZED); + + EnergyEvseDelegate * dg = mn->GetEvseDelegate(); + VerifyOrReturnError(dg != nullptr, CHIP_ERROR_UNINITIALIZED); + + BitMask dayOfWeekMap = 0; + ReturnErrorOnFailure(GetLocalDayOfWeekNow(dayOfWeekMap)); + + uint16_t minutesPastMidnightNow_m = 0; + ReturnErrorOnFailure(GetMinutesPastMidnight(minutesPastMidnightNow_m)); + + uint32_t now_epoch_s = 0; + ReturnErrorOnFailure(GetEpochTS(now_epoch_s)); + + DataModel::Nullable startTime_epoch_s; + DataModel::Nullable targetTime_epoch_s; + DataModel::Nullable targetSoC; + DataModel::Nullable addedEnergy_mWh; + + uint32_t power_W; + uint32_t chargingDuration_s; + uint32_t tempTargetTime_epoch_s; + uint32_t tempStartTime_epoch_s; + uint16_t targetTimeMinutesPastMidnight_m; + + // Initialise the values to Null - if the FindNextTarget finds one, then it will update the value + targetTime_epoch_s.SetNull(); + targetSoC.SetNull(); + addedEnergy_mWh.SetNull(); + startTime_epoch_s.SetNull(); // If we FindNextTarget this will be computed below and set to a non null value + + /* We can only compute charging schedules if the EV is plugged in and the charging is enabled + * so we know the charging current - i.e. can get the max power, and therefore can calculate + * the charging duration and hence start time + */ + if (dg->IsEvsePluggedIn() && dg->GetSupplyState() == SupplyStateEnum::kChargingEnabled) + { + uint8_t searchDay = 0; + while (searchDay < 2) + { + err = FindNextTarget(dayOfWeekMap, minutesPastMidnightNow_m, targetTimeMinutesPastMidnight_m, targetSoC, + addedEnergy_mWh, (searchDay != 0)); + if (err == CHIP_ERROR_NOT_FOUND) + { + // We didn't find one for today, try tomorrow + searchDay++; + dayOfWeekMap = BitMask((dayOfWeekMap.Raw() << 1) & kAllTargetDaysMask); + + if (!dayOfWeekMap.HasAny()) + { + // Must be Saturday and shifted off, so set it to Sunday + dayOfWeekMap = BitMask(TargetDayOfWeekBitmap::kSunday); + } + } + else + { + break; // We found a target or we error'd out for some other reason + } + } + + if (err == CHIP_NO_ERROR) + { + /* Set the target Time in epoch_s format*/ + tempTargetTime_epoch_s = + ((now_epoch_s / 60) + targetTimeMinutesPastMidnight_m + (searchDay * 1440) - minutesPastMidnightNow_m) * 60; + targetTime_epoch_s.SetNonNull(tempTargetTime_epoch_s); + + if (!targetSoC.IsNull()) + { + if (targetSoC.Value() != 100) + { + ChipLogError(AppServer, "EVSE WARNING: TargetSoC is not 100%% and we don't know the EV SoC!"); + } + // We don't know the Vehicle SoC so we must charge now + // TODO make this use the SoC featureMap to determine if this is an error + startTime_epoch_s.SetNonNull(now_epoch_s); + } + else + { + // We expect to use AddedEnergy to determine the charging start time + if (addedEnergy_mWh.IsNull()) + { + ChipLogError(AppServer, "EVSE ERROR: Neither TargetSoC or AddedEnergy has been provided"); + return CHIP_ERROR_INTERNAL; + } + // Simple optimizer - assume a flat tariff throughout the day + // Compute power from nominal voltage and maxChargingRate + // GetMaximumChargeCurrent returns mA, but to help avoid overflow + // We use V (not mV) and compute power to the nearest Watt + power_W = static_cast((230 * dg->GetMaximumChargeCurrent()) / + 1000); // TODO don't use 230V - not all markets will use that + if (power_W == 0) + { + ChipLogError(AppServer, "EVSE Error: MaxCurrent = 0Amp - Can't schedule charging"); + return CHIP_ERROR_INTERNAL; + } + + // Time to charge(seconds) = (3600 * Energy(mWh) / Power(W)) / 1000 + // to avoid using floats we multiply by 36 and then divide by 10 (instead of x3600 and dividing by 1000) + chargingDuration_s = static_cast(((addedEnergy_mWh.Value() / power_W) * 36) / 10); + + // Add in 15 minutes leeway to account for slow starting vehicles + // that need to condition the battery or if it is cold etc + chargingDuration_s += (15 * 60); + + // A price optimizer can look for cheapest time of day + // However for now we'll start charging as late as possible + tempStartTime_epoch_s = tempTargetTime_epoch_s - chargingDuration_s; + + if (tempStartTime_epoch_s < now_epoch_s) + { + // we need to turn on the EVSE now - it won't have enough time to reach the target + startTime_epoch_s.SetNonNull(now_epoch_s); + // TODO call function to turn on the EV + } + else + { + // we turn off the EVSE for now + startTime_epoch_s.SetNonNull(tempStartTime_epoch_s); + // TODO have a periodic timer which checks if we should turn on the charger now + } + } + } + } + + // Update the attributes to allow a UI to inform the user + dg->SetNextChargeStartTime(startTime_epoch_s); + dg->SetNextChargeTargetTime(targetTime_epoch_s); + dg->SetNextChargeRequiredEnergy(addedEnergy_mWh); + dg->SetNextChargeTargetSoC(targetSoC); + + return err; +} + /** * @brief Allows a client application to initialise the Accuracy, Measurement types etc */ @@ -187,6 +399,8 @@ CHIP_ERROR EVSEManufacturer::SendPowerReading(EndpointId aEndpointId, int64_t aA return CHIP_NO_ERROR; } +using namespace chip::app::Clusters::ElectricalEnergyMeasurement::Structs; + /** * @brief Allows a client application to send cumulative energy readings into the system * @@ -349,10 +563,12 @@ void EVSEManufacturer::ApplicationCallbackHandler(const EVSECbInfo * cb, intptr_ { case EVSECallbackType::StateChanged: ChipLogProgress(AppServer, "EVSE callback - state changed"); + pClass->ComputeChargingSchedule(); break; case EVSECallbackType::ChargeCurrentChanged: ChipLogProgress(AppServer, "EVSE callback - maxChargeCurrent changed to %ld", static_cast(cb->ChargingCurrent.maximumChargeCurrent)); + pClass->ComputeChargingSchedule(); break; case EVSECallbackType::EnergyMeterReadingRequested: ChipLogProgress(AppServer, "EVSE callback - EnergyMeterReadingRequested"); @@ -366,6 +582,11 @@ void EVSEManufacturer::ApplicationCallbackHandler(const EVSECbInfo * cb, intptr_ } break; + case EVSECallbackType::ChargingPreferencesChanged: + ChipLogProgress(AppServer, "EVSE callback - ChargingPreferencesChanged"); + pClass->ComputeChargingSchedule(); + break; + default: ChipLogError(AppServer, "Unhandled EVSE Callback type %d", static_cast(cb->type)); } diff --git a/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp b/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp index b0cfd6fc5ce0ef..6266be245c2a4a 100644 --- a/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp +++ b/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -42,13 +43,6 @@ EnergyEvseDelegate::~EnergyEvseDelegate() } } -/** - * @brief Helper function to get current timestamp in Epoch format - * - * @param chipEpoch reference to hold return timestamp - */ -CHIP_ERROR GetEpochTS(uint32_t & chipEpoch); - /** * @brief Called when EVSE cluster receives Disable command */ @@ -63,7 +57,9 @@ Status EnergyEvseDelegate::Disable() /* update MinimumChargeCurrent & MaximumChargeCurrent to 0 */ SetMinimumChargeCurrent(0); - SetMaximumChargeCurrent(0); + + mMaximumChargingCurrentLimitFromCommand = 0; + ComputeMaxChargeCurrentLimit(); /* update MaximumDischargeCurrent to 0 */ SetMaximumDischargeCurrent(0); @@ -83,13 +79,13 @@ Status EnergyEvseDelegate::EnableCharging(const DataModel::Nullable & { ChipLogProgress(AppServer, "EnergyEvseDelegate::EnableCharging()"); - if (maximumChargeCurrent < kMinimumChargeCurrent || maximumChargeCurrent > kMaximumChargeCurrent) + if (maximumChargeCurrent < kMinimumChargeCurrent) { ChipLogError(AppServer, "Maximum Current outside limits"); return Status::ConstraintError; } - if (minimumChargeCurrent < kMinimumChargeCurrent || minimumChargeCurrent > kMaximumChargeCurrent) + if (minimumChargeCurrent < kMinimumChargeCurrent) { ChipLogError(AppServer, "Maximum Current outside limits"); return Status::ConstraintError; @@ -177,7 +173,7 @@ Status EnergyEvseDelegate::ScheduleCheckOnEnabledTimeout() return Status::Success; } - CHIP_ERROR err = GetEpochTS(chipEpoch); + CHIP_ERROR err = DeviceEnergyManagement::GetEpochTS(chipEpoch); if (err == CHIP_NO_ERROR) { /* time is sync'd */ @@ -198,7 +194,7 @@ Status EnergyEvseDelegate::ScheduleCheckOnEnabledTimeout() else if (err == CHIP_ERROR_REAL_TIME_NOT_SYNCED) { /* Real time isn't sync'd -lets check again in 30 seconds - otherwise keep the charger enabled */ - DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds32(kPeriodicCheckIntervalRealTimeClockNotSynced), + DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds32(kPeriodicCheckIntervalRealTimeClockNotSynced_sec), EvseCheckTimerExpiry, this); } return Status::Success; @@ -234,6 +230,83 @@ Status EnergyEvseDelegate::StartDiagnostics() return Status::Success; } +/** + * @brief Called when EVSE cluster receives SetTargets command + */ +Status EnergyEvseDelegate::SetTargets( + const DataModel::DecodableList & chargingTargetSchedules) +{ + ChipLogProgress(AppServer, "EnergyEvseDelegate::SetTargets()"); + + EvseTargetsDelegate * targets = GetEvseTargetsDelegate(); + VerifyOrReturnError(targets != nullptr, Status::Failure); + + CHIP_ERROR err = targets->SetTargets(chargingTargetSchedules); + VerifyOrReturnError(err == CHIP_NO_ERROR, StatusIB(err).mStatus); + + /* The Application needs to be told that the Targets have been updated + * so it can potentially re-optimize the charging start time etc + */ + NotifyApplicationChargingPreferencesChange(); + + return Status::Success; +} + +Status EnergyEvseDelegate::LoadTargets() +{ + ChipLogProgress(AppServer, "EnergyEvseDelegate::LoadTargets()"); + + EvseTargetsDelegate * targets = GetEvseTargetsDelegate(); + VerifyOrReturnError(targets != nullptr, StatusIB(CHIP_ERROR_UNINITIALIZED).mStatus); + + CHIP_ERROR err = targets->LoadTargets(); + VerifyOrReturnError(err == CHIP_NO_ERROR, StatusIB(err).mStatus); + + return Status::Success; +} + +Status EnergyEvseDelegate::GetTargets(DataModel::List & chargingTargetSchedules) +{ + ChipLogProgress(AppServer, "EnergyEvseDelegate::GetTargets()"); + + EvseTargetsDelegate * targets = GetEvseTargetsDelegate(); + VerifyOrReturnError(targets != nullptr, StatusIB(CHIP_ERROR_UNINITIALIZED).mStatus); + + chargingTargetSchedules = targets->GetTargets(); + + return Status::Success; +} + +/** + * @brief Called when EVSE cluster receives ClearTargets command + */ +Status EnergyEvseDelegate::ClearTargets() +{ + ChipLogProgress(AppServer, "EnergyEvseDelegate::ClearTargets()"); + + CHIP_ERROR err; + + EvseTargetsDelegate * targets = GetEvseTargetsDelegate(); + if (targets == nullptr) + { + return StatusIB(CHIP_ERROR_UNINITIALIZED).mStatus; + } + + err = targets->ClearTargets(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Failed to clear Evse targets: %" CHIP_ERROR_FORMAT, err.Format()); + return Status::Failure; + } + + /* The Application needs to be told that the Targets have been deleted + * so it can potentially re-optimize the charging start time etc + */ + NotifyApplicationChargingPreferencesChange(); + + return Status::Success; +} + /* --------------------------------------------------------------------------- * EVSE Hardware interface below */ @@ -269,7 +342,7 @@ Status EnergyEvseDelegate::HwRegisterEvseCallbackHandler(EVSECallbackFunc handle */ Status EnergyEvseDelegate::HwSetMaxHardwareCurrentLimit(int64_t currentmA) { - if (currentmA < kMinimumChargeCurrent || currentmA > kMaximumChargeCurrent) + if (currentmA < kMinimumChargeCurrent) { return Status::ConstraintError; } @@ -291,7 +364,7 @@ Status EnergyEvseDelegate::HwSetMaxHardwareCurrentLimit(int64_t currentmA) */ Status EnergyEvseDelegate::HwSetCircuitCapacity(int64_t currentmA) { - if (currentmA < kMinimumChargeCurrent || currentmA > kMaximumChargeCurrent) + if (currentmA < kMinimumChargeCurrent) { return Status::ConstraintError; } @@ -316,7 +389,7 @@ Status EnergyEvseDelegate::HwSetCircuitCapacity(int64_t currentmA) */ Status EnergyEvseDelegate::HwSetCableAssemblyLimit(int64_t currentmA) { - if (currentmA < kMinimumChargeCurrent || currentmA > kMaximumChargeCurrent) + if (currentmA < kMinimumChargeCurrent) { return Status::ConstraintError; } @@ -909,6 +982,20 @@ Status EnergyEvseDelegate::NotifyApplicationStateChange() return Status::Success; } +Status EnergyEvseDelegate::NotifyApplicationChargingPreferencesChange() +{ + EVSECbInfo cbInfo; + + cbInfo.type = EVSECallbackType::ChargingPreferencesChanged; + + if (mCallbacks.handler != nullptr) + { + mCallbacks.handler(&cbInfo, mCallbacks.arg); + } + + return Status::Success; +} + Status EnergyEvseDelegate::GetEVSEEnergyMeterValue(ChargingDischargingType meterType, int64_t & aMeterValue) { EVSECbInfo cbInfo; @@ -1227,7 +1314,7 @@ CHIP_ERROR EnergyEvseDelegate::SetCircuitCapacity(int64_t newValue) { int64_t oldValue = mCircuitCapacity; - if (newValue >= kMaximumChargeCurrent) + if (newValue < 0) { return CHIP_IM_GLOBAL_STATUS(ConstraintError); } @@ -1251,7 +1338,7 @@ CHIP_ERROR EnergyEvseDelegate::SetMinimumChargeCurrent(int64_t newValue) { int64_t oldValue = mMinimumChargeCurrent; - if (newValue >= kMaximumChargeCurrent) + if (newValue < 0) { return CHIP_IM_GLOBAL_STATUS(ConstraintError); } @@ -1271,24 +1358,6 @@ int64_t EnergyEvseDelegate::GetMaximumChargeCurrent() return mMaximumChargeCurrent; } -CHIP_ERROR EnergyEvseDelegate::SetMaximumChargeCurrent(int64_t newValue) -{ - int64_t oldValue = mMaximumChargeCurrent; - - if (newValue >= kMaximumChargeCurrent) - { - return CHIP_IM_GLOBAL_STATUS(ConstraintError); - } - - mMaximumChargeCurrent = newValue; - if (oldValue != mMaximumChargeCurrent) - { - ChipLogDetail(AppServer, "MaximumChargeCurrent updated to %ld", static_cast(mMaximumChargeCurrent)); - MatterReportingAttributeChangeCallback(mEndpointId, EnergyEvse::Id, MaximumChargeCurrent::Id); - } - return CHIP_NO_ERROR; -} - /* MaximumDischargeCurrent */ int64_t EnergyEvseDelegate::GetMaximumDischargeCurrent() { @@ -1299,7 +1368,7 @@ CHIP_ERROR EnergyEvseDelegate::SetMaximumDischargeCurrent(int64_t newValue) { int64_t oldValue = mMaximumDischargeCurrent; - if (newValue >= kMaximumChargeCurrent) + if (newValue < 0) { return CHIP_IM_GLOBAL_STATUS(ConstraintError); } @@ -1321,7 +1390,7 @@ int64_t EnergyEvseDelegate::GetUserMaximumChargeCurrent() CHIP_ERROR EnergyEvseDelegate::SetUserMaximumChargeCurrent(int64_t newValue) { - if ((newValue < 0) || (newValue > kMaximumChargeCurrent)) + if (newValue < 0) { return CHIP_IM_GLOBAL_STATUS(ConstraintError); } @@ -1378,18 +1447,105 @@ DataModel::Nullable EnergyEvseDelegate::GetNextChargeStartTime() { return mNextChargeStartTime; } +CHIP_ERROR EnergyEvseDelegate::SetNextChargeStartTime(DataModel::Nullable newNextChargeStartTimeUtc) +{ + if (newNextChargeStartTimeUtc == mNextChargeStartTime) + { + return CHIP_NO_ERROR; + } + + mNextChargeStartTime = newNextChargeStartTimeUtc; + if (mNextChargeStartTime.IsNull()) + { + ChipLogDetail(AppServer, "NextChargeStartTime updated to Null"); + } + else + { + ChipLogDetail(AppServer, "NextChargeStartTime updated to %lu", + static_cast(mNextChargeStartTime.Value())); + } + + MatterReportingAttributeChangeCallback(mEndpointId, EnergyEvse::Id, NextChargeStartTime::Id); + + return CHIP_NO_ERROR; +} + DataModel::Nullable EnergyEvseDelegate::GetNextChargeTargetTime() { return mNextChargeTargetTime; } +CHIP_ERROR EnergyEvseDelegate::SetNextChargeTargetTime(DataModel::Nullable newNextChargeTargetTimeUtc) +{ + if (newNextChargeTargetTimeUtc == mNextChargeTargetTime) + { + return CHIP_NO_ERROR; + } + + mNextChargeTargetTime = newNextChargeTargetTimeUtc; + if (mNextChargeTargetTime.IsNull()) + { + ChipLogDetail(AppServer, "NextChargeTargetTime updated to Null"); + } + else + { + ChipLogDetail(AppServer, "NextChargeTargetTime updated to %lu", + static_cast(mNextChargeTargetTime.Value())); + } + + MatterReportingAttributeChangeCallback(mEndpointId, EnergyEvse::Id, NextChargeTargetTime::Id); + + return CHIP_NO_ERROR; +} + DataModel::Nullable EnergyEvseDelegate::GetNextChargeRequiredEnergy() { return mNextChargeRequiredEnergy; } +CHIP_ERROR EnergyEvseDelegate::SetNextChargeRequiredEnergy(DataModel::Nullable newNextChargeRequiredEnergyMilliWattH) +{ + if (mNextChargeRequiredEnergy == newNextChargeRequiredEnergyMilliWattH) + { + return CHIP_NO_ERROR; + } + + mNextChargeRequiredEnergy = newNextChargeRequiredEnergyMilliWattH; + if (mNextChargeRequiredEnergy.IsNull()) + { + ChipLogDetail(AppServer, "NextChargeRequiredEnergy updated to Null"); + } + else + { + ChipLogDetail(AppServer, "NextChargeRequiredEnergy updated to %ld", static_cast(mNextChargeRequiredEnergy.Value())); + } + + MatterReportingAttributeChangeCallback(mEndpointId, EnergyEvse::Id, NextChargeRequiredEnergy::Id); + + return CHIP_NO_ERROR; +} + DataModel::Nullable EnergyEvseDelegate::GetNextChargeTargetSoC() { return mNextChargeTargetSoC; } +CHIP_ERROR EnergyEvseDelegate::SetNextChargeTargetSoC(DataModel::Nullable newValue) +{ + DataModel::Nullable oldValue = mNextChargeTargetSoC; + + mNextChargeTargetSoC = newValue; + if (oldValue != newValue) + { + if (newValue.IsNull()) + { + ChipLogDetail(AppServer, "NextChargeTargetSoC updated to Null"); + } + else + { + ChipLogDetail(AppServer, "NextChargeTargetSoC updated to %d %%", mNextChargeTargetSoC.Value()); + } + MatterReportingAttributeChangeCallback(mEndpointId, EnergyEvse::Id, NextChargeTargetSoC::Id); + } + return CHIP_NO_ERROR; +} /* ApproximateEVEfficiency */ DataModel::Nullable EnergyEvseDelegate::GetApproximateEVEfficiency() @@ -1402,7 +1558,7 @@ CHIP_ERROR EnergyEvseDelegate::SetApproximateEVEfficiency(DataModel::Nullable oldValue = mApproximateEVEfficiency; mApproximateEVEfficiency = newValue; - if ((oldValue != newValue)) + if (oldValue != newValue) { if (newValue.IsNull()) { @@ -1458,42 +1614,13 @@ DataModel::Nullable EnergyEvseDelegate::GetSessionEnergyDischarged() } /** - * @brief Helper function to get current timestamp in Epoch format - * - * @param chipEpoch reference to hold return timestamp + * @brief Helper function to get know if the EV is plugged in based on state + * (regardless of if it is actually transferring energy) */ -CHIP_ERROR GetEpochTS(uint32_t & chipEpoch) +bool EnergyEvseDelegate::IsEvsePluggedIn() { - chipEpoch = 0; - - System::Clock::Milliseconds64 cTMs; - CHIP_ERROR err = System::SystemClock().GetClock_RealTimeMS(cTMs); - - /* If the GetClock_RealTimeMS returns CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE, then - * This platform cannot ever report real time ! - * This should not be certifiable since getting time is a Mandatory - * feature of EVSE Cluster - */ - if (err == CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE) - { - ChipLogError(Zcl, "Platform does not support GetClock_RealTimeMS. Check EVSE certification requirements!"); - return err; - } - - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "EVSE: Unable to get current time - err:%" CHIP_ERROR_FORMAT, err.Format()); - return err; - } - - auto unixEpoch = std::chrono::duration_cast(cTMs).count(); - if (!UnixEpochToChipEpochTime(unixEpoch, chipEpoch)) - { - ChipLogError(Zcl, "EVSE: unable to convert Unix Epoch time to Matter Epoch Time"); - return err; - } - - return CHIP_NO_ERROR; + return (mState == StateEnum::kPluggedInCharging || mState == StateEnum::kPluggedInDemand || + mState == StateEnum::kPluggedInDischarging || mState == StateEnum::kPluggedInNoDemand); } /** @@ -1506,7 +1633,7 @@ void EvseSession::StartSession(int64_t chargingMeterValue, int64_t dischargingMe { /* Get Timestamp */ uint32_t chipEpoch = 0; - CHIP_ERROR err = GetEpochTS(chipEpoch); + CHIP_ERROR err = DeviceEnergyManagement::GetEpochTS(chipEpoch); if (err != CHIP_NO_ERROR) { /* Note that the error will be also be logged inside GetErrorTS() - @@ -1548,6 +1675,8 @@ void EvseSession::StartSession(int64_t chargingMeterValue, int64_t dischargingMe // TODO persist mSessionEnergyDischargedAtStart } +/*---------------------- EvseSession functions --------------------------*/ + /** * @brief This function updates the session attrs to allow read attributes to return latest values */ @@ -1555,7 +1684,7 @@ void EvseSession::RecalculateSessionDuration() { /* Get Timestamp */ uint32_t chipEpoch = 0; - CHIP_ERROR err = GetEpochTS(chipEpoch); + CHIP_ERROR err = DeviceEnergyManagement::GetEpochTS(chipEpoch); if (err != CHIP_NO_ERROR) { /* Note that the error will be also be logged inside GetErrorTS() - diff --git a/examples/energy-management-app/energy-management-common/src/EnergyEvseEventTriggers.cpp b/examples/energy-management-app/energy-management-common/src/EnergyEvseEventTriggers.cpp index 62329068610f47..a164bb0bafb341 100644 --- a/examples/energy-management-app/energy-management-common/src/EnergyEvseEventTriggers.cpp +++ b/examples/energy-management-app/energy-management-common/src/EnergyEvseEventTriggers.cpp @@ -101,6 +101,14 @@ void SetTestEventTrigger_EVChargeDemandClear() dg->HwSetState(sEVSETestEventSaveData.mOldHwStatePluggedInDemand); } +void SetTestEventTrigger_EVTimeOfUseMode() +{ + // TODO - See #34249 +} +void SetTestEventTrigger_EVTimeOfUseModeClear() +{ + // TODO - See #34249 +} void SetTestEventTrigger_EVSEGroundFault() { EnergyEvseDelegate * dg = GetEvseDelegate(); @@ -159,6 +167,10 @@ bool HandleEnergyEvseTestEventTrigger(uint64_t eventTrigger) ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EV Charge NoDemand"); SetTestEventTrigger_EVChargeDemandClear(); break; + case EnergyEvseTrigger::kEVTimeOfUseMode: + ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EV TimeOfUse Mode"); + SetTestEventTrigger_EVTimeOfUseMode(); + break; case EnergyEvseTrigger::kEVSEGroundFault: ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EVSE has a GroundFault fault"); SetTestEventTrigger_EVSEGroundFault(); @@ -175,7 +187,10 @@ bool HandleEnergyEvseTestEventTrigger(uint64_t eventTrigger) ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EVSE Diagnostics Completed"); SetTestEventTrigger_EVSEDiagnosticsComplete(); break; - + case EnergyEvseTrigger::kEVTimeOfUseModeClear: + ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EV TimeOfUse Mode clear"); + SetTestEventTrigger_EVTimeOfUseModeClear(); + break; default: return false; } diff --git a/examples/energy-management-app/energy-management-common/src/EnergyEvseMain.cpp b/examples/energy-management-app/energy-management-common/src/EnergyEvseMain.cpp index 0a4bc0456e2254..bd22c67eafd9b5 100644 --- a/examples/energy-management-app/energy-management-common/src/EnergyEvseMain.cpp +++ b/examples/energy-management-app/energy-management-common/src/EnergyEvseMain.cpp @@ -49,6 +49,7 @@ using namespace chip::app::Clusters::ElectricalEnergyMeasurement; using namespace chip::app::Clusters::PowerTopology; static std::unique_ptr gEvseDelegate; +static std::unique_ptr gEvseTargetsDelegate; static std::unique_ptr gEvseInstance; static std::unique_ptr gDEMDelegate; static std::unique_ptr gDEMInstance; @@ -143,16 +144,24 @@ CHIP_ERROR EnergyEvseInit() { CHIP_ERROR err; - if (gEvseDelegate || gEvseInstance) + if (gEvseDelegate || gEvseInstance || gEvseTargetsDelegate) { - ChipLogError(AppServer, "EVSE Instance or Delegate already exist."); + ChipLogError(AppServer, "EVSE Instance, Delegate or TargetsDelegate already exist."); return CHIP_ERROR_INCORRECT_STATE; } - gEvseDelegate = std::make_unique(); + gEvseTargetsDelegate = std::make_unique(); + if (!gEvseTargetsDelegate) + { + ChipLogError(AppServer, "Failed to allocate memory for EvseTargetsDelegate"); + return CHIP_ERROR_NO_MEMORY; + } + + gEvseDelegate = std::make_unique(*gEvseTargetsDelegate); if (!gEvseDelegate) { ChipLogError(AppServer, "Failed to allocate memory for EnergyEvseDelegate"); + gEvseTargetsDelegate.reset(); return CHIP_ERROR_NO_MEMORY; } @@ -168,6 +177,7 @@ CHIP_ERROR EnergyEvseInit() if (!gEvseInstance) { ChipLogError(AppServer, "Failed to allocate memory for EnergyEvseManager"); + gEvseTargetsDelegate.reset(); gEvseDelegate.reset(); return CHIP_ERROR_NO_MEMORY; } @@ -176,6 +186,17 @@ CHIP_ERROR EnergyEvseInit() if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Init failed on gEvseInstance"); + gEvseTargetsDelegate.reset(); + gEvseInstance.reset(); + gEvseDelegate.reset(); + return err; + } + + err = gEvseTargetsDelegate->LoadTargets(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Failed to LoadTargets"); + gEvseTargetsDelegate.reset(); gEvseInstance.reset(); gEvseDelegate.reset(); return err; diff --git a/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp b/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp index c8b43342f8bd1c..52a8fec55673dd 100644 --- a/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp +++ b/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp @@ -18,6 +18,7 @@ #include #include +#include using namespace chip::app; using namespace chip::app::Clusters; @@ -117,6 +118,16 @@ CHIP_ERROR EnergyEvseManager::LoadPersistentAttributes() CHIP_ERROR EnergyEvseManager::Init() { ReturnErrorOnFailure(Instance::Init()); + + // Set up the EnergyEvseTargetsStore and persistent storage delegate + EnergyEvseDelegate * dg = GetDelegate(); + VerifyOrReturnLogError(dg != nullptr, CHIP_ERROR_UNINITIALIZED); + + EvseTargetsDelegate * targetsStore = dg->GetEvseTargetsDelegate(); + VerifyOrReturnLogError(targetsStore != nullptr, CHIP_ERROR_UNINITIALIZED); + + ReturnErrorOnFailure(targetsStore->Init(&Server::GetInstance().GetPersistentStorage())); + return LoadPersistentAttributes(); } diff --git a/examples/energy-management-app/energy-management-common/src/EnergyEvseTargetsStore.cpp b/examples/energy-management-app/energy-management-common/src/EnergyEvseTargetsStore.cpp new file mode 100644 index 00000000000000..f15e0b7a109817 --- /dev/null +++ b/examples/energy-management-app/energy-management-common/src/EnergyEvseTargetsStore.cpp @@ -0,0 +1,489 @@ +/* + * + * 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 +#include +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::DataModel; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::EnergyEvse; +using chip::Protocols::InteractionModel::Status; + +EvseTargetsDelegate::EvseTargetsDelegate() {} + +EvseTargetsDelegate::~EvseTargetsDelegate() {} + +CHIP_ERROR EvseTargetsDelegate::Init(PersistentStorageDelegate * targetStore) +{ + ChipLogProgress(AppServer, "EVSE: Initializing EvseTargetsDelegate"); + VerifyOrReturnError(targetStore != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + + mpTargetStore = targetStore; + + // Set FabricDelegate + chip::Server::GetInstance().GetFabricTable().AddFabricDelegate(this); + + return CHIP_NO_ERROR; +} + +const DataModel::List & EvseTargetsDelegate::GetTargets() +{ + return mChargingTargetSchedulesList; +} + +/* static */ +uint16_t EvseTargetsDelegate::GetTlvSizeUpperBound() +{ + size_t kListOverhead = 4; + size_t chargingTargetStuctEstimate = + TLV::EstimateStructOverhead(sizeof(uint16_t), sizeof(Optional), sizeof(Optional)); + size_t chargingTargetScheduleStructEstimate = TLV::EstimateStructOverhead(sizeof(chip::BitMask)) + + kListOverhead + kEvseTargetsMaxTargetsPerDay * chargingTargetStuctEstimate; + size_t totalEstimate = kEvseTargetsMaxNumberOfDays * chargingTargetScheduleStructEstimate + kListOverhead; + + return static_cast(totalEstimate); +} + +CHIP_ERROR EvseTargetsDelegate::LoadTargets() +{ + // The DataModel::List data structure contains a list of + // ChargingTargetScheduleStructs which in turn contains a list of ChargingTargetStructs. Lists contain pointers + // to objects allocated outside of the List. For mChargingTargetSchedulesList, that memory is allocated in + // mChargingTargets and mChargingTargetSchedulesArray. + + Platform::ScopedMemoryBuffer backingBuffer; + uint16_t length = GetTlvSizeUpperBound(); + ReturnErrorCodeIf(!backingBuffer.Calloc(length), CHIP_ERROR_NO_MEMORY); + + CHIP_ERROR err = mpTargetStore->SyncGetKeyValue(spEvseTargetsKeyName, backingBuffer.Get(), length); + if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) + { + // Targets does not exist persistent storage -> initialise mChargingTargetSchedulesList as empty + mChargingTargetSchedulesList = DataModel::List(); + + return CHIP_NO_ERROR; + } + ReturnErrorOnFailure(err); + + TLV::ScopedBufferTLVReader reader(std::move(backingBuffer), length); + + ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Array, TLV::AnonymousTag())); + TLV::TLVType arrayType; + ReturnErrorOnFailure(reader.EnterContainer(arrayType)); + + uint16_t chargingTargetSchedulesIdx = 0; + while ((err = reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())) == CHIP_NO_ERROR) + { + TLV::TLVType evseTargetEntryType; + + ReturnErrorOnFailure(reader.EnterContainer(evseTargetEntryType)); + + // Check we are not exceeding the size of the mChargingTargetSchedulesArray + VerifyOrReturnError(chargingTargetSchedulesIdx < kEvseTargetsMaxNumberOfDays, CHIP_ERROR_INCORRECT_STATE); + + // DayOfWeek bitmap + ReturnErrorOnFailure(reader.Next(TLV::ContextTag(TargetEntryTag::kDayOfWeek))); + ReturnErrorOnFailure(reader.Get(mChargingTargetSchedulesArray[chargingTargetSchedulesIdx].dayOfWeekForSequence)); + + ChipLogProgress(AppServer, "LoadTargets: DayOfWeekForSequence = 0x%02x", + mChargingTargetSchedulesArray[chargingTargetSchedulesIdx].dayOfWeekForSequence.GetField( + static_cast(kAllTargetDaysMask))); + + // ChargingTargets List + ReturnErrorOnFailure(reader.Next(TLV::kTLVType_List, TLV::ContextTag(TargetEntryTag::kChargingTargetsList))); + TLV::TLVType chargingTargetsListType; + ReturnErrorOnFailure(reader.EnterContainer(chargingTargetsListType)); + + // The mChargingTargets object handles the allocation of the chargingTargets. Let it know the currentSchedule index + mChargingTargets.PrepareDaySchedule(chargingTargetSchedulesIdx); + + // Load the chargingTargets associated with this schedule + while ((err = reader.Next(TLV::kTLVType_Structure, TLV::ContextTag(TargetEntryTag::kChargingTargetsStruct))) == + CHIP_NO_ERROR) + { + TLV::TLVType chargingTargetsStructType = TLV::kTLVType_Structure; + ReturnErrorOnFailure(reader.EnterContainer(chargingTargetsStructType)); + + // Keep track of the current chargingTarget being loaded + EnergyEvse::Structs::ChargingTargetStruct::Type chargingTarget; + + while ((err = reader.Next()) == CHIP_NO_ERROR) + { + auto type = reader.GetType(); + auto tag = reader.GetTag(); + if (type == TLV::kTLVType_NotSpecified) + { + // Something wrong - we've lost alignment + return CHIP_ERROR_UNEXPECTED_TLV_ELEMENT; + } + + if (tag == TLV::ContextTag(TargetEntryTag::kTargetTime)) + { + ReturnErrorOnFailure(reader.Get(chargingTarget.targetTimeMinutesPastMidnight)); + } + else if (tag == TLV::ContextTag(TargetEntryTag::kTargetSoC)) + { + chip::Percent tempSoC; + ReturnErrorOnFailure(reader.Get(tempSoC)); + chargingTarget.targetSoC.SetValue(tempSoC); + } + else if (tag == TLV::ContextTag(TargetEntryTag::kAddedEnergy)) + { + int64_t tempAddedEnergy; + ReturnErrorOnFailure(reader.Get(tempAddedEnergy)); + chargingTarget.addedEnergy.SetValue(tempAddedEnergy); + } + else + { + // Something else unexpected here + return CHIP_ERROR_UNEXPECTED_TLV_ELEMENT; + } + } + + ReturnErrorOnFailure(reader.ExitContainer(chargingTargetsStructType)); + + ChipLogProgress(AppServer, + "LoadingTargets: targetTimeMinutesPastMidnight %u targetSoC %u addedEnergy 0x" ChipLogFormatX64, + chargingTarget.targetTimeMinutesPastMidnight, chargingTarget.targetSoC.ValueOr(0), + ChipLogValueX64(chargingTarget.addedEnergy.ValueOr(0))); + + // Update mChargingTargets which is tracking the chargingTargets + mChargingTargets.AddChargingTarget(chargingTarget); + } + + ReturnErrorOnFailure(reader.ExitContainer(chargingTargetsListType)); + ReturnErrorOnFailure(reader.ExitContainer(evseTargetEntryType)); + + // Allocate an array for the chargingTargets loaded for this schedule and copy the chargingTargets into that array. + // The allocated array will be pointed to in the List below. + err = mChargingTargets.AllocAndCopy(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "SetTargets: Failed to allocate memory during LoadTargets %s", chip::ErrorStr(err)); + return err; + } + + // Construct the List. mChargingTargetSchedulesArray will be pointed to in the + // List mChargingTargetSchedulesList below + mChargingTargetSchedulesArray[chargingTargetSchedulesIdx].chargingTargets = + chip::app::DataModel::List( + mChargingTargets.GetChargingTargets(), mChargingTargets.GetNumDailyChargingTargets()); + + chargingTargetSchedulesIdx++; + } + + ReturnErrorOnFailure(reader.ExitContainer(arrayType)); + + // Finalise mChargingTargetSchedulesList + mChargingTargetSchedulesList = DataModel::List(mChargingTargetSchedulesArray, + chargingTargetSchedulesIdx); + + return reader.VerifyEndOfContainer(); +} + +/** + * This function tries to compress a list of entries which has: + * dayOfWeek bitmask + * chargingTargetsList + * + * It takes a new entry and scans the existing list to see if the + * dayOfWeek bitmask is already included somewhere + * + * compute bitmask values: + * + * bitmaskA: (entry.bitmask & bitmask) + * work out which bits in the existing entry are the same (overlapping) + * + * Create and append a new entry for the bits that are the same + * newEntry.bitmask = bitmaskA; + * newEntry.chargingTargetsList = chargingTargetsList + * + * if entry.bitmask == bitmaskA + * this entry is being deleted and can share the newEntry + * delete it + * + * bitmaskB = (entry.bitmask & ~bitmask); + * work out which bits in the existing entry are different + * Remove these bits from the existing entry, (effectively deleting them) + * entry.bitmask = bitmaskB; + * + * NOTE: if `all` bits are removed then the existing entry can be deleted, + * but that's not possible because we check for a full match first + * + * We continue walking our list to see if other entries have overlapping bits + * If they do, then the newEntry.bitmask |= bitmaskA + * + */ +CHIP_ERROR EvseTargetsDelegate::SetTargets( + const DataModel::DecodableList & newChargingTargetSchedules) +{ + ChipLogProgress(AppServer, "SetTargets"); + + // We'll need to have a local copy of the chargingTargets that are referenced from updatedChargingTargetSchedules (which + // is a List where each ChargingTargetScheduleStruct has a List of ChargingTargetStructs). + // Note updatedChargingTargets only needs to exist for the duration of this method as once the new targets have been merged + // with the existing targets, we'll save the updated Targets structure to persistent storage and the reload it into + // mChargingTargetSchedulesList + ChargingTargetsMemMgr updatedChargingTargets; + + // Build up a new Targets structure + DataModel::DecodableList updatedChargingTargetSchedules; + + // updatedChargingTargetSchedules contains a List of ChargingTargetScheduleStruct where the memory of + // ChargingTargetScheduleStruct is which is allocated here. + Structs::ChargingTargetScheduleStruct::Type updatedChargingTargetSchedulesArray[kEvseTargetsMaxNumberOfDays]; + + // Iterate across the list of new schedules. For each schedule, iterate through the existing Target + // (mChargingTargetSchedulesList) working out how to merge the new schedule. + auto newIter = newChargingTargetSchedules.begin(); + while (newIter.Next()) + { + auto & newChargingTargetSchedule = newIter.GetValue(); + + uint8_t newBitmask = + newChargingTargetSchedule.dayOfWeekForSequence.GetField(static_cast(kAllTargetDaysMask)); + + ChipLogProgress(AppServer, "SetTargets: DayOfWeekForSequence = 0x%02x", newBitmask); + + PrintTargets(mChargingTargetSchedulesList); + + // Iterate across the existing schedule entries, seeing if there is overlap with + // the dayOfWeekForSequenceBitmap + bool found = false; + uint16_t updatedChargingTargetSchedulesIdx = 0; + + // Let the updatedChargingTargets object of the schedule index + updatedChargingTargets.PrepareDaySchedule(updatedChargingTargetSchedulesIdx); + + for (auto & currentChargingTargetSchedule : mChargingTargetSchedulesList) + { + uint8_t currentBitmask = + currentChargingTargetSchedule.dayOfWeekForSequence.GetField(static_cast(kAllTargetDaysMask)); + + ChipLogProgress(AppServer, "SetTargets: Scanning current entry %d of %d: bitmap 0x%02x", + updatedChargingTargetSchedulesIdx, static_cast(mChargingTargetSchedulesList.size()), + currentBitmask); + + // Work out if the new schedule dayOfWeekSequence overlaps with any existing schedules + uint8_t bitmaskA = static_cast(currentBitmask & newBitmask); + uint8_t bitmaskB = static_cast(currentBitmask & ~newBitmask); + + BitMask updatedBitmask; + + if (currentBitmask == bitmaskA) + { + // This entry has the all the same bits as the newEntry + updatedBitmask = BitMask(bitmaskA); + + // Copy the new chargingTargets to this schedule index + CHIP_ERROR err = updatedChargingTargets.AllocAndCopy(newChargingTargetSchedule.chargingTargets); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "SetTargets: Failed to copy the new chargingTargets %s", chip::ErrorStr(err)); + return err; + } + + found = true; + } + else + { + // This entry stays - but it has lost some days from the bitmask + updatedBitmask = BitMask(bitmaskB); + + // Copy the existing chargingTargets + CHIP_ERROR err = updatedChargingTargets.AllocAndCopy(currentChargingTargetSchedule.chargingTargets); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "SetTargets: Failed to copy the new chargingTargets %s", chip::ErrorStr(err)); + return err; + } + } + + // Update the new schedule with the dayOfWeekForSequence and list of chargingTargets + updatedChargingTargetSchedulesArray[updatedChargingTargetSchedulesIdx].dayOfWeekForSequence = updatedBitmask; + + updatedChargingTargetSchedulesArray[updatedChargingTargetSchedulesIdx].chargingTargets = + chip::app::DataModel::List( + updatedChargingTargets.GetChargingTargets(), updatedChargingTargets.GetNumDailyChargingTargets()); + + // Going to look at the next schedule entry + updatedChargingTargetSchedulesIdx++; + + // Let the updatedChargingTargets object of the schedule index + updatedChargingTargets.PrepareDaySchedule(updatedChargingTargetSchedulesIdx); + } + + // If found is false, then there were no existing entries for the dayOfWeekForSequence. Add a new entry + if (!found) + { + // Copy the new chargingTargets + CHIP_ERROR err = updatedChargingTargets.AllocAndCopy(newChargingTargetSchedule.chargingTargets); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "SetTargets: Failed to copy the new chargingTargets %s", chip::ErrorStr(err)); + return err; + } + + // Update the new schedule with the dayOfWeekForSequence and list of chargingTargets + updatedChargingTargetSchedulesArray[updatedChargingTargetSchedulesIdx].dayOfWeekForSequence = + newChargingTargetSchedule.dayOfWeekForSequence; + + updatedChargingTargetSchedulesArray[updatedChargingTargetSchedulesIdx].chargingTargets = + chip::app::DataModel::List( + updatedChargingTargets.GetChargingTargets(), updatedChargingTargets.GetNumDailyChargingTargets()); + + // We've added a new schedule entry + updatedChargingTargetSchedulesIdx++; + + // Let the updatedChargingTargets object of the schedule index + updatedChargingTargets.PrepareDaySchedule(updatedChargingTargetSchedulesIdx); + } + + // Now create the full Target data structure that we are going to save to persistent storage + DataModel::List updatedChargingTargetSchedulesList( + updatedChargingTargetSchedulesArray, updatedChargingTargetSchedulesIdx); + + CHIP_ERROR err = SaveTargets(updatedChargingTargetSchedulesList); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "SetTargets: Failed to save Target to persistent storage %s", chip::ErrorStr(err)); + return err; + } + + // Now reload from persistent storage so that mChargingTargetSchedulesList gets the update Target + err = LoadTargets(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "SetTargets: Failed to load Target from persistent storage %s", chip::ErrorStr(err)); + return err; + } + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR +EvseTargetsDelegate::SaveTargets(DataModel::List & chargingTargetSchedulesList) +{ + uint16_t total = GetTlvSizeUpperBound(); + + Platform::ScopedMemoryBuffer backingBuffer; + ReturnErrorCodeIf(!backingBuffer.Calloc(total), CHIP_ERROR_NO_MEMORY); + TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), total); + + TLV::TLVType arrayType; + ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, arrayType)); + for (auto & chargingTargetSchedule : chargingTargetSchedulesList) + { + ChipLogProgress( + AppServer, "SaveTargets: DayOfWeekForSequence = 0x%02x", + chargingTargetSchedule.dayOfWeekForSequence.GetField(static_cast(kAllTargetDaysMask))); + + TLV::TLVType evseTargetEntryType; + ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, evseTargetEntryType)); + ReturnErrorOnFailure(writer.Put(TLV::ContextTag(TargetEntryTag::kDayOfWeek), chargingTargetSchedule.dayOfWeekForSequence)); + + TLV::TLVType chargingTargetsListType; + ReturnErrorOnFailure(writer.StartContainer(TLV::ContextTag(TargetEntryTag::kChargingTargetsList), TLV::kTLVType_List, + chargingTargetsListType)); + for (auto & chargingTarget : chargingTargetSchedule.chargingTargets) + { + TLV::TLVType chargingTargetsStructType = TLV::kTLVType_Structure; + ReturnErrorOnFailure(writer.StartContainer(TLV::ContextTag(TargetEntryTag::kChargingTargetsStruct), + TLV::kTLVType_Structure, chargingTargetsStructType)); + ReturnErrorOnFailure( + writer.Put(TLV::ContextTag(TargetEntryTag::kTargetTime), chargingTarget.targetTimeMinutesPastMidnight)); + if (chargingTarget.targetSoC.HasValue()) + { + ReturnErrorOnFailure(writer.Put(TLV::ContextTag(TargetEntryTag::kTargetSoC), chargingTarget.targetSoC.Value())); + } + + if (chargingTarget.addedEnergy.HasValue()) + { + ReturnErrorOnFailure(writer.Put(TLV::ContextTag(TargetEntryTag::kAddedEnergy), chargingTarget.addedEnergy.Value())); + } + + ReturnErrorOnFailure(writer.EndContainer(chargingTargetsStructType)); + } + ReturnErrorOnFailure(writer.EndContainer(chargingTargetsListType)); + ReturnErrorOnFailure(writer.EndContainer(evseTargetEntryType)); + } + + ReturnErrorOnFailure(writer.EndContainer(arrayType)); + + uint64_t len = static_cast(writer.GetLengthWritten()); + ChipLogProgress(AppServer, "SaveTargets: length written 0x" ChipLogFormatX64, ChipLogValueX64(len)); + + writer.Finalize(backingBuffer); + + ReturnErrorOnFailure(mpTargetStore->SyncSetKeyValue(spEvseTargetsKeyName, backingBuffer.Get(), static_cast(len))); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR EvseTargetsDelegate::ClearTargets() +{ + /* We simply delete the data from the persistent store */ + mpTargetStore->SyncDeleteKeyValue(spEvseTargetsKeyName); + + // Now reload from persistent storage so that mChargingTargetSchedulesList gets updated (it will be empty) + CHIP_ERROR err = LoadTargets(); + + return err; +} + +void EvseTargetsDelegate::PrintTargets( + const DataModel::List & chargingTargetSchedules) +{ + ChipLogProgress(AppServer, "---------------------- TARGETS ---------------------"); + + uint16_t chargingTargetScheduleIdx = 0; + for (auto & chargingTargetSchedule : chargingTargetSchedules) + { + [[maybe_unused]] uint8_t bitmask = + chargingTargetSchedule.dayOfWeekForSequence.GetField(static_cast(kAllTargetDaysMask)); + ChipLogProgress(AppServer, "idx %u dayOfWeekForSequence 0x%02x", chargingTargetScheduleIdx, bitmask); + + uint16_t chargingTargetIdx = 0; + for (auto & chargingTarget : chargingTargetSchedule.chargingTargets) + { + [[maybe_unused]] int64_t addedEnergy = chargingTarget.addedEnergy.HasValue() ? chargingTarget.addedEnergy.Value() : 0; + + ChipLogProgress( + AppServer, "chargingTargetIdx %u targetTimeMinutesPastMidnight %u targetSoC %u addedEnergy 0x" ChipLogFormatX64, + chargingTargetIdx, chargingTarget.targetTimeMinutesPastMidnight, + chargingTarget.targetSoC.HasValue() ? chargingTarget.targetSoC.Value() : 0, ChipLogValueX64(addedEnergy)); + + chargingTargetIdx++; + } + + chargingTargetScheduleIdx++; + } +} + +/** + * Part of the FabricTable::Delegate interface. Gets called when a fabric is deleted, such as on FabricTable::Delete(). + **/ +void EvseTargetsDelegate::OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) {} diff --git a/examples/energy-management-app/energy-management-common/tests/BUILD.gn b/examples/energy-management-app/energy-management-common/tests/BUILD.gn new file mode 100644 index 00000000000000..b55340a527233d --- /dev/null +++ b/examples/energy-management-app/energy-management-common/tests/BUILD.gn @@ -0,0 +1,44 @@ +# Copyright (c) 2020-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. + +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") + +import("${chip_root}/build/chip/chip_test_suite.gni") + +config("tests_config") { + include_dirs = [ "${chip_root}/examples/energy-management-app/energy-management-common/include" ] +} + +chip_test_suite("tests") { + output_name = "libEnergyTest" + output_dir = "${root_out_dir}/lib" + + public_configs = [ ":tests_config" ] + + test_sources = [ "TestEvseTargetsStorage.cpp" ] + + cflags = [ "-Wconversion" ] + + public_deps = [ + "${chip_root}/examples/energy-management-app/energy-management-common", + "${chip_root}/examples/energy-management-app/linux:test-evse-targets-store", + "${chip_root}/src/app", + "${chip_root}/src/app/common:cluster-objects", + "${chip_root}/src/app/tests:helpers", + "${chip_root}/src/lib", + "${chip_root}/src/lib/core", + "${chip_root}/src/lib/support:testing", + ] +} diff --git a/examples/energy-management-app/energy-management-common/tests/TestEvseTargetsStorage.cpp b/examples/energy-management-app/energy-management-common/tests/TestEvseTargetsStorage.cpp new file mode 100644 index 00000000000000..6a1f2ec4c424f1 --- /dev/null +++ b/examples/energy-management-app/energy-management-common/tests/TestEvseTargetsStorage.cpp @@ -0,0 +1,219 @@ +/* + * + * Copyright (c) 2022 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 +#include +#include +#include +#include +#include + +#include "EnergyEvseTargetsStore.h" + +using namespace chip; +using namespace chip::app; +using namespace chip::app::DataModel; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::EnergyEvse; + +namespace { + +constexpr uint16_t ENERGY_EVSE_SET_TARGETS_DAYS_IN_A_WEEK = 7; +constexpr uint16_t ENERGY_EVSE_SET_TARGETS_MAX_CHARGING_TARGETS = 10; + +class TestEvseTargetsStorage : public ::testing::Test +{ +public: + static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); } + static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); } + + bool CompTargets(const DataModel::List & targets1, + const DataModel::List & targets2); + + void PopulateTargets(uint16_t numDays, uint16_t numChargingTargetsPerDay); + + void SetTargets(); + void CheckTargets(); + +private: + uint8_t mStore[4096]; + + EnergyEvse::Structs::ChargingTargetScheduleStruct::Type mChargingTargetSchedules[ENERGY_EVSE_SET_TARGETS_DAYS_IN_A_WEEK]; + EnergyEvse::Structs::ChargingTargetStruct::Type mChargingTargets[ENERGY_EVSE_SET_TARGETS_DAYS_IN_A_WEEK] + [ENERGY_EVSE_SET_TARGETS_MAX_CHARGING_TARGETS]; + chip::app::DataModel::List + mChargingTargetsList[ENERGY_EVSE_SET_TARGETS_DAYS_IN_A_WEEK]; + + chip::app::DataModel::List mRefChargingTargetSchedulesList; + DataModel::DecodableList mDecodableChargingTargetSchedulesList; + + TestPersistentStorageDelegate mStorageDelegate; + + EvseTargetsDelegate mEtd; + bool mEtdInitialised = false; +}; + +TEST_F(TestEvseTargetsStorage, TestEmpty) +{ + PopulateTargets(0, 0); + SetTargets(); + CheckTargets(); +} + +TEST_F(TestEvseTargetsStorage, TestFull) +{ + PopulateTargets(ENERGY_EVSE_SET_TARGETS_DAYS_IN_A_WEEK, ENERGY_EVSE_SET_TARGETS_MAX_CHARGING_TARGETS); + SetTargets(); + CheckTargets(); +} + +TEST_F(TestEvseTargetsStorage, TestPartial1) +{ + PopulateTargets(ENERGY_EVSE_SET_TARGETS_DAYS_IN_A_WEEK, 1); + SetTargets(); + CheckTargets(); +} + +TEST_F(TestEvseTargetsStorage, TestPartial2) +{ + PopulateTargets(1, ENERGY_EVSE_SET_TARGETS_MAX_CHARGING_TARGETS); + SetTargets(); + CheckTargets(); +} + +bool TestEvseTargetsStorage::CompTargets(const DataModel::List & targets1, + const DataModel::List & targets2) +{ + if (targets1.size() != targets2.size()) + { + ChipLogError(AppServer, "CompTargets: Different number of ChargingTargetScheduleStruct in lists"); + return false; + } + + uint16_t dayIdx = 0; + for (const auto & entry1 : targets1) + { + const auto & entry2 = targets2[dayIdx++]; + + if (entry1.dayOfWeekForSequence != entry2.dayOfWeekForSequence) + { + ChipLogError(AppServer, "CompTargets: Different dayOfWeekForSequence"); + return false; + } + + if (entry1.chargingTargets.size() != entry2.chargingTargets.size()) + { + ChipLogError(AppServer, "CompTargets: Different number of chargingTargets in day list"); + return false; + } + + uint16_t chargingTargetsIdx = 0; + for (const auto & targetStruct1 : entry1.chargingTargets) + { + const auto & targetStruct2 = entry2.chargingTargets[chargingTargetsIdx++]; + + if (targetStruct1.targetTimeMinutesPastMidnight != targetStruct2.targetTimeMinutesPastMidnight) + { + ChipLogError(AppServer, "CompTargets: Different targetTimeMinutesPastMidnight"); + return false; + } + + if (targetStruct1.targetSoC != targetStruct2.targetSoC) + { + ChipLogError(AppServer, "CompTargets: Different targetSoC"); + return false; + } + + if (targetStruct1.addedEnergy != targetStruct2.addedEnergy) + { + ChipLogError(AppServer, "CompTargets: Different addedEnergy"); + return false; + } + } + } + + return true; +} + +void TestEvseTargetsStorage::PopulateTargets(uint16_t numDays, uint16_t numChargingTargetsPerDay) +{ + for (uint16_t dayIdx = 0; dayIdx < numDays; dayIdx++) + { + for (uint16_t chargingTargetIdx = 0; chargingTargetIdx < numChargingTargetsPerDay; chargingTargetIdx++) + { + mChargingTargets[dayIdx][chargingTargetIdx].targetTimeMinutesPastMidnight = + static_cast(dayIdx * 60 + chargingTargetIdx); + mChargingTargets[dayIdx][chargingTargetIdx].targetSoC.SetValue(65); + mChargingTargets[dayIdx][chargingTargetIdx].addedEnergy.SetValue(400); + } + + mChargingTargetsList[dayIdx] = chip::app::DataModel::List( + mChargingTargets[dayIdx], numChargingTargetsPerDay); + + mChargingTargetSchedules[dayIdx].dayOfWeekForSequence.Set(static_cast(1 << dayIdx)); + mChargingTargetSchedules[dayIdx].chargingTargets = mChargingTargetsList[dayIdx]; + } + + chip::app::DataModel::List chargingTargetSchedulesList( + mChargingTargetSchedules, numDays); + + mRefChargingTargetSchedulesList = chargingTargetSchedulesList; + + TLV::TLVReader mReader; + TLV::TLVWriter mWriter; + + mWriter.Init(mStore, sizeof(mStore)); + + CHIP_ERROR err = DataModel::Encode(mWriter, TLV::AnonymousTag(), mRefChargingTargetSchedulesList); + EXPECT_EQ(err, CHIP_NO_ERROR); + + EXPECT_EQ(mWriter.Finalize(), CHIP_NO_ERROR); + + mReader.Init(mStore); + EXPECT_EQ(mReader.Next(), CHIP_NO_ERROR); + + err = DataModel::Decode(mReader, mDecodableChargingTargetSchedulesList); + EXPECT_EQ(err, CHIP_NO_ERROR); +} + +void TestEvseTargetsStorage::SetTargets() +{ + if (!mEtdInitialised) + { + mEtd.Init(&mStorageDelegate); + + mEtdInitialised = true; + } + + CHIP_ERROR err = mEtd.SetTargets(mDecodableChargingTargetSchedulesList); + EXPECT_EQ(err, CHIP_NO_ERROR); +} + +void TestEvseTargetsStorage::CheckTargets() +{ + const DataModel::List targets = mEtd.GetTargets(); + + EXPECT_TRUE(CompTargets(mRefChargingTargetSchedulesList, targets)); +} + +} // namespace diff --git a/examples/energy-management-app/linux/BUILD.gn b/examples/energy-management-app/linux/BUILD.gn index c3564c345a1182..1a6e2d287fed2e 100644 --- a/examples/energy-management-app/linux/BUILD.gn +++ b/examples/energy-management-app/linux/BUILD.gn @@ -18,8 +18,6 @@ import("${chip_root}/build/chip/tools.gni") import("${chip_root}/src/app/common_flags.gni") import("${chip_root}/third_party/imgui/imgui.gni") -assert(chip_build_tools) - import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni") if (chip_enable_pw_rpc) { @@ -36,6 +34,7 @@ config("includes") { executable("chip-energy-management-app") { sources = [ + "${chip_root}/examples/energy-management-app/energy-management-common/src/ChargingTargetsMemMgr.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DEMTestEventTriggers.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", @@ -45,6 +44,7 @@ executable("chip-energy-management-app") { "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseEventTriggers.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseMain.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseTargetsStore.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyReportingEventTriggers.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/FakeReadings.cpp", @@ -127,6 +127,23 @@ executable("chip-energy-management-app") { output_dir = root_out_dir } +source_set("test-evse-targets-store") { + sources = [ + "${chip_root}/examples/energy-management-app/energy-management-common/src/ChargingTargetsMemMgr.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseTargetsStore.cpp", + ] + + include_dirs = [ + "include", + "${chip_root}/examples/energy-management-app/energy-management-common/include", + ] + + deps = [ + "${chip_root}/examples/energy-management-app/energy-management-common", + "${chip_root}/src/lib", + ] +} + group("linux") { deps = [ ":chip-energy-management-app" ] } diff --git a/examples/energy-management-app/linux/main.cpp b/examples/energy-management-app/linux/main.cpp index 2d056e56a9b149..cdc2c4ed53d844 100644 --- a/examples/energy-management-app/linux/main.cpp +++ b/examples/energy-management-app/linux/main.cpp @@ -39,7 +39,7 @@ constexpr uint16_t kOptionFeatureMap = 'f'; // Define the chip::ArgParser command line structures for extending the command line to support the // -f/--featureMap option static chip::ArgParser::OptionDef sFeatureMapOptionDefs[] = { - { "featureSet", chip::ArgParser::kArgumentRequired, kOptionFeatureMap }, { NULL } + { "featureSet", chip::ArgParser::kArgumentRequired, kOptionFeatureMap }, { nullptr } }; static chip::ArgParser::OptionSet sCmdLineOptions = { @@ -74,11 +74,11 @@ static uint32_t ParseNumber(const char * pString) uint32_t num = 0; if (strlen(pString) > 2 && pString[0] == '0' && pString[1] == 'x') { - num = (uint32_t) strtoul(&pString[2], NULL, 16); + num = (uint32_t) strtoul(&pString[2], nullptr, 16); } else { - num = (uint32_t) strtoul(pString, NULL, 10); + num = (uint32_t) strtoul(pString, nullptr, 10); } return num; diff --git a/examples/platform/linux/BUILD.gn b/examples/platform/linux/BUILD.gn index cd79d2b42e3273..f8a5334d78c47d 100644 --- a/examples/platform/linux/BUILD.gn +++ b/examples/platform/linux/BUILD.gn @@ -84,6 +84,8 @@ source_set("app-main") { ":energy-evse-test-event-trigger", ":energy-reporting-test-event-trigger", ":smco-test-event-trigger", + "${chip_root}/src/controller:controller", + "${chip_root}/src/controller:gen_check_chip_controller_headers", "${chip_root}/src/lib", "${chip_root}/src/platform/logging:stdio", ] @@ -140,7 +142,11 @@ source_set("commissioner-main") { defines += [ "ENABLE_CHIP_SHELL" ] } - public_deps = [ "${chip_root}/src/lib" ] + public_deps = [ + "${chip_root}/src/controller:controller", + "${chip_root}/src/controller:gen_check_chip_controller_headers", + "${chip_root}/src/lib", + ] deps = [ "${chip_root}/src/app/server" ] if (chip_enable_transport_trace) { diff --git a/examples/shell/shell_common/BUILD.gn b/examples/shell/shell_common/BUILD.gn index 0e318ccb31a5ca..470f81b5d2459a 100644 --- a/examples/shell/shell_common/BUILD.gn +++ b/examples/shell/shell_common/BUILD.gn @@ -70,12 +70,14 @@ static_library("shell_common") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/smco-stub.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-modes-manager.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/ChargingTargetsMemMgr.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/ElectricalPowerMeasurementDelegate.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", + "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseTargetsStore.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyTimeUtils.cpp", ] diff --git a/src/BUILD.gn b/src/BUILD.gn index 7e5fedcc0d539b..c162a61f0620fa 100644 --- a/src/BUILD.gn +++ b/src/BUILD.gn @@ -155,6 +155,15 @@ if (chip_build_tests) { } } + chip_test_group("example_tests") { + deps = [] + tests = [] + if (chip_device_platform != "esp32" && chip_device_platform != "efr32" && + current_os != "android") { + tests += [ "${chip_root}/examples/energy-management-app/energy-management-common/tests" ] + } + } + chip_test_group("fake_platform_tests") { tests = [ "${chip_root}/src/lib/dnssd/platform/tests" ] } diff --git a/src/app/clusters/electrical-energy-measurement-server/electrical-energy-measurement-server.cpp b/src/app/clusters/electrical-energy-measurement-server/electrical-energy-measurement-server.cpp index f5c8c281615435..cdf480b64cdde4 100644 --- a/src/app/clusters/electrical-energy-measurement-server/electrical-energy-measurement-server.cpp +++ b/src/app/clusters/electrical-energy-measurement-server/electrical-energy-measurement-server.cpp @@ -25,6 +25,7 @@ #include #include #include +#include using chip::Protocols::InteractionModel::Status; diff --git a/src/app/clusters/electrical-power-measurement-server/electrical-power-measurement-server.h b/src/app/clusters/electrical-power-measurement-server/electrical-power-measurement-server.h index 8204a271434e63..53c741d9378cc4 100644 --- a/src/app/clusters/electrical-power-measurement-server/electrical-power-measurement-server.h +++ b/src/app/clusters/electrical-power-measurement-server/electrical-power-measurement-server.h @@ -21,7 +21,6 @@ #include #include -#include #include namespace chip { @@ -36,8 +35,6 @@ class Delegate void SetEndpointId(EndpointId aEndpoint) { mEndpointId = aEndpoint; } - using HarmonicMeasurementIterator = CommonIterator; - virtual PowerModeEnum GetPowerMode() = 0; virtual uint8_t GetNumberOfMeasurementTypes() = 0; diff --git a/src/app/clusters/energy-evse-server/EnergyEvseTestEventTriggerHandler.h b/src/app/clusters/energy-evse-server/EnergyEvseTestEventTriggerHandler.h index 307156e21f1197..38db739bc97f32 100644 --- a/src/app/clusters/energy-evse-server/EnergyEvseTestEventTriggerHandler.h +++ b/src/app/clusters/energy-evse-server/EnergyEvseTestEventTriggerHandler.h @@ -57,6 +57,8 @@ enum class EnergyEvseTrigger : uint64_t kEVChargeDemand = 0x0099000000000004, // EV Charge Demand Test Event Clear | Simulate the EV becoming fully charged kEVChargeDemandClear = 0x0099000000000005, + // EV Charge TimeOfUse Mode | Simulate putting the EVSE into a Mode with the TimeOfUse tag included + kEVTimeOfUseMode = 0x0099000000000006, // EVSE has a GroundFault fault kEVSEGroundFault = 0x0099000000000010, // EVSE has a OverTemperature fault @@ -65,6 +67,8 @@ enum class EnergyEvseTrigger : uint64_t kEVSEFaultClear = 0x0099000000000012, // EVSE Diagnostics Complete | Simulate diagnostics have been completed and return to normal kEVSEDiagnosticsComplete = 0x0099000000000020, + // EV Charge TimeOfUse Mode clear | Simulate clearing the EVSE Mode TimeOfUse tag + kEVTimeOfUseModeClear = 0x0099000000000021, }; class EnergyEvseTestEventTriggerHandler : public TestEventTriggerHandler diff --git a/src/app/clusters/energy-evse-server/energy-evse-server.cpp b/src/app/clusters/energy-evse-server/energy-evse-server.cpp index af825217e2565a..4807bf33969681 100644 --- a/src/app/clusters/energy-evse-server/energy-evse-server.cpp +++ b/src/app/clusters/energy-evse-server/energy-evse-server.cpp @@ -309,13 +309,13 @@ void Instance::HandleEnableCharging(HandlerContext & ctx, const Commands::Enable auto & minimumChargeCurrent = commandData.minimumChargeCurrent; auto & maximumChargeCurrent = commandData.maximumChargeCurrent; - if ((minimumChargeCurrent < kMinimumChargeCurrent) || (minimumChargeCurrent > kMaximumChargeCurrent)) + if (minimumChargeCurrent < kMinimumChargeCurrent) { ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); return; } - if ((maximumChargeCurrent < kMinimumChargeCurrent) || (maximumChargeCurrent > kMaximumChargeCurrent)) + if (maximumChargeCurrent < kMinimumChargeCurrent) { ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); return; @@ -339,7 +339,7 @@ void Instance::HandleEnableDischarging(HandlerContext & ctx, const Commands::Ena auto & dischargingEnabledUntil = commandData.dischargingEnabledUntil; auto & maximumDischargeCurrent = commandData.maximumDischargeCurrent; - if ((maximumDischargeCurrent < kMinimumChargeCurrent) || (maximumDischargeCurrent > kMaximumChargeCurrent)) + if (maximumDischargeCurrent < kMinimumChargeCurrent) { ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); return; @@ -362,27 +362,136 @@ void Instance::HandleStartDiagnostics(HandlerContext & ctx, const Commands::Star void Instance::HandleSetTargets(HandlerContext & ctx, const Commands::SetTargets::DecodableType & commandData) { // Call the delegate - // TODO - // Status status = mDelegate.SetTargets(); - Status status = Status::UnsupportedCommand; + auto & chargingTargetSchedules = commandData.chargingTargetSchedules; + + Status status = ValidateTargets(chargingTargetSchedules); + if (status != Status::Success) + { + ChipLogError(AppServer, "SetTargets contained invalid data - Rejecting"); + } + else + { + status = mDelegate.SetTargets(chargingTargetSchedules); + } ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); } + +Status Instance::ValidateTargets( + const DataModel::DecodableList & chargingTargetSchedules) +{ + /* A) check that the targets are valid + * 1) each target must be within valid range (TargetTimeMinutesPastMidnight < 1440) + * 2) each target must be within valid range (TargetSoC percent 0 - 100) + * If SOC feature not supported then this MUST be 100 or not present + * 3) each target must be within valid range (AddedEnergy >= 0) + * B) Day of Week is only allowed to be included once + */ + + uint8_t dayOfWeekBitmap = 0; + + auto iter = chargingTargetSchedules.begin(); + while (iter.Next()) + { + auto & entry = iter.GetValue(); + uint8_t bitmask = entry.dayOfWeekForSequence.GetField(static_cast(0x7F)); + ChipLogProgress(AppServer, "DayOfWeekForSequence = 0x%02x", bitmask); + + if ((dayOfWeekBitmap & bitmask) != 0) + { + // A bit has already been set - Return ConstraintError + ChipLogError(AppServer, "DayOfWeekForSequence has a bit set which has already been set in another entry."); + return Status::ConstraintError; + } + dayOfWeekBitmap |= bitmask; // add this day Of week to the previously seen days + + auto iterInner = entry.chargingTargets.begin(); + uint8_t innerIdx = 0; + while (iterInner.Next()) + { + auto & targetStruct = iterInner.GetValue(); + uint16_t minutesPastMidnight = targetStruct.targetTimeMinutesPastMidnight; + ChipLogProgress(AppServer, "[%d] MinutesPastMidnight : %d", innerIdx, + static_cast(minutesPastMidnight)); + + if (minutesPastMidnight > 1439) + { + ChipLogError(AppServer, "MinutesPastMidnight has invalid value (%d)", static_cast(minutesPastMidnight)); + return Status::ConstraintError; + } + + // If SocReporting is supported, targetSoc must have a value in the range [0, 100] + if (HasFeature(Feature::kSoCReporting)) + { + if (!targetStruct.targetSoC.HasValue()) + { + ChipLogError(AppServer, "kSoCReporting is supported but TargetSoC does not have a value"); + return Status::Failure; + } + + if (targetStruct.targetSoC.Value() > 100) + { + ChipLogError(AppServer, "TargetSoC has invalid value (%d)", static_cast(targetStruct.targetSoC.Value())); + return Status::ConstraintError; + } + } + else if (targetStruct.targetSoC.HasValue() && targetStruct.targetSoC.Value() != 100) + { + // If SocReporting is not supported but targetSoc has a value, it must be 100 + ChipLogError(AppServer, "TargetSoC has can only be 100%% if SOC feature is not supported"); + return Status::ConstraintError; + } + + // One or both of targetSoc and addedEnergy must be specified + if (!(targetStruct.targetSoC.HasValue()) && !(targetStruct.addedEnergy.HasValue())) + { + ChipLogError(AppServer, "Must have one of AddedEnergy or TargetSoC"); + return Status::Failure; + } + + // Validate the value of addedEnergy, if specified is >= 0 + if (targetStruct.addedEnergy.HasValue() && targetStruct.addedEnergy.Value() < 0) + { + ChipLogError(AppServer, "AddedEnergy has invalid value (%ld)", + static_cast(targetStruct.addedEnergy.Value())); + return Status::ConstraintError; + } + innerIdx++; + } + + if (iterInner.GetStatus() != CHIP_NO_ERROR) + { + return Status::InvalidCommand; + } + } + + if (iter.GetStatus() != CHIP_NO_ERROR) + { + return Status::InvalidCommand; + } + + return Status::Success; +} + void Instance::HandleGetTargets(HandlerContext & ctx, const Commands::GetTargets::DecodableType & commandData) { - // Call the delegate - // TODO - // Status status = mDelegate.GetTargets(); - Status status = Status::UnsupportedCommand; + Commands::GetTargetsResponse::Type response; - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); + Status status = mDelegate.GetTargets(response.chargingTargetSchedules); + if (status != Status::Success) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); + return; + } + + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::Success); } + void Instance::HandleClearTargets(HandlerContext & ctx, const Commands::ClearTargets::DecodableType & commandData) { // Call the delegate - // TODO - // Status status = mDelegate.ClearTargets(); - Status status = Status::UnsupportedCommand; + Status status = mDelegate.ClearTargets(); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); } diff --git a/src/app/clusters/energy-evse-server/energy-evse-server.h b/src/app/clusters/energy-evse-server/energy-evse-server.h index 2ff46f339ff6ad..979e44c040a17b 100644 --- a/src/app/clusters/energy-evse-server/energy-evse-server.h +++ b/src/app/clusters/energy-evse-server/energy-evse-server.h @@ -36,8 +36,9 @@ namespace EnergyEvse { // Spec-defined constraints constexpr int64_t kMinimumChargeCurrent = 0; -constexpr int64_t kMaximumChargeCurrent = 80000; constexpr uint32_t kMaxRandomizationDelayWindow = 86400; +constexpr uint8_t kEvseTargetsMaxNumberOfDays = 7; +constexpr uint8_t kEvseTargetsMaxTargetsPerDay = 10; /** @brief * Defines methods for implementing application-specific logic for the EVSE Management Cluster. @@ -81,6 +82,36 @@ class Delegate */ virtual Protocols::InteractionModel::Status StartDiagnostics() = 0; + /** + * @brief Delegate should implement a handler for the SetTargets command. + * It should report Status::Success if successful and may + * return other Status codes if it fails + */ + virtual Protocols::InteractionModel::Status + SetTargets(const DataModel::DecodableList & chargingTargetSchedules) = 0; + + /** + * @brief Delegate should implement a handler for LoadTargets + * + * This needs to load any stored targets into memory + */ + virtual Protocols::InteractionModel::Status LoadTargets() = 0; + + /** + * @brief Delegate should implement a handler for GetTargets + * + * @param[out] The full targets structure + */ + virtual Protocols::InteractionModel::Status + GetTargets(DataModel::List & chargingTargetSchedules) = 0; + + /** + * @brief Delegate should implement a handler for ClearTargets command. + * It should report Status::Success if successful and may + * return other Status codes if it fails + */ + virtual Protocols::InteractionModel::Status ClearTargets() = 0; + // ------------------------------------------------------------------ // Get attribute methods virtual StateEnum GetState() = 0; @@ -178,6 +209,10 @@ class Instance : public AttributeAccessInterface, public CommandHandlerInterface void HandleSetTargets(HandlerContext & ctx, const Commands::SetTargets::DecodableType & commandData); void HandleGetTargets(HandlerContext & ctx, const Commands::GetTargets::DecodableType & commandData); void HandleClearTargets(HandlerContext & ctx, const Commands::ClearTargets::DecodableType & commandData); + + // Check that the targets are valid + Protocols::InteractionModel::Status + ValidateTargets(const DataModel::DecodableList & chargingTargetSchedules); }; } // namespace EnergyEvse diff --git a/src/python_testing/TC_EEVSE_2_3.py b/src/python_testing/TC_EEVSE_2_3.py new file mode 100644 index 00000000000000..d8d8f8fdfb96b5 --- /dev/null +++ b/src/python_testing/TC_EEVSE_2_3.py @@ -0,0 +1,454 @@ +# +# 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. + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${ENERGY_MANAGEMENT_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json --enable-key 000102030405060708090a0b0c0d0e0f +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --hex-arg enableKey:000102030405060708090a0b0c0d0e0f --endpoint 1 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# === END CI TEST ARGUMENTS === + +import logging +from datetime import datetime, timedelta, timezone + +import chip.clusters as Clusters +from chip.clusters.Types import NullValue +from chip.interaction_model import Status +from matter_testing_support import EventChangeCallback, MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts +from TC_EEVSE_Utils import EEVSEBaseTestHelper + +logger = logging.getLogger(__name__) + + +class TC_EEVSE_2_3(MatterBaseTest, EEVSEBaseTestHelper): + + def desc_TC_EEVSE_2_3(self) -> str: + """Returns a description of this test""" + return "5.1.4. [TC-EEVSE-2.3] Optional ChargingPreferences feature functionality with DUT as Server\n" \ + "This test case verifies the primary functionality of the Energy EVSE cluster server with the optional ChargingPreferences feature supported." + + def pics_TC_EEVSE_2_3(self): + """ This function returns a list of PICS for this test case that must be True for the test to be run""" + return ["EEVSE.S", "EEVSE.S.F00"] + + def steps_TC_EEVSE_2_3(self) -> list[TestStep]: + steps = [ + TestStep("1", "Commissioning, already done", + is_commissioning=True), + TestStep("2", "TH reads TestEventTriggersEnabled attribute from General Diagnostics Cluster.", + "Verify value is 1 (True)"), + TestStep("3", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EEVSE.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EEVSE.TEST_EVENT_TRIGGER for Basic Functionality Test Event.", + "Verify Command response is Success"), + TestStep("4", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EEVSE.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EEVSE.TEST_EVENT_TRIGGER for EVSE TimeOfUse Mode Test Event.", + "Verify Command response is Success"), + TestStep("5", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EEVSE.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EEVSE.TEST_EVENT_TRIGGER for EV Plugged-in Test Event.", + "Verify Command response is Success and event EEVSE.S.E00(EVConnected) sent"), + TestStep("6", "TH sends ClearTargets.", + "Verify Command response is Success"), + TestStep("6a", "TH reads NextChargeStartTime attribute.", + "Verify value is null."), + TestStep("6b", "TH reads NextChargeTargetTime attribute.", + "Verify value is null."), + TestStep("6c", "TH reads NextChargeRequiredEnergy attribute.", + "Verify value is null."), + TestStep("6d", "TH reads NextChargeTargetSoC attribute.", + "Verify value is null."), + TestStep("7", "TH sends GetTargets.", + "Response EEVSE.S.C00.Tx(GetTargetsResponse) sent with no targets defined."), + TestStep("8", "TH sends SetTargets with DayOfTheWeekforSequence=0x7F (i.e. having all days set) and a single ChargingTargets={TargetTime=1439, TargetSoC=null, AddedEnergy=25000000}.", + "Verify Command response is Success"), + TestStep("8a", "TH reads NextChargeStartTime attribute.", + "Verify value is null."), + TestStep("8b", "TH reads NextChargeTargetTime attribute.", + "Verify value is null."), + TestStep("8c", "TH reads NextChargeRequiredEnergy attribute.", + "Verify value is null."), + TestStep("8d", "TH reads NextChargeTargetSoC attribute.", + "Verify value is null."), + TestStep("9", "TH sends EnableCharging with ChargingEnabledUntil=null, minimumChargeCurrent=6000, maximumChargeCurrent=60000.", + "Verify Command response is Success"), + TestStep("9a", "TH reads NextChargeStartTime attribute.", + "Verify value is before the next TargetTime above."), + TestStep("9b", "TH reads NextChargeTargetTime attribute.", + "Verify value is TargetTime above."), + TestStep("9c", "TH reads NextChargeRequiredEnergy attribute.", + "Verify value is AddedEnergy above."), + TestStep("9d", "TH reads NextChargeTargetSoC attribute.", + "Verify value is null."), + TestStep("10", "TH sends GetTargets.", + "Response EEVSE.S.C00.Tx(GetTargetsResponse) sent with targets equivalent to the above (Note 1)."), + TestStep("11", "TH sends SetTargets with DayOfTheWeekforSequence=0x7F (i.e. having all days set) and a single ChargingTargets={TargetTime=1, TargetSoC=100, AddedEnergy=null}.", + "Verify Command response is Success"), + TestStep("11a", "TH reads NextChargeStartTime attribute.", + "Verify value is before the next TargetTime above."), + TestStep("11b", "TH reads NextChargeTargetTime attribute.", + "Verify value is TargetTime above."), + TestStep("11c", "TH reads NextChargeRequiredEnergy attribute.", + "Verify value is null."), + TestStep("11d", "TH reads NextChargeTargetSoC attribute.", + "Verify value is 100."), + TestStep("12", "TH sends GetTargets.", + "Response EEVSE.S.C00.Tx(GetTargetsResponse) sent with targets equivalent to the above (Note 1)."), + TestStep("13", "TH sends SetTargets with DayOfTheWeekforSequence=0x40 (i.e. having Saturday set) and 10 ChargingTargets with TargetTimes=60,180,300,420,540,660,780,900,1020,1140 and all with TargetSoC=null, AddedEnergy=2500000.", + "Verify Command response is Success"), + TestStep("14", "TH sends SetTargets with DayOfTheWeekforSequence=0x01 (i.e. having Sunday set) and no ChargingTargets.", + "Verify Command response is Success"), + TestStep("15", "TH sends GetTargets.", + "Response EEVSE.S.C00.Tx(GetTargetsResponse) sent with 1 target for each day Monday to Friday equivalent to step 9 (Note 1), 10 targets for Saturday as step 11, and no targets for Sunday."), + TestStep("16", "TH sends ClearTargets.", + "Verify Command response is Success"), + TestStep("16a", "TH reads NextChargeStartTime attribute.", + "Verify value is null."), + TestStep("16b", "TH reads NextChargeTargetTime attribute.", + "Verify value is null."), + TestStep("16c", "TH reads NextChargeRequiredEnergy attribute.", + "Verify value is null."), + TestStep("16d", "TH reads NextChargeTargetSoC attribute.", + "Verify value is null."), + TestStep("17", "TH sends GetTargets.", + "Response EEVSE.S.C00.Tx(GetTargetsResponse) sent with no targets defined."), + TestStep("18", "TH sends SetTargets with two identical ChargingTargetSchedules={DayOfTheWeekforSequence=0x01,ChargingTarget[0]={TargetTime=60,TargetSoC=null,AddedEnergy=2500000}}.", + "Verify Command response is ConstraintError"), + TestStep("19", "TH sends SetTargets with DayOfTheWeekforSequence=0x40 and 11 ChargingTargets with TargetTimes=60,180,300,420,540,660,780,900,1020,1140,1260 and all with TargetSoC=null, AddedEnergy=2500000.", + "Verify Command response is ResourceExhausted"), + TestStep("20", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EEVSE.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EEVSE.TEST_EVENT_TRIGGER for EV Plugged-in Test Event Clear.", + "Verify Command response is Success and event EEVSE.S.E01(EVNotDetected) sent"), + TestStep("21", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EEVSE.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EEVSE.TEST_EVENT_TRIGGER for Basic Functionality Test Event Clear.", + "Verify Command response is Success"), + ] + + return steps + + def log_get_targets_response(self, get_targets_response): + logger.info(f" Rx'd: {get_targets_response}") + for index, entry in enumerate(get_targets_response.chargingTargetSchedules): + logger.info( + f" [{index}] DayOfWeekForSequence: {entry.dayOfWeekForSequence:02x}") + for sub_index, sub_entry in enumerate(entry.chargingTargets): + logger.info( + f" - [{sub_index}] TargetTime: {sub_entry.targetTimeMinutesPastMidnight} TargetSoC: {sub_entry.targetSoC} AddedEnergy: {sub_entry.addedEnergy}") + + def convert_epoch_s_to_time(self, epoch_s, tz=timezone.utc): + delta_from_epoch = timedelta(seconds=epoch_s) + matter_epoch = datetime(2000, 1, 1, 0, 0, 0, 0, tz) + + return matter_epoch + delta_from_epoch + + def compute_expected_target_time_as_epoch_s(self, minutes_past_midnight): + """Takes minutes past midnight and assumes local timezone, returns the value in Matter Epoch_S""" + # Matter epoch is 0 hours, 0 minutes, 0 seconds on Jan 1, 2000 UTC + # Get the current midnight + minutesPastMidnight as epoch_s + # NOTE that MinutesPastMidnight is in LOCAL time not UTC so it reflects + # the charging time based on where the consumer is. + target_time = datetime.now() # Get time in local time + target_time = target_time.replace(hour=int(minutes_past_midnight / 60), + minute=(minutes_past_midnight % 60), second=0, + microsecond=0) # Align to minutes past midnight + + if (target_time < datetime.now()): + # This is in the past - so we need to add 1 day + # We can get away with this in this test scenario - but should + # really look at the next target on this day to see if that is in the future + target_time = target_time + timedelta(days=1) + + # Shift to UTC so we can use timezone aware subtraction from Matter epoch in UTC + target_time = target_time.astimezone(timezone.utc) + + logger.info( + f"minutesPastMidnight = {minutes_past_midnight} => " + f"{int(minutes_past_midnight/60)}:{int(minutes_past_midnight%60)}" + f" Expected target_time = {target_time}") + + target_time_delta = target_time - \ + datetime(2000, 1, 1, 0, 0, 0, 0).astimezone(timezone.utc) + expected_target_time_epoch_s = int(target_time_delta.total_seconds()) + return expected_target_time_epoch_s + + @async_test_body + async def test_TC_EEVSE_2_3(self): + + self.step("1") + # Commission DUT - already done + + # Subscribe to Events and when they are sent push them to a queue for checking later + events_callback = EventChangeCallback(Clusters.EnergyEvse) + await events_callback.start(self.default_controller, + self.dut_node_id, + self.matter_test_config.endpoint) + + self.step("2") + await self.check_test_event_triggers_enabled() + + self.step("3") + await self.send_test_event_trigger_basic() + + self.step("4") + await self.send_test_event_trigger_time_of_use_mode() + + self.step("5") + await self.send_test_event_trigger_pluggedin() + event_data = events_callback.wait_for_event_report( + Clusters.EnergyEvse.Events.EVConnected) + session_id = event_data.sessionID + + self.step("6") + await self.send_clear_targets_command() + + self.step("6a") + await self.check_evse_attribute("NextChargeStartTime", NullValue) + + self.step("6b") + await self.check_evse_attribute("NextChargeTargetTime", NullValue) + + self.step("6c") + await self.check_evse_attribute("NextChargeRequiredEnergy", NullValue) + + self.step("6d") + await self.check_evse_attribute("NextChargeTargetSoC", NullValue) + + self.step("7") + get_targets_response = await self.send_get_targets_command() + self.log_get_targets_response(get_targets_response) + empty_targets_response = Clusters.EnergyEvse.Commands.GetTargetsResponse( + chargingTargetSchedules=[]) + asserts.assert_equal(get_targets_response, empty_targets_response, + f"Unexpected 'GetTargets' response value - expected {empty_targets_response}, was {get_targets_response}") + + self.step("8") + # The targets is a list of up to 7x ChargingTargetScheduleStruct's (one per day) + # each containing a list of up to 10x targets per day + minutes_past_midnight = 1439 + dailyTargets = [Clusters.EnergyEvse.Structs.ChargingTargetStruct(targetTimeMinutesPastMidnight=minutes_past_midnight, + # targetSoc not sent + addedEnergy=25000000)] + targets = [Clusters.EnergyEvse.Structs.ChargingTargetScheduleStruct( + dayOfWeekForSequence=0x7F, chargingTargets=dailyTargets)] + # This should be all days Sun-Sat (0x7F) with an TargetTime 1439 and added Energy 25kWh + await self.send_set_targets_command(chargingTargetSchedules=targets) + + self.step("8a") + await self.check_evse_attribute("NextChargeStartTime", NullValue) + + self.step("8b") + await self.check_evse_attribute("NextChargeTargetTime", NullValue) + + self.step("8c") + await self.check_evse_attribute("NextChargeRequiredEnergy", NullValue) + + self.step("8d") + await self.check_evse_attribute("NextChargeTargetSoC", NullValue) + + self.step("9") + await self.send_enable_charge_command(charge_until=NullValue, min_charge=6000, max_charge=60000) + + self.step("9a") + next_start_time_epoch_s = await self.read_evse_attribute_expect_success(attribute="NextChargeStartTime") + + expected_next_start_time_epoch_s = self.compute_expected_target_time_as_epoch_s( + minutes_past_midnight) + asserts.assert_less(next_start_time_epoch_s, + expected_next_start_time_epoch_s) + + self.step("9b") + await self.check_evse_attribute("NextChargeTargetTime", expected_next_start_time_epoch_s) + + self.step("9c") + await self.check_evse_attribute("NextChargeRequiredEnergy", 25000000) + + self.step("9d") + await self.check_evse_attribute("NextChargeTargetSoC", NullValue) + + self.step("10") + get_targets_response = await self.send_get_targets_command() + self.log_get_targets_response(get_targets_response) + asserts.assert_equal(get_targets_response.chargingTargetSchedules, targets, + f"Unexpected 'GetTargets' response value - expected {targets}, was {get_targets_response}") + + self.step("11") + # This should be all days Sun-Sat (0x7F) with an TargetTime 1 and SoC of 100%, AddedEnergy= NullValue + minutes_past_midnight = 1 + daily_targets_step_11 = [Clusters.EnergyEvse.Structs.ChargingTargetStruct(targetTimeMinutesPastMidnight=minutes_past_midnight, + targetSoC=100)] + targets_step_11 = [Clusters.EnergyEvse.Structs.ChargingTargetScheduleStruct( + dayOfWeekForSequence=0x7F, chargingTargets=daily_targets_step_11)] + + await self.send_set_targets_command(chargingTargetSchedules=targets_step_11) + + self.step("11a") + next_start_time_epoch_s = await self.read_evse_attribute_expect_success(attribute="NextChargeStartTime") + logger.info( + f"Received NextChargeStartTime: {next_start_time_epoch_s} = {self.convert_epoch_s_to_time(next_start_time_epoch_s, tz=None)}") + + self.step("11b") + next_target_time_epoch_s = await self.read_evse_attribute_expect_success(attribute="NextChargeTargetTime") + logger.info( + f"Received NextChargeTargetTime: {next_target_time_epoch_s} = {self.convert_epoch_s_to_time(next_target_time_epoch_s, tz=None)}") + + # This should be the next MinutesPastMidnight converted to realtime as epoch_s + expected_target_time_epoch_s = self.compute_expected_target_time_as_epoch_s( + minutes_past_midnight) + + asserts.assert_less(next_start_time_epoch_s, next_target_time_epoch_s, + f"Unexpected 'NextChargeStartTime' response value - expected this to be < {next_target_time_epoch_s}, was {next_start_time_epoch_s}") + + asserts.assert_equal(next_target_time_epoch_s, expected_target_time_epoch_s, + f"Unexpected 'NextChargeTargetTime' response value - expected {expected_target_time_epoch_s} = {self.convert_epoch_s_to_time(expected_target_time_epoch_s, tz=None)}, was {next_target_time_epoch_s} = {self.convert_epoch_s_to_time(next_target_time_epoch_s, tz=None)}") + + self.step("11c") + await self.check_evse_attribute("NextChargeRequiredEnergy", NullValue) + + self.step("11d") + await self.check_evse_attribute("NextChargeTargetSoC", 100) + + self.step("12") + get_targets_response = await self.send_get_targets_command() + self.log_get_targets_response(get_targets_response) + # This should be the same as targets_step_11 + asserts.assert_equal(get_targets_response.chargingTargetSchedules, targets_step_11, + f"Unexpected 'GetTargets' response value - expected {targets_step_11}, was {get_targets_response}") + + self.step("13") + # This should modify Sat (0x40) with 10 targets throughout the day + daily_targets_step_13 = [ + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=60, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=180, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=300, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=420, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=540, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=660, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=780, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=900, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=1020, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=1140, addedEnergy=25000000), + ] + targets_step_13 = [Clusters.EnergyEvse.Structs.ChargingTargetScheduleStruct( + dayOfWeekForSequence=0x40, chargingTargets=daily_targets_step_13)] + await self.send_set_targets_command(chargingTargetSchedules=targets_step_13) + + self.step("14") + # This should modify Sun (0x01) with NO targets on that day + daily_targets_step_14 = [] + targets_step_14 = [Clusters.EnergyEvse.Structs.ChargingTargetScheduleStruct( + dayOfWeekForSequence=0x01, chargingTargets=daily_targets_step_14)] + await self.send_set_targets_command(chargingTargetSchedules=targets_step_14) + + self.step("15") + get_targets_response = await self.send_get_targets_command() + self.log_get_targets_response(get_targets_response) + # We should expect that there should be 3 entries: + # [0] This should be all days (except Sun & Sat) = 0x3e with an TargetTime 1439 and added Energy 25kWh (from step 9) + # [1] This should be (Sat) = 0x40 with 10 TargetTimes and added Energy 25kWh (from step 11) + # [2] This should be (Sun) = 0x01 with NO Targets (from step 12) + # TODO - it would be better to iterate through each day and check it matches + asserts.assert_equal(len(get_targets_response.chargingTargetSchedules), 3, + "'GetTargets' response should have 3 entries") + asserts.assert_equal(get_targets_response.chargingTargetSchedules[0].dayOfWeekForSequence, 0x3e, + "'GetTargets' response entry 0 should have DayOfWeekForSequence = 0x3e (62)") + asserts.assert_equal(get_targets_response.chargingTargetSchedules[0].chargingTargets, daily_targets_step_11, + f"'GetTargets' response entry 0 should have chargingTargets = {daily_targets_step_11})") + asserts.assert_equal(get_targets_response.chargingTargetSchedules[1], targets_step_13[0], + f"'GetTargets' response entry 1 should be {targets_step_13})") + asserts.assert_equal(get_targets_response.chargingTargetSchedules[2], targets_step_14[0], + f"'GetTargets' response entry 2 should be {targets_step_14})") + + self.step("16") + await self.send_clear_targets_command() + + self.step("16a") + await self.check_evse_attribute("NextChargeStartTime", NullValue) + + self.step("16b") + await self.check_evse_attribute("NextChargeTargetTime", NullValue) + + self.step("16c") + await self.check_evse_attribute("NextChargeRequiredEnergy", NullValue) + + self.step("16d") + await self.check_evse_attribute("NextChargeTargetSoC", NullValue) + + self.step("17") + get_targets_response = await self.send_get_targets_command() + self.log_get_targets_response(get_targets_response) + asserts.assert_equal(get_targets_response, empty_targets_response, + f"Unexpected 'GetTargets' response value - expected {empty_targets_response}, was {get_targets_response}") + + self.step("18") + daily_targets_step_18 = [Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=60, addedEnergy=25000000)] + targets_step_18 = [Clusters.EnergyEvse.Structs.ChargingTargetScheduleStruct(dayOfWeekForSequence=0x1, chargingTargets=daily_targets_step_18), + Clusters.EnergyEvse.Structs.ChargingTargetScheduleStruct(dayOfWeekForSequence=0x1, chargingTargets=daily_targets_step_18)] + await self.send_set_targets_command(chargingTargetSchedules=targets_step_18, expected_status=Status.ConstraintError) + + self.step("19") + daily_targets_step_19 = [ + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=60, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=180, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=300, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=420, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=540, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=660, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=780, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=900, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=1020, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=1140, addedEnergy=25000000), + Clusters.EnergyEvse.Structs.ChargingTargetStruct( + targetTimeMinutesPastMidnight=1260, addedEnergy=25000000), + ] + + targets_step_19 = [Clusters.EnergyEvse.Structs.ChargingTargetScheduleStruct( + dayOfWeekForSequence=0x40, chargingTargets=daily_targets_step_19)] + await self.send_set_targets_command(chargingTargetSchedules=targets_step_19, expected_status=Status.ResourceExhausted) + + self.step("20") + await self.send_test_event_trigger_pluggedin_clear() + event_data = events_callback.wait_for_event_report( + Clusters.EnergyEvse.Events.EVNotDetected) + expected_state = Clusters.EnergyEvse.Enums.StateEnum.kPluggedInNoDemand + self.validate_ev_not_detected_event( + event_data, session_id, expected_state, expected_duration=0, expected_charged=0) + + self.step("21") + await self.send_test_event_trigger_basic_clear() + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_EEVSE_Utils.py b/src/python_testing/TC_EEVSE_Utils.py index 6b6b1395072ba0..17f64191ed37ec 100644 --- a/src/python_testing/TC_EEVSE_Utils.py +++ b/src/python_testing/TC_EEVSE_Utils.py @@ -16,8 +16,10 @@ import logging +import typing import chip.clusters as Clusters +from chip.clusters.Types import NullValue from chip.interaction_model import InteractionModelError, Status from mobly import asserts @@ -36,6 +38,22 @@ async def check_evse_attribute(self, attribute, expected_value, endpoint: int = asserts.assert_equal(value, expected_value, f"Unexpected '{attribute}' value - expected {expected_value}, was {value}") + def check_value_in_range(self, attribute: str, value: int, lower_value: int, upper_value: int): + asserts.assert_greater_equal(value, lower_value, + f"Unexpected '{attribute}' value - expected {lower_value}, was {value}") + asserts.assert_less_equal(value, upper_value, + f"Unexpected '{attribute}' value - expected {upper_value}, was {value}") + + async def check_evse_attribute_in_range(self, attribute, lower_value: int, upper_value: int, endpoint: int = None, allow_null: bool = False): + value = await self.read_evse_attribute_expect_success(endpoint=endpoint, attribute=attribute) + if allow_null and value is NullValue: + # skip the range check + logger.info("value is NULL - OK") + return value + + self.check_value_in_range(attribute, value, lower_value, upper_value) + return value + async def get_supported_energy_evse_attributes(self, endpoint: int = None): return await self.read_evse_attribute_expect_success(endpoint, "AttributeList") @@ -45,7 +63,8 @@ async def write_user_max_charge(self, endpoint: int = None, user_max_charge: int result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, Clusters.EnergyEvse.Attributes.UserMaximumChargeCurrent(user_max_charge))]) - asserts.assert_equal(result[0].Status, Status.Success, "UserMaximumChargeCurrent write failed") + asserts.assert_equal( + result[0].Status, Status.Success, "UserMaximumChargeCurrent write failed") async def send_enable_charge_command(self, endpoint: int = None, charge_until: int = None, timedRequestTimeoutMs: int = 3000, min_charge: int = 6000, max_charge: int = 32000, expected_status: Status = Status.Success): @@ -58,7 +77,8 @@ async def send_enable_charge_command(self, endpoint: int = None, charge_until: i timedRequestTimeoutMs=timedRequestTimeoutMs) except InteractionModelError as e: - asserts.assert_equal(e.status, expected_status, "Unexpected error returned") + asserts.assert_equal(e.status, expected_status, + "Unexpected error returned") async def send_disable_command(self, endpoint: int = None, timedRequestTimeoutMs: int = 3000, expected_status: Status = Status.Success): try: @@ -67,7 +87,8 @@ async def send_disable_command(self, endpoint: int = None, timedRequestTimeoutMs timedRequestTimeoutMs=timedRequestTimeoutMs) except InteractionModelError as e: - asserts.assert_equal(e.status, expected_status, "Unexpected error returned") + asserts.assert_equal(e.status, expected_status, + "Unexpected error returned") async def send_start_diagnostics_command(self, endpoint: int = None, timedRequestTimeoutMs: int = 3000, expected_status: Status = Status.Success): @@ -77,7 +98,46 @@ async def send_start_diagnostics_command(self, endpoint: int = None, timedReques timedRequestTimeoutMs=timedRequestTimeoutMs) except InteractionModelError as e: - asserts.assert_equal(e.status, expected_status, "Unexpected error returned") + asserts.assert_equal(e.status, expected_status, + "Unexpected error returned") + + async def send_clear_targets_command(self, endpoint: int = None, timedRequestTimeoutMs: int = 3000, + expected_status: Status = Status.Success): + try: + await self.send_single_cmd(cmd=Clusters.EnergyEvse.Commands.ClearTargets(), + endpoint=endpoint, + timedRequestTimeoutMs=timedRequestTimeoutMs) + + except InteractionModelError as e: + asserts.assert_equal(e.status, expected_status, + "Unexpected error returned") + + async def send_get_targets_command(self, endpoint: int = None, timedRequestTimeoutMs: int = 3000, + expected_status: Status = Status.Success): + try: + get_targets_resp = await self.send_single_cmd(cmd=Clusters.EnergyEvse.Commands.GetTargets(), + endpoint=endpoint, + timedRequestTimeoutMs=timedRequestTimeoutMs) + + except InteractionModelError as e: + asserts.assert_equal(e.status, expected_status, + "Unexpected error returned") + + return get_targets_resp + + async def send_set_targets_command(self, endpoint: int = None, + chargingTargetSchedules: typing.List[ + Clusters.EnergyEvse.Structs.ChargingTargetScheduleStruct] = None, + timedRequestTimeoutMs: int = 3000, + expected_status: Status = Status.Success): + try: + await self.send_single_cmd(cmd=Clusters.EnergyEvse.Commands.SetTargets(chargingTargetSchedules), + endpoint=endpoint, + timedRequestTimeoutMs=timedRequestTimeoutMs) + + except InteractionModelError as e: + asserts.assert_equal(e.status, expected_status, + "Unexpected error returned") async def send_test_event_trigger_basic(self): await self.send_test_event_triggers(eventTrigger=0x0099000000000000) @@ -97,6 +157,9 @@ async def send_test_event_trigger_charge_demand(self): async def send_test_event_trigger_charge_demand_clear(self): await self.send_test_event_triggers(eventTrigger=0x0099000000000005) + async def send_test_event_trigger_time_of_use_mode(self): + await self.send_test_event_triggers(eventTrigger=0x0099000000000006) + async def send_test_event_trigger_evse_ground_fault(self): await self.send_test_event_triggers(eventTrigger=0x0099000000000010) From e58954def3197381a8fff029b1f1e95ee120c5e1 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 26 Jul 2024 09:25:34 -0400 Subject: [PATCH 30/49] Add Darwin codegen support for global structs and enums. (#34527) There's a bunch of refactoring of templates to avoid copy/paste for the global case. But the generated output is the same so far, until some ZAP-side changes happen that would let us actually enable the global bits. --- .../CHIP/templates/MTRBaseClusters.zapt | 51 +++------------- .../CHIP/templates/MTRStructsObjc-src.zapt | 59 ++----------------- .../CHIP/templates/MTRStructsObjc.zapt | 36 ++++------- .../CHIP/templates/availability.yaml | 9 +++ .../CHIP/templates/partials/enum_decl.zapt | 46 +++++++++++++++ .../partials/struct_interface_decl.zapt | 25 ++++++++ .../partials/struct_interface_impl.zapt | 57 ++++++++++++++++++ .../Framework/CHIP/templates/templates.json | 12 ++++ 8 files changed, 173 insertions(+), 122 deletions(-) create mode 100644 src/darwin/Framework/CHIP/templates/partials/enum_decl.zapt create mode 100644 src/darwin/Framework/CHIP/templates/partials/struct_interface_decl.zapt create mode 100644 src/darwin/Framework/CHIP/templates/partials/struct_interface_impl.zapt diff --git a/src/darwin/Framework/CHIP/templates/MTRBaseClusters.zapt b/src/darwin/Framework/CHIP/templates/MTRBaseClusters.zapt index 35d09c0f916431..1f5c8dd3404f6a 100644 --- a/src/darwin/Framework/CHIP/templates/MTRBaseClusters.zapt +++ b/src/darwin/Framework/CHIP/templates/MTRBaseClusters.zapt @@ -107,53 +107,16 @@ subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptio {{/unless}} {{/zcl_clusters}} -{{#zcl_clusters}} {{#zcl_enums}} -{{#*inline "enumDef"}} -typedef NS_ENUM({{asUnderlyingZclType name}}, {{objCEnumName clusterName enumName}}) { - {{#zcl_enum_items}} - {{#if (isSupported ../clusterName enum=../enumName enumValue=(asUpperCamelCase label preserveAcronyms=true))}} - {{objCEnumName ../clusterName ../enumName}}{{asUpperCamelCase label preserveAcronyms=true}} {{availability ../clusterName enum=../enumName enumValue=(asUpperCamelCase label preserveAcronyms=true) deprecationMessage=(concat "Please use " (objCEnumName (asUpperCamelCase ../../name preserveAcronyms=true) ../label) (asUpperCamelCase label preserveAcronyms=true))}} = {{asHex value 2}}, - {{/if}} - {{#*inline "oldNameItemDecl"}} - {{#if oldItemName}} - {{#if (isSupported ../clusterName enum=../enumName enumValue=oldItemName)}} - {{objCEnumName ../clusterName ../enumName}}{{objCEnumItemLabel oldItemName}} {{availability ../clusterName enum=../enumName enumValue=oldItemName deprecationMessage=(concat "Please use " (objCEnumName (asUpperCamelCase ../../name preserveAcronyms=true) ../label) (asUpperCamelCase label preserveAcronyms=true))}} = {{asHex value 2}}, - {{/if}} - {{/if}} - {{/inline}} - {{> oldNameItemDecl oldItemName=(oldName ../clusterName enum=../enumName enumValue=(asUpperCamelCase label preserveAcronyms=true))}} - {{/zcl_enum_items}} - {{!We had extra "Not Supported" values for DoorLockUserStatus/DoorLockUserType that we have to wedge in here manually for now.}} - {{#if (and (isStrEqual clusterName "DoorLock") - (or (isStrEqual enumName "UserTypeEnum") (isStrEqual enumName "UserStatusEnum")) - (isSupported clusterName enum=enumName enumValue="NotSupported"))}} - {{objCEnumName clusterName enumName}}{{objCEnumItemLabel "NotSupported"}} {{availability clusterName enum=enumName enumValue="NotSupported" deprecationMessage="This value is not part of the specification and will be removed"}} = 0xFF, - {{/if}} -} -{{/inline}} -{{#if (isSupported (asUpperCamelCase ../name preserveAcronyms=true) enum=(asUpperCamelCase label preserveAcronyms=true))}} -{{> enumDef name=name clusterName=(asUpperCamelCase ../name preserveAcronyms=true) enumName=(asUpperCamelCase label preserveAcronyms=true)}} {{availability (asUpperCamelCase ../name preserveAcronyms=true) enum=(asUpperCamelCase label preserveAcronyms=true) deprecationMessage="This enum is unused and will be removed"}}; -{{/if}} -{{! Takes the name of the enum to use as enumName. }} -{{#*inline "oldNameDecl"}} -{{#if (isSupported (compatClusterNameRemapping ../name) enum=enumName)}} +{{#if has_no_clusters}} +{{> enum_decl cluster="Globals" name=name enumLabel=label}} -{{> enumDef name=name clusterName=(compatClusterNameRemapping ../name) enumName=enumName}} {{availability (compatClusterNameRemapping ../name) enum=enumName deprecationMessage=(concat "Please use " (objCEnumName (asUpperCamelCase ../name preserveAcronyms=true) label))}}; {{/if}} -{{/inline}} -{{! Takes the old name of the enum, if any, as oldEnumName. }} -{{#*inline "oldNameCheck"}} -{{#if (or oldEnumName - (hasOldName (asUpperCamelCase ../name preserveAcronyms=true)))}} -{{#if oldEnumName}} -{{> oldNameDecl enumName=oldEnumName}} -{{else}} -{{> oldNameDecl enumName=(asUpperCamelCase label preserveAcronyms=true)}} -{{/if}} -{{/if}} -{{/inline}} -{{> oldNameCheck oldEnumName=(oldName (asUpperCamelCase ../name preserveAcronyms=true) enum=(asUpperCamelCase label preserveAcronyms=true))}} +{{/zcl_enums}} + +{{#zcl_clusters}} +{{#zcl_enums}} +{{> enum_decl cluster=../name name=name enumLabel=label}} {{/zcl_enums}} {{#zcl_bitmaps}} diff --git a/src/darwin/Framework/CHIP/templates/MTRStructsObjc-src.zapt b/src/darwin/Framework/CHIP/templates/MTRStructsObjc-src.zapt index 8c9372f87bc9ff..fee9de42e3c4af 100644 --- a/src/darwin/Framework/CHIP/templates/MTRStructsObjc-src.zapt +++ b/src/darwin/Framework/CHIP/templates/MTRStructsObjc-src.zapt @@ -4,62 +4,15 @@ NS_ASSUME_NONNULL_BEGIN -{{#zcl_clusters}} {{#zcl_structs}} -{{#*inline "interfaceImpl"}} -@implementation {{interfaceName}} -- (instancetype)init -{ - if (self = [super init]) { - {{#zcl_struct_items}} - {{#if (isSupported (asUpperCamelCase parent.parent.name preserveAcronyms=true) struct=(asUpperCamelCase parent.name preserveAcronyms=true) structField=(asStructPropertyName label))}} - {{>init_struct_member label=label type=type cluster=parent.parent.name}} - {{/if}} - {{/zcl_struct_items}} - } - return self; -} - -- (id)copyWithZone:(NSZone * _Nullable)zone -{ - auto other = [[{{interfaceName}} alloc] init]; - - {{#zcl_struct_items}} - {{#if (isSupported (asUpperCamelCase parent.parent.name preserveAcronyms=true) struct=(asUpperCamelCase parent.name preserveAcronyms=true) structField=(asStructPropertyName label))}} - other.{{asStructPropertyName label}} = self.{{asStructPropertyName label}}; - {{/if}} - {{/zcl_struct_items}} - - return other; -} - -- (NSString *)description -{ - NSString *descriptionString = [NSString stringWithFormat:@"<%@: {{#zcl_struct_items~}} - {{~#if (isSupported (asUpperCamelCase parent.parent.name preserveAcronyms=true) struct=(asUpperCamelCase parent.name preserveAcronyms=true) structField=(asStructPropertyName label))~}} - {{~asStructPropertyName label}}:%@; {{!Just here to keep the preceding space}} - {{~/if~}} - {{~/zcl_struct_items}}>", NSStringFromClass([self class]){{#zcl_struct_items~}} - {{~#if (isSupported (asUpperCamelCase parent.parent.name preserveAcronyms=true) struct=(asUpperCamelCase parent.name preserveAcronyms=true) structField=(asStructPropertyName label))~}} - ,{{#if isArray}}_{{asStructPropertyName label}}{{else if (isOctetString type)}}[_{{asStructPropertyName label}} base64EncodedStringWithOptions:0]{{else}}_{{asStructPropertyName label}}{{/if}} - {{~/if~}} - {{~/zcl_struct_items}}]; - return descriptionString; -} -{{#zcl_struct_items}} -{{#if (and (hasOldName (asUpperCamelCase ../../name preserveAcronyms=true) struct=(asUpperCamelCase ../name preserveAcronyms=true) structField=(asStructPropertyName label)) - (isSupported (asUpperCamelCase ../../name preserveAcronyms=true) struct=(asUpperCamelCase ../name preserveAcronyms=true) structField=(oldName (asUpperCamelCase ../../name preserveAcronyms=true) struct=(asUpperCamelCase ../name preserveAcronyms=true) structField=(asStructPropertyName label))))}} - -{{> renamed_struct_field_impl cluster=../../name type=type newName=label oldName=(oldName (asUpperCamelCase ../../name preserveAcronyms=true) struct=(asUpperCamelCase ../name preserveAcronyms=true) structField=(asStructPropertyName label))}} +{{#if has_no_clusters}} +{{> struct_interface_impl cluster="Globals" struct=name}} {{/if}} -{{/zcl_struct_items}} - -@end -{{/inline}} +{{/zcl_structs}} -{{#if (isSupported (asUpperCamelCase parent.name preserveAcronyms=true) struct=(asUpperCamelCase name preserveAcronyms=true))}} -{{> interfaceImpl interfaceName=(concat "MTR" (asUpperCamelCase parent.name preserveAcronyms=true) "Cluster" (asUpperCamelCase name preserveAcronyms=true))}} -{{/if}} +{{#zcl_clusters}} +{{#zcl_structs}} +{{> struct_interface_impl cluster=parent.name struct=name}} {{! Takes the name of the struct to use as structName. }} {{#*inline "oldNameImpl"}} diff --git a/src/darwin/Framework/CHIP/templates/MTRStructsObjc.zapt b/src/darwin/Framework/CHIP/templates/MTRStructsObjc.zapt index 5e8c2528007182..5daa532a11a87b 100644 --- a/src/darwin/Framework/CHIP/templates/MTRStructsObjc.zapt +++ b/src/darwin/Framework/CHIP/templates/MTRStructsObjc.zapt @@ -4,36 +4,22 @@ NS_ASSUME_NONNULL_BEGIN -{{#zcl_clusters}} {{#zcl_structs}} -{{#*inline "interfaceDecl"}} -{{#zcl_struct_items}} -{{#if (isSupported ../cluster struct=../struct structField=(asStructPropertyName label))}} -{{> struct_field_decl cluster=../cluster type=type label=label}} {{availability (asUpperCamelCase ../cluster preserveAcronyms=true) struct=../struct structField=(asStructPropertyName label) deprecationMessage=(concat "Please use MTR" (asUpperCamelCase ../../name preserveAcronyms=true) "Cluster" (asUpperCamelCase ../name preserveAcronyms=true))}}; -{{/if}} -{{#if (hasOldName ../cluster struct=../struct structField=(asStructPropertyName label))}} -{{#if (isSupported ../cluster struct=../struct structField=(oldName ../cluster struct=../struct structField=(asStructPropertyName label)))}} -{{> struct_field_decl cluster=../cluster type=type label=(oldName ../cluster struct=../struct structField=(asStructPropertyName label))}} {{availability ../cluster struct=../struct structField=(oldName ../cluster struct=../struct structField=(asStructPropertyName label)) deprecationMessage=(concat "Please use " (asStructPropertyName label))}}; -{{/if}} +{{#if has_no_clusters}} +{{> struct_interface_decl cluster="Globals" originalCluster="Globals" struct=(asUpperCamelCase name preserveAcronyms=true) baseName="" deprecationMessage="This struct is unused and will be removed"}} {{/if}} -{{/zcl_struct_items}} -{{/inline}} -{{#if (isSupported (asUpperCamelCase parent.name preserveAcronyms=true) struct=(asUpperCamelCase name preserveAcronyms=true))}} -{{availability (asUpperCamelCase parent.name preserveAcronyms=true) struct=(asUpperCamelCase name preserveAcronyms=true) deprecationMessage="This struct is unused and will be removed"}} -@interface MTR{{asUpperCamelCase parent.name preserveAcronyms=true}}Cluster{{asUpperCamelCase name preserveAcronyms=true}} : NSObject -{{> interfaceDecl cluster=(asUpperCamelCase parent.name preserveAcronyms=true) struct=(asUpperCamelCase name preserveAcronyms=true)}} -@end +{{/zcl_structs}} -{{/if}} +{{#zcl_clusters}} +{{#zcl_structs}} +{{> struct_interface_decl cluster=(asUpperCamelCase parent.name preserveAcronyms=true) originalCluster=parent.name struct=(asUpperCamelCase name preserveAcronyms=true) baseName="" deprecationMessage="This struct is unused and will be removed"}} {{! Takes the name of the struct to use as structName. }} {{#*inline "oldNameDecl"}} -{{#if (isSupported (compatClusterNameRemapping parent.name) struct=structName)}} -{{availability (compatClusterNameRemapping parent.name) struct=structName deprecationMessage=(concat "Please use MTR" (asUpperCamelCase parent.name preserveAcronyms=true) "Cluster" (asUpperCamelCase name preserveAcronyms=true))}} -@interface MTR{{compatClusterNameRemapping parent.name}}Cluster{{structName}} : MTR{{asUpperCamelCase parent.name preserveAcronyms=true}}Cluster{{asUpperCamelCase name preserveAcronyms=true}} -{{> interfaceDecl cluster=(compatClusterNameRemapping parent.name) struct=structName}} -@end - -{{/if}} +{{> struct_interface_decl cluster=(compatClusterNameRemapping parent.name) + originalCluster=parent.name + struct=structName + baseName=(concat "MTR" (asUpperCamelCase parent.name preserveAcronyms=true) "Cluster" (asUpperCamelCase name preserveAcronyms=true)) + deprecationMessage=(concat "Please use MTR" (asUpperCamelCase parent.name preserveAcronyms=true) "Cluster" (asUpperCamelCase name preserveAcronyms=true))}} {{/inline}} {{! Takes the old name of the struct, if any, as oldStructName. }} {{#*inline "oldNameCheck"}} diff --git a/src/darwin/Framework/CHIP/templates/availability.yaml b/src/darwin/Framework/CHIP/templates/availability.yaml index 723133cb74d477..1189c61b0516f3 100644 --- a/src/darwin/Framework/CHIP/templates/availability.yaml +++ b/src/darwin/Framework/CHIP/templates/availability.yaml @@ -9717,3 +9717,12 @@ Feature: # Targeting 1.4 - ActionSwitch + removed: + structs: + Globals: + # Don't enable TestGlobalStruct until the ZAP side is fixed to handle it properly + - TestGlobalStruct + enums: + Globals: + # Don't enable TestGlobalEnum until the ZAP side is fixed to handle it properly + - TestGlobalEnum diff --git a/src/darwin/Framework/CHIP/templates/partials/enum_decl.zapt b/src/darwin/Framework/CHIP/templates/partials/enum_decl.zapt new file mode 100644 index 00000000000000..8ea1431bf20350 --- /dev/null +++ b/src/darwin/Framework/CHIP/templates/partials/enum_decl.zapt @@ -0,0 +1,46 @@ +{{! Arguments: cluster (might be "Globals", is not case-canonicalized), name, enumLabel }} +{{#*inline "enumDef"}} +typedef NS_ENUM({{asUnderlyingZclType name}}, {{objCEnumName clusterName enumName}}) { + {{#zcl_enum_items}} + {{#if (isSupported ../clusterName enum=../enumName enumValue=(asUpperCamelCase label preserveAcronyms=true))}} + {{objCEnumName ../clusterName ../enumName}}{{asUpperCamelCase label preserveAcronyms=true}} {{availability ../clusterName enum=../enumName enumValue=(asUpperCamelCase label preserveAcronyms=true) deprecationMessage=(concat "Please use " (objCEnumName (asUpperCamelCase ../cluster preserveAcronyms=true) ../enumLabel) (asUpperCamelCase label preserveAcronyms=true))}} = {{asHex value 2}}, + {{/if}} + {{#*inline "oldNameItemDecl"}} + {{#if oldItemName}} + {{#if (isSupported ../clusterName enum=../enumName enumValue=oldItemName)}} + {{objCEnumName ../clusterName ../enumName}}{{objCEnumItemLabel oldItemName}} {{availability ../clusterName enum=../enumName enumValue=oldItemName deprecationMessage=(concat "Please use " (objCEnumName (asUpperCamelCase ../cluster preserveAcronyms=true) ../enumLabel) (asUpperCamelCase label preserveAcronyms=true))}} = {{asHex value 2}}, + {{/if}} + {{/if}} + {{/inline}} + {{> oldNameItemDecl oldItemName=(oldName ../clusterName enum=../enumName enumValue=(asUpperCamelCase label preserveAcronyms=true))}} + {{/zcl_enum_items}} + {{!We had extra "Not Supported" values for DoorLockUserStatus/DoorLockUserType that we have to wedge in here manually for now.}} + {{#if (and (isStrEqual clusterName "DoorLock") + (or (isStrEqual enumName "UserTypeEnum") (isStrEqual enumName "UserStatusEnum")) + (isSupported clusterName enum=enumName enumValue="NotSupported"))}} + {{objCEnumName clusterName enumName}}{{objCEnumItemLabel "NotSupported"}} {{availability clusterName enum=enumName enumValue="NotSupported" deprecationMessage="This value is not part of the specification and will be removed"}} = 0xFF, + {{/if}} +} +{{/inline}} +{{#if (isSupported (asUpperCamelCase cluster preserveAcronyms=true) enum=(asUpperCamelCase enumLabel preserveAcronyms=true))}} +{{> enumDef name=name clusterName=(asUpperCamelCase cluster preserveAcronyms=true) enumName=(asUpperCamelCase enumLabel preserveAcronyms=true)}} {{availability (asUpperCamelCase cluster preserveAcronyms=true) enum=(asUpperCamelCase enumLabel preserveAcronyms=true) deprecationMessage="This enum is unused and will be removed"}}; +{{/if}} +{{! Takes the name of the enum to use as enumName. }} +{{#*inline "oldNameDecl"}} +{{#if (isSupported (compatClusterNameRemapping cluster) enum=enumName)}} + +{{> enumDef name=name clusterName=(compatClusterNameRemapping cluster) enumName=enumName}} {{availability (compatClusterNameRemapping cluster) enum=enumName deprecationMessage=(concat "Please use " (objCEnumName (asUpperCamelCase cluster preserveAcronyms=true) enumLabel))}}; +{{/if}} +{{/inline}} +{{! Takes the old name of the enum, if any, as oldEnumName. }} +{{#*inline "oldNameCheck"}} +{{#if (or oldEnumName + (hasOldName (asUpperCamelCase cluster preserveAcronyms=true)))}} +{{#if oldEnumName}} +{{> oldNameDecl enumName=oldEnumName}} +{{else}} +{{> oldNameDecl enumName=(asUpperCamelCase enumLabel preserveAcronyms=true)}} +{{/if}} +{{/if}} +{{/inline}} +{{> oldNameCheck oldEnumName=(oldName (asUpperCamelCase cluster preserveAcronyms=true) enum=(asUpperCamelCase enumLabel preserveAcronyms=true))}} diff --git a/src/darwin/Framework/CHIP/templates/partials/struct_interface_decl.zapt b/src/darwin/Framework/CHIP/templates/partials/struct_interface_decl.zapt new file mode 100644 index 00000000000000..69ec6d38a11286 --- /dev/null +++ b/src/darwin/Framework/CHIP/templates/partials/struct_interface_decl.zapt @@ -0,0 +1,25 @@ +{{! Arguments: cluster (might be "Globals", is case-canonicalized already), originalCluster (the name before remapping and whatnot), struct, baseName (might be "" to indicate NSObject), deprecationMessage }} +{{#if (isSupported cluster struct=struct)}} +{{availability cluster struct=struct deprecationMessage=deprecationMessage}} +@interface {{#if (isStrEqual cluster "Globals") ~}} + MTRDataType{{struct}} +{{~else~}} + MTR{{cluster}}Cluster{{struct}} +{{~/if}} : {{#if (isStrEqual baseName "")~}} + NSObject +{{~else~}} + {{baseName}} +{{~/if}} +{{#zcl_struct_items}} +{{#if (isSupported ../cluster struct=../struct structField=(asStructPropertyName label))}} +{{> struct_field_decl cluster=../cluster type=type label=label}} {{availability ../cluster struct=../struct structField=(asStructPropertyName label) deprecationMessage=(concat "Please use MTR" (asUpperCamelCase ../originalCluster preserveAcronyms=true) "Cluster" (asUpperCamelCase ../name preserveAcronyms=true))}}; +{{/if}} +{{#if (hasOldName ../cluster struct=../struct structField=(asStructPropertyName label))}} +{{#if (isSupported ../cluster struct=../struct structField=(oldName ../cluster struct=../struct structField=(asStructPropertyName label)))}} +{{> struct_field_decl cluster=../cluster type=type label=(oldName ../cluster struct=../struct structField=(asStructPropertyName label))}} {{availability ../cluster struct=../struct structField=(oldName ../cluster struct=../struct structField=(asStructPropertyName label)) deprecationMessage=(concat "Please use " (asStructPropertyName label))}}; +{{/if}} +{{/if}} +{{/zcl_struct_items}} +@end + +{{/if}} diff --git a/src/darwin/Framework/CHIP/templates/partials/struct_interface_impl.zapt b/src/darwin/Framework/CHIP/templates/partials/struct_interface_impl.zapt new file mode 100644 index 00000000000000..15a2db222f73c2 --- /dev/null +++ b/src/darwin/Framework/CHIP/templates/partials/struct_interface_impl.zapt @@ -0,0 +1,57 @@ +{{! Arguments: cluster (might be "Globals", not case-canonicalized), struct }} +{{! Avoid uppercasing stuff all the time by wrapping the whole thing in an inline that takes cluster, + originalCluster, and struct, where cluster and struct are uppercased }} +{{#*inline "interfaceImpl"}} +{{#if (isSupported cluster struct=struct)}} +@implementation {{asObjectiveCClass struct cluster}} +- (instancetype)init +{ + if (self = [super init]) { + {{#zcl_struct_items}} + {{#if (isSupported ../cluster struct=../struct structField=(asStructPropertyName label))}} + {{>init_struct_member label=label type=type cluster=../originalCluster}} + {{/if}} + {{/zcl_struct_items}} + } + return self; +} + +- (id)copyWithZone:(NSZone * _Nullable)zone +{ + auto other = [[{{asObjectiveCClass struct cluster}} alloc] init]; + + {{#zcl_struct_items}} + {{#if (isSupported ../cluster struct=../struct structField=(asStructPropertyName label))}} + other.{{asStructPropertyName label}} = self.{{asStructPropertyName label}}; + {{/if}} + {{/zcl_struct_items}} + + return other; +} + +- (NSString *)description +{ + NSString *descriptionString = [NSString stringWithFormat:@"<%@: {{#zcl_struct_items~}} + {{~#if (isSupported ../cluster struct=../struct structField=(asStructPropertyName label))~}} + {{~asStructPropertyName label}}:%@; {{!Just here to keep the preceding space}} + {{~/if~}} + {{~/zcl_struct_items}}>", NSStringFromClass([self class]){{#zcl_struct_items~}} + {{~#if (isSupported ../cluster struct=../struct structField=(asStructPropertyName label))~}} + ,{{#if isArray}}_{{asStructPropertyName label}}{{else if (isOctetString type)}}[_{{asStructPropertyName label}} base64EncodedStringWithOptions:0]{{else}}_{{asStructPropertyName label}}{{/if}} + {{~/if~}} + {{~/zcl_struct_items}}]; + return descriptionString; +} +{{#zcl_struct_items}} +{{#if (and (hasOldName ../cluster struct=../struct structField=(asStructPropertyName label)) + (isSupported ../cluster struct=../struct structField=(oldName ../cluster struct=../struct structField=(asStructPropertyName label))))}} + +{{> renamed_struct_field_impl cluster=../originalCluster type=type newName=label oldName=(oldName ../cluster struct=../struct structField=(asStructPropertyName label))}} +{{/if}} +{{/zcl_struct_items}} + +@end + +{{/if}} +{{/inline}} +{{> interfaceImpl cluster=(asUpperCamelCase cluster preserveAcronyms=true) originalCluster=cluster struct=(asUpperCamelCase struct preserveAcronyms=true)}} diff --git a/src/darwin/Framework/CHIP/templates/templates.json b/src/darwin/Framework/CHIP/templates/templates.json index 83df8bdab40e63..df731d72ee2c4c 100644 --- a/src/darwin/Framework/CHIP/templates/templates.json +++ b/src/darwin/Framework/CHIP/templates/templates.json @@ -44,6 +44,18 @@ "name": "struct_field_decl", "path": "partials/struct_field_decl.zapt" }, + { + "name": "struct_interface_decl", + "path": "partials/struct_interface_decl.zapt" + }, + { + "name": "struct_interface_impl", + "path": "partials/struct_interface_impl.zapt" + }, + { + "name": "enum_decl", + "path": "partials/enum_decl.zapt" + }, { "name": "renamed_struct_field_impl", "path": "partials/renamed_struct_field_impl.zapt" From 4334e919f5334fd4d0ab5924b3cacdc4705ffecf Mon Sep 17 00:00:00 2001 From: Kamil Kasperczyk <66371704+kkasperczyk-no@users.noreply.github.com> Date: Fri, 26 Jul 2024 15:26:06 +0200 Subject: [PATCH 31/49] [nrfconnect] Fixed paths used to create flash bundle (#34528) Added nrfconnect directory in path used to copy bundle outputs. --- scripts/build/builders/nrf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build/builders/nrf.py b/scripts/build/builders/nrf.py index 6f650d23c7656a..f9c801da0b5dd1 100644 --- a/scripts/build/builders/nrf.py +++ b/scripts/build/builders/nrf.py @@ -239,6 +239,6 @@ def build_outputs(self): def bundle_outputs(self): if self.app == NrfApp.UNIT_TESTS: return - with open(os.path.join(self.output_dir, self.app.FlashBundleName())) as f: + with open(os.path.join(self.output_dir, 'nrfconnect', self.app.FlashBundleName())) as f: for line in filter(None, [x.strip() for x in f.readlines()]): - yield BuilderOutput(os.path.join(self.output_dir, line), line) + yield BuilderOutput(os.path.join(self.output_dir, 'nrfconnect', line), line) From a31319430d28e33d8f20bc017b4f16f763341bd0 Mon Sep 17 00:00:00 2001 From: C Freeman Date: Fri, 26 Jul 2024 10:35:25 -0400 Subject: [PATCH 32/49] TC-IDM-10.5: Device type conformance - Add (#34424) * TC-IDM-10.5: Device type conformance - Add Initial test only looks at clusters. Remaining - revisions - feature conformance - cluster elements * Restyled by autopep8 * Restyled by isort * Add OTA requestor device type to door lock Then I can use it in the example. * add test to CI * linter * OTA requestor isn't hooked up, remove Let's go the other way and remove the cluster and the device type. * Revert "OTA requestor isn't hooked up, remove" This reverts commit b4bde38166ceff777ba22ebcd126692e00f5d56f. * Revert "Add OTA requestor device type to door lock" This reverts commit 51edb7925df17448085feb2adbc5bb9596430548. * Remove from CI until door lock OTA is sorted. --------- Co-authored-by: Restyled.io --- src/python_testing/TC_DeviceConformance.py | 99 +++++++++++- .../TestSpecParsingDeviceType.py | 149 +++++++++++++++++- src/python_testing/conformance_support.py | 4 +- 3 files changed, 242 insertions(+), 10 deletions(-) diff --git a/src/python_testing/TC_DeviceConformance.py b/src/python_testing/TC_DeviceConformance.py index c7fa19c45cec2c..c64a3470d19ed1 100644 --- a/src/python_testing/TC_DeviceConformance.py +++ b/src/python_testing/TC_DeviceConformance.py @@ -27,6 +27,7 @@ # test-runner-run/run1/script-args: --storage-path admin_storage.json --manual-code 10054912339 --bool-arg ignore_in_progress:True allow_provisional:True --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto --tests test_TC_IDM_10_2 # === END CI TEST ARGUMENTS === +# TODO: Enable 10.5 in CI once the door lock OTA requestor problem is sorted. from typing import Callable import chip.clusters as Clusters @@ -35,16 +36,19 @@ from choice_conformance_support import (evaluate_attribute_choice_conformance, evaluate_command_choice_conformance, evaluate_feature_choice_conformance) from conformance_support import ConformanceDecision, conformance_allowed -from global_attribute_ids import GlobalAttributeIds -from matter_testing_support import (AttributePathLocation, ClusterPathLocation, CommandPathLocation, MatterBaseTest, ProblemNotice, - ProblemSeverity, async_test_body, default_matter_test_main) -from spec_parsing_support import CommandType, build_xml_clusters +from global_attribute_ids import (ClusterIdType, DeviceTypeIdType, GlobalAttributeIds, cluster_id_type, device_type_id_type, + is_valid_device_type_id) +from matter_testing_support import (AttributePathLocation, ClusterPathLocation, CommandPathLocation, DeviceTypePathLocation, + MatterBaseTest, ProblemNotice, ProblemSeverity, async_test_body, default_matter_test_main) +from spec_parsing_support import CommandType, build_xml_clusters, build_xml_device_types class DeviceConformanceTests(BasicCompositionTests): async def setup_class_helper(self): await super().setup_class_helper() self.xml_clusters, self.problems = build_xml_clusters() + self.xml_device_types, problems = build_xml_device_types() + self.problems.extend(problems) def check_conformance(self, ignore_in_progress: bool, is_ci: bool): problems = [] @@ -245,6 +249,86 @@ def record_warning(location, problem): return success, problems + def check_device_type(self, fail_on_extra_clusters: bool = True, allow_provisional: bool = False) -> tuple[bool, list[ProblemNotice]]: + success = True + problems = [] + + def record_problem(location, problem, severity): + problems.append(ProblemNotice("IDM-10.5", location, severity, problem, "")) + + def record_error(location, problem): + nonlocal success + record_problem(location, problem, ProblemSeverity.ERROR) + success = False + + def record_warning(location, problem): + record_problem(location, problem, ProblemSeverity.WARNING) + + for endpoint_id, endpoint in self.endpoints.items(): + if Clusters.Descriptor not in endpoint: + location = ClusterPathLocation(endpoint_id=endpoint_id, cluster_id=Clusters.Descriptor.id) + record_error(location=location, problem='No descriptor cluster found on endpoint') + continue + + device_type_list = endpoint[Clusters.Descriptor][Clusters.Descriptor.Attributes.DeviceTypeList] + invalid_device_types = [x for x in device_type_list if not is_valid_device_type_id(device_type_id_type(x.deviceType))] + standard_device_types = [x for x in endpoint[Clusters.Descriptor] + [Clusters.Descriptor.Attributes.DeviceTypeList] if device_type_id_type(x.deviceType) == DeviceTypeIdType.kStandard] + endpoint_clusters = [] + server_clusters = [] + for device_type in invalid_device_types: + location = DeviceTypePathLocation(device_type_id=device_type.deviceType) + record_error(location=location, problem='Invalid device type ID (out of valid range)') + + for device_type in standard_device_types: + device_type_id = device_type.deviceType + location = DeviceTypePathLocation(device_type_id=device_type_id) + if device_type_id not in self.xml_device_types.keys(): + record_error(location=location, problem='Unknown device type ID in standard range') + continue + + if device_type_id not in self.xml_device_types.keys(): + location = DeviceTypePathLocation(device_type_id=device_type_id) + record_error(location=location, problem='Unknown device type') + continue + + # TODO: check revision. Possibly in another test? + + xml_device = self.xml_device_types[device_type_id] + # IDM 10.1 checks individual clusters for validity, + # so here we can ignore checks for invalid and manufacturer clusters. + server_clusters = [x for x in endpoint[Clusters.Descriptor] + [Clusters.Descriptor.Attributes.ServerList] if cluster_id_type(x) == ClusterIdType.kStandard] + + # As a start, we are only checking server clusters + # TODO: check client clusters too? + for cluster_id, cluster_requirement in xml_device.server_clusters.items(): + # Device type cluster conformances do not include any conformances based on cluster elements + conformance_decision_with_choice = cluster_requirement.conformance(0, [], []) + location = DeviceTypePathLocation(device_type_id=device_type_id, cluster_id=cluster_id) + if conformance_decision_with_choice.decision == ConformanceDecision.MANDATORY and cluster_id not in server_clusters: + record_error(location=location, + problem=f"Mandatory cluster {cluster_requirement.name} for device type {xml_device.name} is not present in the server list") + success = False + + if cluster_id in server_clusters and not conformance_allowed(conformance_decision_with_choice, allow_provisional): + record_error(location=location, + problem=f"Disallowed cluster {cluster_requirement.name} found in server list for device type {xml_device.name}") + success = False + # If we want to check for extra clusters on the endpoint, we need to know the entire set of clusters in all the device type + # lists across all the device types on the endpoint. + endpoint_clusters += xml_device.server_clusters.keys() + if fail_on_extra_clusters: + fn = record_error + else: + fn = record_warning + extra_clusters = set(server_clusters) - set(endpoint_clusters) + for extra in extra_clusters: + location = ClusterPathLocation(endpoint_id=endpoint_id, cluster_id=extra) + fn(location=location, problem=f"Extra cluster found on endpoint with device types {device_type_list}") + + return success, problems + class TC_DeviceConformance(MatterBaseTest, DeviceConformanceTests): @async_test_body @@ -267,6 +351,13 @@ def test_TC_IDM_10_3(self): if not success: self.fail_current_test("Problems with cluster revision on at least one cluster") + def test_TC_IDM_10_5(self): + fail_on_extra_clusters = self.user_params.get("fail_on_extra_clusters", True) + success, problems = self.check_device_type(fail_on_extra_clusters) + self.problems.extend(problems) + if not success: + self.fail_current_test("Problems with Device type conformance on one or more endpoints") + if __name__ == "__main__": default_matter_test_main() diff --git a/src/python_testing/TestSpecParsingDeviceType.py b/src/python_testing/TestSpecParsingDeviceType.py index bc199872f8f178..7729ccee8af24d 100644 --- a/src/python_testing/TestSpecParsingDeviceType.py +++ b/src/python_testing/TestSpecParsingDeviceType.py @@ -16,22 +16,27 @@ # import xml.etree.ElementTree as ElementTree +import chip.clusters as Clusters +from chip.clusters import Attribute +from chip.tlv import uint +from conformance_support import conformance_allowed from jinja2 import Template from matter_testing_support import MatterBaseTest, default_matter_test_main from mobly import asserts -from spec_parsing_support import build_xml_device_types, parse_single_device_type +from spec_parsing_support import build_xml_clusters, build_xml_device_types, parse_single_device_type +from TC_DeviceConformance import DeviceConformanceTests class TestSpecParsingDeviceType(MatterBaseTest): - # This just tests that the current spec can be parsed without failures def test_spec_device_parsing(self): - device_types, problems = build_xml_device_types() - self.problems += problems - for id, d in device_types.items(): + for id, d in self.xml_device_types.items(): print(str(d)) def setup_class(self): + self.xml_clusters, self.xml_cluster_problems = build_xml_clusters() + self.xml_device_types, self.xml_device_types_problems = build_xml_device_types() + self.device_type_id = 0xBBEF self.revision = 2 self.classification_class = "simple" @@ -106,6 +111,140 @@ def test_bad_scope(self): device_type, problems = parse_single_device_type(et) asserts.assert_equal(len(problems), 1, "Device with no scope did not generate a problem notice") + # All these tests are based on the temp sensor device type because it is very simple + # it requires temperature measurement, identify and the base devices. + # Right now I'm not testing for binding condition. + # The test is entirely based on the descriptor cluster so that's all I'm populating here + # because it makes the test less complex to write. + def create_test(self, server_list: list[uint], no_descriptor: bool = False, bad_device_id: bool = False) -> DeviceConformanceTests: + self.test = DeviceConformanceTests() + self.test.xml_device_types = self.xml_device_types + self.test.xml_clusters = self.xml_clusters + + if bad_device_id: + known_ids = list(self.test.xml_device_types.keys()) + device_type_id = [a for a in range(min(known_ids), max(known_ids)) if a not in known_ids][0] + else: + device_type_id = 0x0302 + + resp = Attribute.AsyncReadTransaction.ReadResponse({}, [], {}) + if no_descriptor: + resp.attributes = {1: {}} + else: + desc = Clusters.Descriptor + server_list_attr = Clusters.Descriptor.Attributes.ServerList + device_type_list_attr = Clusters.Descriptor.Attributes.DeviceTypeList + device_type_list = [Clusters.Descriptor.Structs.DeviceTypeStruct(deviceType=device_type_id, revision=2)] + resp.attributes = {1: {desc: {device_type_list_attr: device_type_list, server_list_attr: server_list}}} + self.test.endpoints = resp.attributes + + def create_good_device(self, device_type_id: int) -> DeviceConformanceTests: + self.test = DeviceConformanceTests() + self.test.xml_device_types = self.xml_device_types + self.test.xml_clusters = self.xml_clusters + + resp = Attribute.AsyncReadTransaction.ReadResponse({}, [], {}) + desc = Clusters.Descriptor + server_list_attr = Clusters.Descriptor.Attributes.ServerList + device_type_list_attr = Clusters.Descriptor.Attributes.DeviceTypeList + device_type_list = [Clusters.Descriptor.Structs.DeviceTypeStruct( + deviceType=device_type_id, revision=self.xml_device_types[device_type_id].revision)] + server_list = [k for k, v in self.xml_device_types[device_type_id].server_clusters.items( + ) if conformance_allowed(v.conformance(0, [], []), False)] + resp.attributes = {1: {desc: {device_type_list_attr: device_type_list, server_list_attr: server_list}}} + + self.test.endpoints = resp.attributes + + # Test with temp sensor with temp sensor, identify and descriptor + def test_ts_minimal_clusters(self): + self.create_test([Clusters.TemperatureMeasurement.id, Clusters.Identify.id, Clusters.Descriptor.id]) + success, problems = self.test.check_device_type(fail_on_extra_clusters=True) + if problems: + print(problems) + asserts.assert_true(success, "Failure on Temperature Sensor device type test") + + # Temp sensor with temp sensor, identify, descriptor, binding + def test_ts_minimal_with_binding(self): + self.create_test([Clusters.TemperatureMeasurement.id, Clusters.Identify.id, Clusters.Binding.id, Clusters.Descriptor.id]) + success, problems = self.test.check_device_type(fail_on_extra_clusters=True) + if problems: + print(problems) + asserts.assert_true(success, "Failure on Temperature Sensor device type test") + asserts.assert_false(problems, "Found problems on Temperature sensor device type test") + + # Temp sensor with temp sensor, identify, descriptor, fixed label + def test_ts_minimal_with_label(self): + self.create_test([Clusters.TemperatureMeasurement.id, Clusters.Identify.id, Clusters.FixedLabel.id, Clusters.Descriptor.id]) + success, problems = self.test.check_device_type(fail_on_extra_clusters=True) + if problems: + print(problems) + asserts.assert_true(success, "Failure on Temperature Sensor device type test") + asserts.assert_false(problems, "Found problems on Temperature sensor device type test") + + # Temp sensor with temp sensor, descriptor + def test_ts_missing_identify(self): + self.create_test([Clusters.TemperatureMeasurement.id, Clusters.Descriptor.id]) + success, problems = self.test.check_device_type(fail_on_extra_clusters=True) + if problems: + print(problems) + asserts.assert_equal(len(problems), 1, "Unexpected number of problems") + asserts.assert_false(success, "Unexpected success running test that should fail") + + # endpoint 1 empty + def test_endpoint_missing_descriptor(self): + self.create_test([], no_descriptor=True) + success, problems = self.test.check_device_type(fail_on_extra_clusters=True) + if problems: + print(problems) + asserts.assert_equal(len(problems), 1, "Unexpected number of problems") + asserts.assert_false(success, "Unexpected success running test that should fail") + + # Temp sensor with temp sensor, descriptor, identify, onoff + def test_ts_extra_cluster(self): + self.create_test([Clusters.TemperatureMeasurement.id, Clusters.Identify.id, Clusters.Descriptor.id, Clusters.OnOff.id]) + success, problems = self.test.check_device_type(fail_on_extra_clusters=True) + if problems: + print(problems) + asserts.assert_equal(len(problems), 1, "Unexpected number of problems") + asserts.assert_false(success, "Unexpected success running test that should fail") + + success, problems = self.test.check_device_type(fail_on_extra_clusters=False) + asserts.assert_equal(len(problems), 1, "Did not receive expected warning for extra clusters") + asserts.assert_true(success, "Unexpected failure") + + def test_bad_device_type_id_device_type_test(self): + self.create_test([], bad_device_id=True) + success, problems = self.test.check_device_type(fail_on_extra_clusters=True) + if problems: + print(problems) + asserts.assert_equal(len(problems), 1, "Unexpected number of problems") + asserts.assert_false(success, "Unexpected success running test that should fail") + + def test_all_device_types(self): + for id in self.xml_device_types.keys(): + self.create_good_device(id) + success, problems = self.test.check_device_type(fail_on_extra_clusters=True) + if problems: + print(problems) + asserts.assert_false(problems, f"Unexpected problems on device type {id}") + asserts.assert_true(success, f"Unexpected failure on device type {id}") + + def test_disallowed_cluster(self): + for id, dt in self.xml_device_types.items(): + expected_problems = 0 + self.create_good_device(id) + for cluster_id, cluster in dt.server_clusters.items(): + if not conformance_allowed(cluster.conformance(0, [], []), False): + self.test.endpoints[1][Clusters.Descriptor][Clusters.Descriptor.Attributes.ServerList].append(cluster_id) + expected_problems += 1 + if expected_problems == 0: + continue + success, problems = self.test.check_device_type(fail_on_extra_clusters=True) + if problems: + print(problems) + asserts.assert_equal(len(problems), expected_problems, "Unexpected number of problems") + asserts.assert_false(success, "Unexpected success running test that should fail") + if __name__ == "__main__": default_matter_test_main() diff --git a/src/python_testing/conformance_support.py b/src/python_testing/conformance_support.py index 6e439f1deb2b4d..90b6a4c3dd105c 100644 --- a/src/python_testing/conformance_support.py +++ b/src/python_testing/conformance_support.py @@ -331,7 +331,9 @@ def __call__(self, feature_map: uint, attribute_list: list[uint], all_command_li for op in self.op_list: decision_with_choice = op(feature_map, attribute_list, all_command_list) # and operations can't happen on optional or disallowed - if decision_with_choice.decision in [ConformanceDecision.OPTIONAL, ConformanceDecision.DISALLOWED, ConformanceDecision.PROVISIONAL]: + if decision_with_choice.decision == ConformanceDecision.OPTIONAL and all([type(op) == device_feature for op in self.op_list]): + return decision_with_choice + elif decision_with_choice.decision in [ConformanceDecision.OPTIONAL, ConformanceDecision.DISALLOWED, ConformanceDecision.PROVISIONAL]: raise ConformanceException('AND operation on optional or disallowed item') elif decision_with_choice.decision == ConformanceDecision.NOT_APPLICABLE: return decision_with_choice From 95bf668218ec9dccec94239e7a9cdd13163cc7aa Mon Sep 17 00:00:00 2001 From: Vatsal Ghelani <152916324+vatsalghelani-csa@users.noreply.github.com> Date: Fri, 26 Jul 2024 11:08:55 -0400 Subject: [PATCH 33/49] Fix metadata_parser python module to make it wheel buildable. (#34322) * Fix the metadata module to make the wheel buildable * Added the fix for metadata import * Added fixes as per module build for metadata_testing_infrastructure * Restyled by shfmt * Make CI checks run * Update build_python.sh * Delete admin_storage.json * Make metadata_parser python module wheel buildable within src/python_testing/matter_testing_infrastructure/ (#1) * Adding metadata buildable wheel to src/python_testing/matter_testing_infrastructure * Update build_python.sh * Delete src/python_testing/matter_testing_infrastructure/build/lib/metadata_parser directory --------- Co-authored-by: Restyled.io --- BUILD.gn | 4 +++- scripts/build_python.sh | 3 +++ scripts/tests/run_python_test.py | 2 +- .../python_testing/matter_testing_infrastructure}/BUILD.gn | 6 +++--- .../matter_testing_infrastructure}/env_test.yaml | 0 .../metadata_parser}/__init__.py | 0 .../metadata_parser}/metadata.py | 0 .../metadata_parser}/test_metadata.py | 0 .../matter_testing_infrastructure}/pyproject.toml | 0 .../python_testing/matter_testing_infrastructure}/setup.cfg | 2 +- .../python_testing/matter_testing_infrastructure}/setup.py | 0 11 files changed, 11 insertions(+), 6 deletions(-) rename {scripts/tests/py => src/python_testing/matter_testing_infrastructure}/BUILD.gn (88%) rename {scripts/tests/py => src/python_testing/matter_testing_infrastructure}/env_test.yaml (100%) rename {scripts/tests/py => src/python_testing/matter_testing_infrastructure/metadata_parser}/__init__.py (100%) rename {scripts/tests/py => src/python_testing/matter_testing_infrastructure/metadata_parser}/metadata.py (100%) rename {scripts/tests/py => src/python_testing/matter_testing_infrastructure/metadata_parser}/test_metadata.py (100%) rename {scripts/tests/py => src/python_testing/matter_testing_infrastructure}/pyproject.toml (100%) rename {scripts/tests/py => src/python_testing/matter_testing_infrastructure}/setup.cfg (95%) rename {scripts/tests/py => src/python_testing/matter_testing_infrastructure}/setup.py (100%) diff --git a/BUILD.gn b/BUILD.gn index ecf710efdf8a15..4d76100fbe6d20 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -67,6 +67,7 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") { "//examples/common/pigweed/rpc_console/py:chip_rpc", "//integrations/mobly:chip_mobly", "//scripts/py_matter_yamltests:matter_yamltests", + "//src/python_testing/matter_testing_infrastructure:metadata_parser", ] pw_python_venv("matter_build_venv") { @@ -107,6 +108,7 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") { deps = [ "${chip_root}/scripts:matter_yamltests_distribution.wheel", "${chip_root}/src/controller/python:chip-repl", + "${chip_root}/src/python_testing/matter_testing_infrastructure:metadata_parser.wheel", ] if (enable_pylib) { deps += [ "${chip_root}/src/pybindings/pycontroller" ] @@ -234,8 +236,8 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") { "//scripts/build:build_examples.tests", "//scripts/py_matter_idl:matter_idl.tests", "//scripts/py_matter_yamltests:matter_yamltests.tests", - "//scripts/tests/py:metadata_parser.tests", "//src:tests_run", + "//src/python_testing/matter_testing_infrastructure:metadata_parser.tests", ] if (current_os == "linux" || current_os == "mac") { diff --git a/scripts/build_python.sh b/scripts/build_python.sh index a946bb1b8f518b..e70b220130935d 100755 --- a/scripts/build_python.sh +++ b/scripts/build_python.sh @@ -212,6 +212,9 @@ else WHEEL=("$OUTPUT_ROOT"/controller/python/chip*.whl) fi +# Add the matter_testing_infrastructure wheel +WHEEL+=("$OUTPUT_ROOT"/python/obj/src/python_testing/matter_testing_infrastructure/metadata_parser._build_wheel/metadata_parser-*.whl) + if [ -n "$extra_packages" ]; then WHEEL+=("$extra_packages") fi diff --git a/scripts/tests/run_python_test.py b/scripts/tests/run_python_test.py index 91ee73e00d0e8d..61ec274bfc1c64 100755 --- a/scripts/tests/run_python_test.py +++ b/scripts/tests/run_python_test.py @@ -32,7 +32,7 @@ import click import coloredlogs from colorama import Fore, Style -from py.metadata import Metadata, MetadataReader +from metadata_parser.metadata import Metadata, MetadataReader DEFAULT_CHIP_ROOT = os.path.abspath( os.path.join(os.path.dirname(__file__), '..', '..')) diff --git a/scripts/tests/py/BUILD.gn b/src/python_testing/matter_testing_infrastructure/BUILD.gn similarity index 88% rename from scripts/tests/py/BUILD.gn rename to src/python_testing/matter_testing_infrastructure/BUILD.gn index e77297ac4e0955..f972040a3e64b5 100644 --- a/scripts/tests/py/BUILD.gn +++ b/src/python_testing/matter_testing_infrastructure/BUILD.gn @@ -28,9 +28,9 @@ pw_python_package("metadata_parser") { inputs = [ "env_test.yaml" ] sources = [ - "__init__.py", - "metadata.py", + "metadata_parser/__init__.py", + "metadata_parser/metadata.py", ] - tests = [ "test_metadata.py" ] + tests = [ "metadata_parser/test_metadata.py" ] } diff --git a/scripts/tests/py/env_test.yaml b/src/python_testing/matter_testing_infrastructure/env_test.yaml similarity index 100% rename from scripts/tests/py/env_test.yaml rename to src/python_testing/matter_testing_infrastructure/env_test.yaml diff --git a/scripts/tests/py/__init__.py b/src/python_testing/matter_testing_infrastructure/metadata_parser/__init__.py similarity index 100% rename from scripts/tests/py/__init__.py rename to src/python_testing/matter_testing_infrastructure/metadata_parser/__init__.py diff --git a/scripts/tests/py/metadata.py b/src/python_testing/matter_testing_infrastructure/metadata_parser/metadata.py similarity index 100% rename from scripts/tests/py/metadata.py rename to src/python_testing/matter_testing_infrastructure/metadata_parser/metadata.py diff --git a/scripts/tests/py/test_metadata.py b/src/python_testing/matter_testing_infrastructure/metadata_parser/test_metadata.py similarity index 100% rename from scripts/tests/py/test_metadata.py rename to src/python_testing/matter_testing_infrastructure/metadata_parser/test_metadata.py diff --git a/scripts/tests/py/pyproject.toml b/src/python_testing/matter_testing_infrastructure/pyproject.toml similarity index 100% rename from scripts/tests/py/pyproject.toml rename to src/python_testing/matter_testing_infrastructure/pyproject.toml diff --git a/scripts/tests/py/setup.cfg b/src/python_testing/matter_testing_infrastructure/setup.cfg similarity index 95% rename from scripts/tests/py/setup.cfg rename to src/python_testing/matter_testing_infrastructure/setup.cfg index 464e36c03d3e2f..d1cbadff10880c 100644 --- a/scripts/tests/py/setup.cfg +++ b/src/python_testing/matter_testing_infrastructure/setup.cfg @@ -16,4 +16,4 @@ name = metadata_parser version = 0.0.1 author = Project CHIP Authors -description = Scripts to get metadata (runner arguments) associated with the python_testing scripts +description = Scripts to get metadata (runner arguments) associated with the python_testing scripts \ No newline at end of file diff --git a/scripts/tests/py/setup.py b/src/python_testing/matter_testing_infrastructure/setup.py similarity index 100% rename from scripts/tests/py/setup.py rename to src/python_testing/matter_testing_infrastructure/setup.py From bdbf6acb5775bf0ded3b9c9f9c70d1c8a0c376c8 Mon Sep 17 00:00:00 2001 From: Terence Hampson Date: Fri, 26 Jul 2024 12:24:42 -0400 Subject: [PATCH 34/49] Spec rename of HomeLocationStruct to LocationDescriptorStruct (#34525) --- .../chip/ecosystem-information-cluster.xml | 2 +- .../zcl/data-model/chip/global-structs.xml | 2 +- .../data-model/chip/service-area-cluster.xml | 12 ++--- .../data_model/controller-clusters.matter | 10 ++-- .../chip/devicecontroller/ChipStructs.java | 36 ++++++------- .../chip/devicecontroller/cluster/files.gni | 4 +- ...formationClusterEcosystemLocationStruct.kt | 4 +- ...rmationClusterLocationDescriptorStruct.kt} | 15 ++++-- ...iceAreaClusterLocationDescriptorStruct.kt} | 8 +-- .../ServiceAreaClusterLocationInfoStruct.kt | 4 +- .../java/matter/controller/cluster/files.gni | 4 +- ...formationClusterEcosystemLocationStruct.kt | 4 +- ...rmationClusterLocationDescriptorStruct.kt} | 15 ++++-- ...iceAreaClusterLocationDescriptorStruct.kt} | 8 +-- .../ServiceAreaClusterLocationInfoStruct.kt | 4 +- .../CHIPAttributeTLVValueDecoder.cpp | 51 ++++++++++--------- .../python/chip/clusters/Objects.py | 12 ++--- .../MTRAttributeTLVValueDecoder.mm | 4 +- .../CHIP/zap-generated/MTRStructsObjc.h | 8 +-- .../CHIP/zap-generated/MTRStructsObjc.mm | 10 ++-- .../zap-generated/cluster-objects.cpp | 4 +- .../zap-generated/cluster-objects.h | 12 ++--- .../cluster/ComplexArgumentParser.cpp | 12 ++--- .../cluster/ComplexArgumentParser.h | 4 +- .../cluster/logging/DataModelLogger.cpp | 2 +- .../cluster/logging/DataModelLogger.h | 2 +- 26 files changed, 134 insertions(+), 119 deletions(-) rename src/controller/java/generated/java/chip/devicecontroller/cluster/structs/{EcosystemInformationClusterHomeLocationStruct.kt => EcosystemInformationClusterLocationDescriptorStruct.kt} (86%) rename src/controller/java/generated/java/chip/devicecontroller/cluster/structs/{ServiceAreaClusterHomeLocationStruct.kt => ServiceAreaClusterLocationDescriptorStruct.kt} (91%) rename src/controller/java/generated/java/matter/controller/cluster/structs/{EcosystemInformationClusterHomeLocationStruct.kt => EcosystemInformationClusterLocationDescriptorStruct.kt} (86%) rename src/controller/java/generated/java/matter/controller/cluster/structs/{ServiceAreaClusterHomeLocationStruct.kt => ServiceAreaClusterLocationDescriptorStruct.kt} (91%) diff --git a/src/app/zap-templates/zcl/data-model/chip/ecosystem-information-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/ecosystem-information-cluster.xml index 9a34cd3d02b20e..1b553eeb54faf6 100644 --- a/src/app/zap-templates/zcl/data-model/chip/ecosystem-information-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/ecosystem-information-cluster.xml @@ -30,7 +30,7 @@ limitations under the License. - + diff --git a/src/app/zap-templates/zcl/data-model/chip/global-structs.xml b/src/app/zap-templates/zcl/data-model/chip/global-structs.xml index 777630ec32b06d..04f477a6094f5e 100644 --- a/src/app/zap-templates/zcl/data-model/chip/global-structs.xml +++ b/src/app/zap-templates/zcl/data-model/chip/global-structs.xml @@ -23,7 +23,7 @@ TODO: Make these structures global rather than defining them for each cluster. - + diff --git a/src/app/zap-templates/zcl/data-model/chip/service-area-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/service-area-cluster.xml index 84e9a464336080..3332172500e451 100644 --- a/src/app/zap-templates/zcl/data-model/chip/service-area-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/service-area-cluster.xml @@ -18,12 +18,12 @@ limitations under the License. Data types - + - - - - + + + + @@ -71,7 +71,7 @@ limitations under the License. - + General Service Area The Service Area cluster provides an interface for controlling the locations where a device should operate, and for querying the current location. diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter index a1ed710504df8a..6831539e0c9def 100644 --- a/src/controller/data_model/controller-clusters.matter +++ b/src/controller/data_model/controller-clusters.matter @@ -6133,7 +6133,7 @@ deprecated cluster BarrierControl = 259 { } /** The Service Area cluster provides an interface for controlling the locations where a device should operate, and for querying the current location. */ -cluster ServiceArea = 336 { +provisional cluster ServiceArea = 336 { revision 1; // NOTE: Default/not specifically set enum AreaTypeTag : enum8 { @@ -6358,14 +6358,14 @@ cluster ServiceArea = 336 { kSelectWhileRunning = 0x2; } - struct HomeLocationStruct { + struct LocationDescriptorStruct { char_string<128> locationName = 0; nullable int16s floorNumber = 1; nullable AreaTypeTag areaType = 2; } struct LocationInfoStruct { - nullable HomeLocationStruct locationInfo = 0; + nullable LocationDescriptorStruct locationInfo = 0; nullable LandmarkTag landmarkTag = 1; nullable PositionTag positionTag = 2; nullable FloorSurfaceTag surfaceTag = 3; @@ -9347,7 +9347,7 @@ provisional cluster EcosystemInformation = 1872 { kWorkshop = 94; } - struct HomeLocationStruct { + struct LocationDescriptorStruct { char_string<128> locationName = 0; nullable int16s floorNumber = 1; nullable AreaTypeTag areaType = 2; @@ -9355,7 +9355,7 @@ provisional cluster EcosystemInformation = 1872 { fabric_scoped struct EcosystemLocationStruct { fabric_sensitive char_string<64> uniqueLocationID = 0; - fabric_sensitive HomeLocationStruct locationDescriptor = 1; + fabric_sensitive LocationDescriptorStruct locationDescriptor = 1; fabric_sensitive epoch_us locationDescriptorLastEdit = 2; fabric_idx fabricIndex = 254; } diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java b/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java index 075e3dbd45ed0e..90330bbdee3ae6 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java @@ -8943,7 +8943,7 @@ public String toString() { return output.toString(); } } -public static class ServiceAreaClusterHomeLocationStruct { +public static class ServiceAreaClusterLocationDescriptorStruct { public String locationName; public @Nullable Integer floorNumber; public @Nullable Integer areaType; @@ -8951,7 +8951,7 @@ public static class ServiceAreaClusterHomeLocationStruct { private static final long FLOOR_NUMBER_ID = 1L; private static final long AREA_TYPE_ID = 2L; - public ServiceAreaClusterHomeLocationStruct( + public ServiceAreaClusterLocationDescriptorStruct( String locationName, @Nullable Integer floorNumber, @Nullable Integer areaType @@ -8970,7 +8970,7 @@ public StructType encodeTlv() { return new StructType(values); } - public static ServiceAreaClusterHomeLocationStruct decodeTlv(BaseTLVType tlvValue) { + public static ServiceAreaClusterLocationDescriptorStruct decodeTlv(BaseTLVType tlvValue) { if (tlvValue == null || tlvValue.type() != TLVType.Struct) { return null; } @@ -8995,7 +8995,7 @@ public static ServiceAreaClusterHomeLocationStruct decodeTlv(BaseTLVType tlvValu } } } - return new ServiceAreaClusterHomeLocationStruct( + return new ServiceAreaClusterLocationDescriptorStruct( locationName, floorNumber, areaType @@ -9005,7 +9005,7 @@ public static ServiceAreaClusterHomeLocationStruct decodeTlv(BaseTLVType tlvValu @Override public String toString() { StringBuilder output = new StringBuilder(); - output.append("ServiceAreaClusterHomeLocationStruct {\n"); + output.append("ServiceAreaClusterLocationDescriptorStruct {\n"); output.append("\tlocationName: "); output.append(locationName); output.append("\n"); @@ -9020,7 +9020,7 @@ public String toString() { } } public static class ServiceAreaClusterLocationInfoStruct { - public @Nullable ChipStructs.ServiceAreaClusterHomeLocationStruct locationInfo; + public @Nullable ChipStructs.ServiceAreaClusterLocationDescriptorStruct locationInfo; public @Nullable Integer landmarkTag; public @Nullable Integer positionTag; public @Nullable Integer surfaceTag; @@ -9030,7 +9030,7 @@ public static class ServiceAreaClusterLocationInfoStruct { private static final long SURFACE_TAG_ID = 3L; public ServiceAreaClusterLocationInfoStruct( - @Nullable ChipStructs.ServiceAreaClusterHomeLocationStruct locationInfo, + @Nullable ChipStructs.ServiceAreaClusterLocationDescriptorStruct locationInfo, @Nullable Integer landmarkTag, @Nullable Integer positionTag, @Nullable Integer surfaceTag @@ -9055,7 +9055,7 @@ public static ServiceAreaClusterLocationInfoStruct decodeTlv(BaseTLVType tlvValu if (tlvValue == null || tlvValue.type() != TLVType.Struct) { return null; } - @Nullable ChipStructs.ServiceAreaClusterHomeLocationStruct locationInfo = null; + @Nullable ChipStructs.ServiceAreaClusterLocationDescriptorStruct locationInfo = null; @Nullable Integer landmarkTag = null; @Nullable Integer positionTag = null; @Nullable Integer surfaceTag = null; @@ -9063,7 +9063,7 @@ public static ServiceAreaClusterLocationInfoStruct decodeTlv(BaseTLVType tlvValu if (element.contextTagNum() == LOCATION_INFO_ID) { if (element.value(BaseTLVType.class).type() == TLVType.Struct) { StructType castingValue = element.value(StructType.class); - locationInfo = ChipStructs.ServiceAreaClusterHomeLocationStruct.decodeTlv(castingValue); + locationInfo = ChipStructs.ServiceAreaClusterLocationDescriptorStruct.decodeTlv(castingValue); } } else if (element.contextTagNum() == LANDMARK_TAG_ID) { if (element.value(BaseTLVType.class).type() == TLVType.UInt) { @@ -12283,7 +12283,7 @@ public String toString() { return output.toString(); } } -public static class EcosystemInformationClusterHomeLocationStruct { +public static class EcosystemInformationClusterLocationDescriptorStruct { public String locationName; public @Nullable Integer floorNumber; public @Nullable Integer areaType; @@ -12291,7 +12291,7 @@ public static class EcosystemInformationClusterHomeLocationStruct { private static final long FLOOR_NUMBER_ID = 1L; private static final long AREA_TYPE_ID = 2L; - public EcosystemInformationClusterHomeLocationStruct( + public EcosystemInformationClusterLocationDescriptorStruct( String locationName, @Nullable Integer floorNumber, @Nullable Integer areaType @@ -12310,7 +12310,7 @@ public StructType encodeTlv() { return new StructType(values); } - public static EcosystemInformationClusterHomeLocationStruct decodeTlv(BaseTLVType tlvValue) { + public static EcosystemInformationClusterLocationDescriptorStruct decodeTlv(BaseTLVType tlvValue) { if (tlvValue == null || tlvValue.type() != TLVType.Struct) { return null; } @@ -12335,7 +12335,7 @@ public static EcosystemInformationClusterHomeLocationStruct decodeTlv(BaseTLVTyp } } } - return new EcosystemInformationClusterHomeLocationStruct( + return new EcosystemInformationClusterLocationDescriptorStruct( locationName, floorNumber, areaType @@ -12345,7 +12345,7 @@ public static EcosystemInformationClusterHomeLocationStruct decodeTlv(BaseTLVTyp @Override public String toString() { StringBuilder output = new StringBuilder(); - output.append("EcosystemInformationClusterHomeLocationStruct {\n"); + output.append("EcosystemInformationClusterLocationDescriptorStruct {\n"); output.append("\tlocationName: "); output.append(locationName); output.append("\n"); @@ -12361,7 +12361,7 @@ public String toString() { } public static class EcosystemInformationClusterEcosystemLocationStruct { public String uniqueLocationID; - public ChipStructs.EcosystemInformationClusterHomeLocationStruct locationDescriptor; + public ChipStructs.EcosystemInformationClusterLocationDescriptorStruct locationDescriptor; public Long locationDescriptorLastEdit; public Integer fabricIndex; private static final long UNIQUE_LOCATION_I_D_ID = 0L; @@ -12371,7 +12371,7 @@ public static class EcosystemInformationClusterEcosystemLocationStruct { public EcosystemInformationClusterEcosystemLocationStruct( String uniqueLocationID, - ChipStructs.EcosystemInformationClusterHomeLocationStruct locationDescriptor, + ChipStructs.EcosystemInformationClusterLocationDescriptorStruct locationDescriptor, Long locationDescriptorLastEdit, Integer fabricIndex ) { @@ -12396,7 +12396,7 @@ public static EcosystemInformationClusterEcosystemLocationStruct decodeTlv(BaseT return null; } String uniqueLocationID = null; - ChipStructs.EcosystemInformationClusterHomeLocationStruct locationDescriptor = null; + ChipStructs.EcosystemInformationClusterLocationDescriptorStruct locationDescriptor = null; Long locationDescriptorLastEdit = null; Integer fabricIndex = null; for (StructElement element: ((StructType)tlvValue).value()) { @@ -12408,7 +12408,7 @@ public static EcosystemInformationClusterEcosystemLocationStruct decodeTlv(BaseT } else if (element.contextTagNum() == LOCATION_DESCRIPTOR_ID) { if (element.value(BaseTLVType.class).type() == TLVType.Struct) { StructType castingValue = element.value(StructType.class); - locationDescriptor = ChipStructs.EcosystemInformationClusterHomeLocationStruct.decodeTlv(castingValue); + locationDescriptor = ChipStructs.EcosystemInformationClusterLocationDescriptorStruct.decodeTlv(castingValue); } } else if (element.contextTagNum() == LOCATION_DESCRIPTOR_LAST_EDIT_ID) { if (element.value(BaseTLVType.class).type() == TLVType.UInt) { diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni b/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni index ea8a94605f8ca8..35bec1ec2b451e 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni @@ -59,7 +59,7 @@ structs_sources = [ "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterDeviceTypeStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterEcosystemDeviceStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt", - "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt", + "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterLocationDescriptorStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ElectricalEnergyMeasurementClusterCumulativeEnergyResetStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ElectricalEnergyMeasurementClusterEnergyMeasurementStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ElectricalEnergyMeasurementClusterMeasurementAccuracyRangeStruct.kt", @@ -120,7 +120,7 @@ structs_sources = [ "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ScenesManagementClusterAttributeValuePairStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ScenesManagementClusterExtensionFieldSet.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ScenesManagementClusterSceneInfoStruct.kt", - "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterHomeLocationStruct.kt", + "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterLocationDescriptorStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterLocationInfoStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterLocationStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterMapStruct.kt", diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt index c9db75b3082344..5b1b7103aaed67 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt @@ -24,7 +24,7 @@ import matter.tlv.TlvWriter class EcosystemInformationClusterEcosystemLocationStruct( val uniqueLocationID: String, - val locationDescriptor: EcosystemInformationClusterHomeLocationStruct, + val locationDescriptor: EcosystemInformationClusterLocationDescriptorStruct, val locationDescriptorLastEdit: ULong, val fabricIndex: UInt, ) { @@ -61,7 +61,7 @@ class EcosystemInformationClusterEcosystemLocationStruct( tlvReader.enterStructure(tlvTag) val uniqueLocationID = tlvReader.getString(ContextSpecificTag(TAG_UNIQUE_LOCATION_I_D)) val locationDescriptor = - EcosystemInformationClusterHomeLocationStruct.fromTlv( + EcosystemInformationClusterLocationDescriptorStruct.fromTlv( ContextSpecificTag(TAG_LOCATION_DESCRIPTOR), tlvReader, ) diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterLocationDescriptorStruct.kt similarity index 86% rename from src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt rename to src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterLocationDescriptorStruct.kt index 727c2276191f81..36a8de6b6f175a 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/EcosystemInformationClusterLocationDescriptorStruct.kt @@ -22,13 +22,13 @@ import matter.tlv.Tag import matter.tlv.TlvReader import matter.tlv.TlvWriter -class EcosystemInformationClusterHomeLocationStruct( +class EcosystemInformationClusterLocationDescriptorStruct( val locationName: String, val floorNumber: Int?, val areaType: UInt?, ) { override fun toString(): String = buildString { - append("EcosystemInformationClusterHomeLocationStruct {\n") + append("EcosystemInformationClusterLocationDescriptorStruct {\n") append("\tlocationName : $locationName\n") append("\tfloorNumber : $floorNumber\n") append("\tareaType : $areaType\n") @@ -58,7 +58,10 @@ class EcosystemInformationClusterHomeLocationStruct( private const val TAG_FLOOR_NUMBER = 1 private const val TAG_AREA_TYPE = 2 - fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): EcosystemInformationClusterHomeLocationStruct { + fun fromTlv( + tlvTag: Tag, + tlvReader: TlvReader, + ): EcosystemInformationClusterLocationDescriptorStruct { tlvReader.enterStructure(tlvTag) val locationName = tlvReader.getString(ContextSpecificTag(TAG_LOCATION_NAME)) val floorNumber = @@ -78,7 +81,11 @@ class EcosystemInformationClusterHomeLocationStruct( tlvReader.exitContainer() - return EcosystemInformationClusterHomeLocationStruct(locationName, floorNumber, areaType) + return EcosystemInformationClusterLocationDescriptorStruct( + locationName, + floorNumber, + areaType, + ) } } } diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterHomeLocationStruct.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterLocationDescriptorStruct.kt similarity index 91% rename from src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterHomeLocationStruct.kt rename to src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterLocationDescriptorStruct.kt index 4eb2b2ea14784e..9a5362c443eab9 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterHomeLocationStruct.kt +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterLocationDescriptorStruct.kt @@ -22,13 +22,13 @@ import matter.tlv.Tag import matter.tlv.TlvReader import matter.tlv.TlvWriter -class ServiceAreaClusterHomeLocationStruct( +class ServiceAreaClusterLocationDescriptorStruct( val locationName: String, val floorNumber: Int?, val areaType: UInt?, ) { override fun toString(): String = buildString { - append("ServiceAreaClusterHomeLocationStruct {\n") + append("ServiceAreaClusterLocationDescriptorStruct {\n") append("\tlocationName : $locationName\n") append("\tfloorNumber : $floorNumber\n") append("\tareaType : $areaType\n") @@ -58,7 +58,7 @@ class ServiceAreaClusterHomeLocationStruct( private const val TAG_FLOOR_NUMBER = 1 private const val TAG_AREA_TYPE = 2 - fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): ServiceAreaClusterHomeLocationStruct { + fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): ServiceAreaClusterLocationDescriptorStruct { tlvReader.enterStructure(tlvTag) val locationName = tlvReader.getString(ContextSpecificTag(TAG_LOCATION_NAME)) val floorNumber = @@ -78,7 +78,7 @@ class ServiceAreaClusterHomeLocationStruct( tlvReader.exitContainer() - return ServiceAreaClusterHomeLocationStruct(locationName, floorNumber, areaType) + return ServiceAreaClusterLocationDescriptorStruct(locationName, floorNumber, areaType) } } } diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterLocationInfoStruct.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterLocationInfoStruct.kt index c6137199382840..3d3938fdbedf2b 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterLocationInfoStruct.kt +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterLocationInfoStruct.kt @@ -23,7 +23,7 @@ import matter.tlv.TlvReader import matter.tlv.TlvWriter class ServiceAreaClusterLocationInfoStruct( - val locationInfo: ServiceAreaClusterHomeLocationStruct?, + val locationInfo: ServiceAreaClusterLocationDescriptorStruct?, val landmarkTag: UInt?, val positionTag: UInt?, val surfaceTag: UInt?, @@ -74,7 +74,7 @@ class ServiceAreaClusterLocationInfoStruct( tlvReader.enterStructure(tlvTag) val locationInfo = if (!tlvReader.isNull()) { - ServiceAreaClusterHomeLocationStruct.fromTlv( + ServiceAreaClusterLocationDescriptorStruct.fromTlv( ContextSpecificTag(TAG_LOCATION_INFO), tlvReader, ) diff --git a/src/controller/java/generated/java/matter/controller/cluster/files.gni b/src/controller/java/generated/java/matter/controller/cluster/files.gni index 6b02ff0ca05bbb..fd3fafa315b932 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/files.gni +++ b/src/controller/java/generated/java/matter/controller/cluster/files.gni @@ -59,7 +59,7 @@ matter_structs_sources = [ "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterDeviceTypeStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterEcosystemDeviceStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt", - "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt", + "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterLocationDescriptorStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/ElectricalEnergyMeasurementClusterCumulativeEnergyResetStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/ElectricalEnergyMeasurementClusterEnergyMeasurementStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/ElectricalEnergyMeasurementClusterMeasurementAccuracyRangeStruct.kt", @@ -120,7 +120,7 @@ matter_structs_sources = [ "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/ScenesManagementClusterAttributeValuePairStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/ScenesManagementClusterExtensionFieldSet.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/ScenesManagementClusterSceneInfoStruct.kt", - "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterHomeLocationStruct.kt", + "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterLocationDescriptorStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterLocationInfoStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterLocationStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterMapStruct.kt", diff --git a/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt b/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt index 0c218223efe1d7..5d44434897197d 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt +++ b/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterEcosystemLocationStruct.kt @@ -24,7 +24,7 @@ import matter.tlv.TlvWriter class EcosystemInformationClusterEcosystemLocationStruct( val uniqueLocationID: String, - val locationDescriptor: EcosystemInformationClusterHomeLocationStruct, + val locationDescriptor: EcosystemInformationClusterLocationDescriptorStruct, val locationDescriptorLastEdit: ULong, val fabricIndex: UByte, ) { @@ -61,7 +61,7 @@ class EcosystemInformationClusterEcosystemLocationStruct( tlvReader.enterStructure(tlvTag) val uniqueLocationID = tlvReader.getString(ContextSpecificTag(TAG_UNIQUE_LOCATION_I_D)) val locationDescriptor = - EcosystemInformationClusterHomeLocationStruct.fromTlv( + EcosystemInformationClusterLocationDescriptorStruct.fromTlv( ContextSpecificTag(TAG_LOCATION_DESCRIPTOR), tlvReader, ) diff --git a/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt b/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterLocationDescriptorStruct.kt similarity index 86% rename from src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt rename to src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterLocationDescriptorStruct.kt index 1ecbf220139dba..ddb0f9498efefc 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterHomeLocationStruct.kt +++ b/src/controller/java/generated/java/matter/controller/cluster/structs/EcosystemInformationClusterLocationDescriptorStruct.kt @@ -22,13 +22,13 @@ import matter.tlv.Tag import matter.tlv.TlvReader import matter.tlv.TlvWriter -class EcosystemInformationClusterHomeLocationStruct( +class EcosystemInformationClusterLocationDescriptorStruct( val locationName: String, val floorNumber: Short?, val areaType: UByte?, ) { override fun toString(): String = buildString { - append("EcosystemInformationClusterHomeLocationStruct {\n") + append("EcosystemInformationClusterLocationDescriptorStruct {\n") append("\tlocationName : $locationName\n") append("\tfloorNumber : $floorNumber\n") append("\tareaType : $areaType\n") @@ -58,7 +58,10 @@ class EcosystemInformationClusterHomeLocationStruct( private const val TAG_FLOOR_NUMBER = 1 private const val TAG_AREA_TYPE = 2 - fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): EcosystemInformationClusterHomeLocationStruct { + fun fromTlv( + tlvTag: Tag, + tlvReader: TlvReader, + ): EcosystemInformationClusterLocationDescriptorStruct { tlvReader.enterStructure(tlvTag) val locationName = tlvReader.getString(ContextSpecificTag(TAG_LOCATION_NAME)) val floorNumber = @@ -78,7 +81,11 @@ class EcosystemInformationClusterHomeLocationStruct( tlvReader.exitContainer() - return EcosystemInformationClusterHomeLocationStruct(locationName, floorNumber, areaType) + return EcosystemInformationClusterLocationDescriptorStruct( + locationName, + floorNumber, + areaType, + ) } } } diff --git a/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterHomeLocationStruct.kt b/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterLocationDescriptorStruct.kt similarity index 91% rename from src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterHomeLocationStruct.kt rename to src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterLocationDescriptorStruct.kt index 76eb3671a26962..fd24fa9218c22d 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterHomeLocationStruct.kt +++ b/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterLocationDescriptorStruct.kt @@ -22,13 +22,13 @@ import matter.tlv.Tag import matter.tlv.TlvReader import matter.tlv.TlvWriter -class ServiceAreaClusterHomeLocationStruct( +class ServiceAreaClusterLocationDescriptorStruct( val locationName: String, val floorNumber: Short?, val areaType: UByte?, ) { override fun toString(): String = buildString { - append("ServiceAreaClusterHomeLocationStruct {\n") + append("ServiceAreaClusterLocationDescriptorStruct {\n") append("\tlocationName : $locationName\n") append("\tfloorNumber : $floorNumber\n") append("\tareaType : $areaType\n") @@ -58,7 +58,7 @@ class ServiceAreaClusterHomeLocationStruct( private const val TAG_FLOOR_NUMBER = 1 private const val TAG_AREA_TYPE = 2 - fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): ServiceAreaClusterHomeLocationStruct { + fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): ServiceAreaClusterLocationDescriptorStruct { tlvReader.enterStructure(tlvTag) val locationName = tlvReader.getString(ContextSpecificTag(TAG_LOCATION_NAME)) val floorNumber = @@ -78,7 +78,7 @@ class ServiceAreaClusterHomeLocationStruct( tlvReader.exitContainer() - return ServiceAreaClusterHomeLocationStruct(locationName, floorNumber, areaType) + return ServiceAreaClusterLocationDescriptorStruct(locationName, floorNumber, areaType) } } } diff --git a/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterLocationInfoStruct.kt b/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterLocationInfoStruct.kt index 5c8b08507bb76f..d61927fb7ec852 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterLocationInfoStruct.kt +++ b/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterLocationInfoStruct.kt @@ -23,7 +23,7 @@ import matter.tlv.TlvReader import matter.tlv.TlvWriter class ServiceAreaClusterLocationInfoStruct( - val locationInfo: ServiceAreaClusterHomeLocationStruct?, + val locationInfo: ServiceAreaClusterLocationDescriptorStruct?, val landmarkTag: UByte?, val positionTag: UByte?, val surfaceTag: UByte?, @@ -74,7 +74,7 @@ class ServiceAreaClusterLocationInfoStruct( tlvReader.enterStructure(tlvTag) val locationInfo = if (!tlvReader.isNull()) { - ServiceAreaClusterHomeLocationStruct.fromTlv( + ServiceAreaClusterLocationDescriptorStruct.fromTlv( ContextSpecificTag(TAG_LOCATION_INFO), tlvReader, ) diff --git a/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp b/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp index 20a00dd454ae7f..b6702569d994d9 100644 --- a/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp +++ b/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp @@ -28377,28 +28377,28 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR jninewElement_0_locationInfo_locationInfo_areaType, newElement_0_locationInfo_locationInfo_areaType); } - jclass homeLocationStructStructClass_4; + jclass locationDescriptorStructStructClass_4; err = chip::JniReferences::GetInstance().GetLocalClassRef( - env, "chip/devicecontroller/ChipStructs$ServiceAreaClusterHomeLocationStruct", - homeLocationStructStructClass_4); + env, "chip/devicecontroller/ChipStructs$ServiceAreaClusterLocationDescriptorStruct", + locationDescriptorStructStructClass_4); if (err != CHIP_NO_ERROR) { - ChipLogError(Zcl, "Could not find class ChipStructs$ServiceAreaClusterHomeLocationStruct"); + ChipLogError(Zcl, "Could not find class ChipStructs$ServiceAreaClusterLocationDescriptorStruct"); return nullptr; } - jmethodID homeLocationStructStructCtor_4; + jmethodID locationDescriptorStructStructCtor_4; err = chip::JniReferences::GetInstance().FindMethod( - env, homeLocationStructStructClass_4, "", - "(Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;)V", &homeLocationStructStructCtor_4); - if (err != CHIP_NO_ERROR || homeLocationStructStructCtor_4 == nullptr) + env, locationDescriptorStructStructClass_4, "", + "(Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;)V", &locationDescriptorStructStructCtor_4); + if (err != CHIP_NO_ERROR || locationDescriptorStructStructCtor_4 == nullptr) { - ChipLogError(Zcl, "Could not find ChipStructs$ServiceAreaClusterHomeLocationStruct constructor"); + ChipLogError(Zcl, "Could not find ChipStructs$ServiceAreaClusterLocationDescriptorStruct constructor"); return nullptr; } newElement_0_locationInfo_locationInfo = env->NewObject( - homeLocationStructStructClass_4, homeLocationStructStructCtor_4, + locationDescriptorStructStructClass_4, locationDescriptorStructStructCtor_4, newElement_0_locationInfo_locationInfo_locationName, newElement_0_locationInfo_locationInfo_floorNumber, newElement_0_locationInfo_locationInfo_areaType); } @@ -28460,7 +28460,7 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR jmethodID locationInfoStructStructCtor_2; err = chip::JniReferences::GetInstance().FindMethod( env, locationInfoStructStructClass_2, "", - "(Lchip/devicecontroller/ChipStructs$ServiceAreaClusterHomeLocationStruct;Ljava/lang/Integer;Ljava/lang/" + "(Lchip/devicecontroller/ChipStructs$ServiceAreaClusterLocationDescriptorStruct;Ljava/lang/Integer;Ljava/lang/" "Integer;Ljava/lang/Integer;)V", &locationInfoStructStructCtor_2); if (err != CHIP_NO_ERROR || locationInfoStructStructCtor_2 == nullptr) @@ -42915,29 +42915,30 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR newElement_0_locationDescriptor_areaType); } - jclass homeLocationStructStructClass_2; + jclass locationDescriptorStructStructClass_2; err = chip::JniReferences::GetInstance().GetLocalClassRef( - env, "chip/devicecontroller/ChipStructs$EcosystemInformationClusterHomeLocationStruct", - homeLocationStructStructClass_2); + env, "chip/devicecontroller/ChipStructs$EcosystemInformationClusterLocationDescriptorStruct", + locationDescriptorStructStructClass_2); if (err != CHIP_NO_ERROR) { - ChipLogError(Zcl, "Could not find class ChipStructs$EcosystemInformationClusterHomeLocationStruct"); + ChipLogError(Zcl, "Could not find class ChipStructs$EcosystemInformationClusterLocationDescriptorStruct"); return nullptr; } - jmethodID homeLocationStructStructCtor_2; - err = chip::JniReferences::GetInstance().FindMethod(env, homeLocationStructStructClass_2, "", + jmethodID locationDescriptorStructStructCtor_2; + err = chip::JniReferences::GetInstance().FindMethod(env, locationDescriptorStructStructClass_2, "", "(Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;)V", - &homeLocationStructStructCtor_2); - if (err != CHIP_NO_ERROR || homeLocationStructStructCtor_2 == nullptr) + &locationDescriptorStructStructCtor_2); + if (err != CHIP_NO_ERROR || locationDescriptorStructStructCtor_2 == nullptr) { - ChipLogError(Zcl, "Could not find ChipStructs$EcosystemInformationClusterHomeLocationStruct constructor"); + ChipLogError(Zcl, "Could not find ChipStructs$EcosystemInformationClusterLocationDescriptorStruct constructor"); return nullptr; } - newElement_0_locationDescriptor = env->NewObject( - homeLocationStructStructClass_2, homeLocationStructStructCtor_2, newElement_0_locationDescriptor_locationName, - newElement_0_locationDescriptor_floorNumber, newElement_0_locationDescriptor_areaType); + newElement_0_locationDescriptor = + env->NewObject(locationDescriptorStructStructClass_2, locationDescriptorStructStructCtor_2, + newElement_0_locationDescriptor_locationName, newElement_0_locationDescriptor_floorNumber, + newElement_0_locationDescriptor_areaType); jobject newElement_0_locationDescriptorLastEdit; std::string newElement_0_locationDescriptorLastEditClassName = "java/lang/Long"; std::string newElement_0_locationDescriptorLastEditCtorSignature = "(J)V"; @@ -42967,8 +42968,8 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR jmethodID ecosystemLocationStructStructCtor_1; err = chip::JniReferences::GetInstance().FindMethod( env, ecosystemLocationStructStructClass_1, "", - "(Ljava/lang/String;Lchip/devicecontroller/ChipStructs$EcosystemInformationClusterHomeLocationStruct;Ljava/" - "lang/Long;Ljava/lang/Integer;)V", + "(Ljava/lang/String;Lchip/devicecontroller/" + "ChipStructs$EcosystemInformationClusterLocationDescriptorStruct;Ljava/lang/Long;Ljava/lang/Integer;)V", &ecosystemLocationStructStructCtor_1); if (err != CHIP_NO_ERROR || ecosystemLocationStructStructCtor_1 == nullptr) { diff --git a/src/controller/python/chip/clusters/Objects.py b/src/controller/python/chip/clusters/Objects.py index 2ad8d02fb1aae2..29aca6d33be3b8 100644 --- a/src/controller/python/chip/clusters/Objects.py +++ b/src/controller/python/chip/clusters/Objects.py @@ -30824,7 +30824,7 @@ class Feature(IntFlag): class Structs: @dataclass - class HomeLocationStruct(ClusterObject): + class LocationDescriptorStruct(ClusterObject): @ChipUtility.classproperty def descriptor(cls) -> ClusterObjectDescriptor: return ClusterObjectDescriptor( @@ -30844,13 +30844,13 @@ class LocationInfoStruct(ClusterObject): def descriptor(cls) -> ClusterObjectDescriptor: return ClusterObjectDescriptor( Fields=[ - ClusterObjectFieldDescriptor(Label="locationInfo", Tag=0, Type=typing.Union[Nullable, ServiceArea.Structs.HomeLocationStruct]), + ClusterObjectFieldDescriptor(Label="locationInfo", Tag=0, Type=typing.Union[Nullable, ServiceArea.Structs.LocationDescriptorStruct]), ClusterObjectFieldDescriptor(Label="landmarkTag", Tag=1, Type=typing.Union[Nullable, ServiceArea.Enums.LandmarkTag]), ClusterObjectFieldDescriptor(Label="positionTag", Tag=2, Type=typing.Union[Nullable, ServiceArea.Enums.PositionTag]), ClusterObjectFieldDescriptor(Label="surfaceTag", Tag=3, Type=typing.Union[Nullable, ServiceArea.Enums.FloorSurfaceTag]), ]) - locationInfo: 'typing.Union[Nullable, ServiceArea.Structs.HomeLocationStruct]' = NullValue + locationInfo: 'typing.Union[Nullable, ServiceArea.Structs.LocationDescriptorStruct]' = NullValue landmarkTag: 'typing.Union[Nullable, ServiceArea.Enums.LandmarkTag]' = NullValue positionTag: 'typing.Union[Nullable, ServiceArea.Enums.PositionTag]' = NullValue surfaceTag: 'typing.Union[Nullable, ServiceArea.Enums.FloorSurfaceTag]' = NullValue @@ -46767,7 +46767,7 @@ class AreaTypeTag(MatterIntEnum): class Structs: @dataclass - class HomeLocationStruct(ClusterObject): + class LocationDescriptorStruct(ClusterObject): @ChipUtility.classproperty def descriptor(cls) -> ClusterObjectDescriptor: return ClusterObjectDescriptor( @@ -46788,13 +46788,13 @@ def descriptor(cls) -> ClusterObjectDescriptor: return ClusterObjectDescriptor( Fields=[ ClusterObjectFieldDescriptor(Label="uniqueLocationID", Tag=0, Type=str), - ClusterObjectFieldDescriptor(Label="locationDescriptor", Tag=1, Type=EcosystemInformation.Structs.HomeLocationStruct), + ClusterObjectFieldDescriptor(Label="locationDescriptor", Tag=1, Type=EcosystemInformation.Structs.LocationDescriptorStruct), ClusterObjectFieldDescriptor(Label="locationDescriptorLastEdit", Tag=2, Type=uint), ClusterObjectFieldDescriptor(Label="fabricIndex", Tag=254, Type=uint), ]) uniqueLocationID: 'str' = "" - locationDescriptor: 'EcosystemInformation.Structs.HomeLocationStruct' = field(default_factory=lambda: EcosystemInformation.Structs.HomeLocationStruct()) + locationDescriptor: 'EcosystemInformation.Structs.LocationDescriptorStruct' = field(default_factory=lambda: EcosystemInformation.Structs.LocationDescriptorStruct()) locationDescriptorLastEdit: 'uint' = 0 fabricIndex: 'uint' = 0 diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm index c20bade614d8e3..ca3cc6c5e620eb 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm @@ -11045,7 +11045,7 @@ static id _Nullable DecodeAttributeValueForServiceAreaCluster(AttributeId aAttri if (entry_0.locationInfo.locationInfo.IsNull()) { newElement_0.locationInfo.locationInfo = nil; } else { - newElement_0.locationInfo.locationInfo = [MTRServiceAreaClusterHomeLocationStruct new]; + newElement_0.locationInfo.locationInfo = [MTRServiceAreaClusterLocationDescriptorStruct new]; newElement_0.locationInfo.locationInfo.locationName = AsString(entry_0.locationInfo.locationInfo.Value().locationName); if (newElement_0.locationInfo.locationInfo.locationName == nil) { CHIP_ERROR err = CHIP_ERROR_INVALID_ARGUMENT; @@ -17161,7 +17161,7 @@ static id _Nullable DecodeAttributeValueForEcosystemInformationCluster(Attribute *aError = err; return nil; } - newElement_0.locationDescriptor = [MTREcosystemInformationClusterHomeLocationStruct new]; + newElement_0.locationDescriptor = [MTREcosystemInformationClusterLocationDescriptorStruct new]; newElement_0.locationDescriptor.locationName = AsString(entry_0.locationDescriptor.locationName); if (newElement_0.locationDescriptor.locationName == nil) { CHIP_ERROR err = CHIP_ERROR_INVALID_ARGUMENT; diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h index f87d5b31dd75b5..b7e1502c30330d 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h @@ -1540,7 +1540,7 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) @end MTR_PROVISIONALLY_AVAILABLE -@interface MTRServiceAreaClusterHomeLocationStruct : NSObject +@interface MTRServiceAreaClusterLocationDescriptorStruct : NSObject @property (nonatomic, copy) NSString * _Nonnull locationName MTR_PROVISIONALLY_AVAILABLE; @property (nonatomic, copy) NSNumber * _Nullable floorNumber MTR_PROVISIONALLY_AVAILABLE; @property (nonatomic, copy) NSNumber * _Nullable areaType MTR_PROVISIONALLY_AVAILABLE; @@ -1548,7 +1548,7 @@ MTR_PROVISIONALLY_AVAILABLE MTR_PROVISIONALLY_AVAILABLE @interface MTRServiceAreaClusterLocationInfoStruct : NSObject -@property (nonatomic, copy) MTRServiceAreaClusterHomeLocationStruct * _Nullable locationInfo MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) MTRServiceAreaClusterLocationDescriptorStruct * _Nullable locationInfo MTR_PROVISIONALLY_AVAILABLE; @property (nonatomic, copy) NSNumber * _Nullable landmarkTag MTR_PROVISIONALLY_AVAILABLE; @property (nonatomic, copy) NSNumber * _Nullable positionTag MTR_PROVISIONALLY_AVAILABLE; @property (nonatomic, copy) NSNumber * _Nullable surfaceTag MTR_PROVISIONALLY_AVAILABLE; @@ -2048,7 +2048,7 @@ MTR_PROVISIONALLY_AVAILABLE @end MTR_PROVISIONALLY_AVAILABLE -@interface MTREcosystemInformationClusterHomeLocationStruct : NSObject +@interface MTREcosystemInformationClusterLocationDescriptorStruct : NSObject @property (nonatomic, copy) NSString * _Nonnull locationName MTR_PROVISIONALLY_AVAILABLE; @property (nonatomic, copy) NSNumber * _Nullable floorNumber MTR_PROVISIONALLY_AVAILABLE; @property (nonatomic, copy) NSNumber * _Nullable areaType MTR_PROVISIONALLY_AVAILABLE; @@ -2057,7 +2057,7 @@ MTR_PROVISIONALLY_AVAILABLE MTR_PROVISIONALLY_AVAILABLE @interface MTREcosystemInformationClusterEcosystemLocationStruct : NSObject @property (nonatomic, copy) NSString * _Nonnull uniqueLocationID MTR_PROVISIONALLY_AVAILABLE; -@property (nonatomic, copy) MTREcosystemInformationClusterHomeLocationStruct * _Nonnull locationDescriptor MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) MTREcosystemInformationClusterLocationDescriptorStruct * _Nonnull locationDescriptor MTR_PROVISIONALLY_AVAILABLE; @property (nonatomic, copy) NSNumber * _Nonnull locationDescriptorLastEdit MTR_PROVISIONALLY_AVAILABLE; @property (nonatomic, copy) NSNumber * _Nonnull fabricIndex MTR_PROVISIONALLY_AVAILABLE; @end diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm index dc7a0ad672bb25..410af5b8c7b2cc 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm @@ -6382,7 +6382,7 @@ - (NSString *)description @end -@implementation MTRServiceAreaClusterHomeLocationStruct +@implementation MTRServiceAreaClusterLocationDescriptorStruct - (instancetype)init { if (self = [super init]) { @@ -6398,7 +6398,7 @@ - (instancetype)init - (id)copyWithZone:(NSZone * _Nullable)zone { - auto other = [[MTRServiceAreaClusterHomeLocationStruct alloc] init]; + auto other = [[MTRServiceAreaClusterLocationDescriptorStruct alloc] init]; other.locationName = self.locationName; other.floorNumber = self.floorNumber; @@ -8427,7 +8427,7 @@ - (NSString *)description @end -@implementation MTREcosystemInformationClusterHomeLocationStruct +@implementation MTREcosystemInformationClusterLocationDescriptorStruct - (instancetype)init { if (self = [super init]) { @@ -8443,7 +8443,7 @@ - (instancetype)init - (id)copyWithZone:(NSZone * _Nullable)zone { - auto other = [[MTREcosystemInformationClusterHomeLocationStruct alloc] init]; + auto other = [[MTREcosystemInformationClusterLocationDescriptorStruct alloc] init]; other.locationName = self.locationName; other.floorNumber = self.floorNumber; @@ -8467,7 +8467,7 @@ - (instancetype)init _uniqueLocationID = @""; - _locationDescriptor = [MTREcosystemInformationClusterHomeLocationStruct new]; + _locationDescriptor = [MTREcosystemInformationClusterLocationDescriptorStruct new]; _locationDescriptorLastEdit = @(0); diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp index 9126422f85bd03..618bbb50b5ecb3 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp @@ -294,7 +294,7 @@ CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) } // namespace MeasurementAccuracyStruct -namespace HomeLocationStruct { +namespace LocationDescriptorStruct { CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const { DataModel::WrappedStructEncoder encoder{ aWriter, aTag }; @@ -338,7 +338,7 @@ CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) } } -} // namespace HomeLocationStruct +} // namespace LocationDescriptorStruct namespace DeviceTypeStruct { CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h index c99987215c8316..6b9901ba351a58 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h @@ -173,7 +173,7 @@ struct DecodableType }; } // namespace MeasurementAccuracyStruct -namespace HomeLocationStruct { +namespace LocationDescriptorStruct { enum class Fields : uint8_t { kLocationName = 0, @@ -197,7 +197,7 @@ struct Type using DecodableType = Type; -} // namespace HomeLocationStruct +} // namespace LocationDescriptorStruct namespace DeviceTypeStruct { enum class Fields : uint8_t { @@ -27845,7 +27845,7 @@ struct TypeInfo } // namespace BarrierControl namespace ServiceArea { namespace Structs { -namespace HomeLocationStruct = Clusters::detail::Structs::HomeLocationStruct; +namespace LocationDescriptorStruct = Clusters::detail::Structs::LocationDescriptorStruct; namespace LocationInfoStruct { enum class Fields : uint8_t { @@ -27858,7 +27858,7 @@ enum class Fields : uint8_t struct Type { public: - DataModel::Nullable locationInfo; + DataModel::Nullable locationInfo; DataModel::Nullable landmarkTag; DataModel::Nullable positionTag; DataModel::Nullable surfaceTag; @@ -41176,7 +41176,7 @@ struct TypeInfo } // namespace ContentAppObserver namespace EcosystemInformation { namespace Structs { -namespace HomeLocationStruct = Clusters::detail::Structs::HomeLocationStruct; +namespace LocationDescriptorStruct = Clusters::detail::Structs::LocationDescriptorStruct; namespace EcosystemLocationStruct { enum class Fields : uint8_t { @@ -41190,7 +41190,7 @@ struct Type { public: chip::CharSpan uniqueLocationID; - Structs::HomeLocationStruct::Type locationDescriptor; + Structs::LocationDescriptorStruct::Type locationDescriptor; uint64_t locationDescriptorLastEdit = static_cast(0); chip::FabricIndex fabricIndex = static_cast(0); diff --git a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp index d78bc468849928..76a2d92d51a154 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp +++ b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp @@ -220,7 +220,7 @@ void ComplexArgumentParser::Finalize(chip::app::Clusters::detail::Structs::Measu } CHIP_ERROR ComplexArgumentParser::Setup(const char * label, - chip::app::Clusters::detail::Structs::HomeLocationStruct::Type & request, + chip::app::Clusters::detail::Structs::LocationDescriptorStruct::Type & request, Json::Value & value) { VerifyOrReturnError(value.isObject(), CHIP_ERROR_INVALID_ARGUMENT); @@ -228,12 +228,12 @@ CHIP_ERROR ComplexArgumentParser::Setup(const char * label, // Copy to track which members we already processed. Json::Value valueCopy(value); - ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("HomeLocationStruct.locationName", "locationName", + ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("LocationDescriptorStruct.locationName", "locationName", value.isMember("locationName"))); + ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("LocationDescriptorStruct.floorNumber", "floorNumber", + value.isMember("floorNumber"))); ReturnErrorOnFailure( - ComplexArgumentParser::EnsureMemberExist("HomeLocationStruct.floorNumber", "floorNumber", value.isMember("floorNumber"))); - ReturnErrorOnFailure( - ComplexArgumentParser::EnsureMemberExist("HomeLocationStruct.areaType", "areaType", value.isMember("areaType"))); + ComplexArgumentParser::EnsureMemberExist("LocationDescriptorStruct.areaType", "areaType", value.isMember("areaType"))); char labelWithMember[kMaxLabelLength]; snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "locationName"); @@ -251,7 +251,7 @@ CHIP_ERROR ComplexArgumentParser::Setup(const char * label, return ComplexArgumentParser::EnsureNoMembersRemaining(label, valueCopy); } -void ComplexArgumentParser::Finalize(chip::app::Clusters::detail::Structs::HomeLocationStruct::Type & request) +void ComplexArgumentParser::Finalize(chip::app::Clusters::detail::Structs::LocationDescriptorStruct::Type & request) { ComplexArgumentParser::Finalize(request.locationName); ComplexArgumentParser::Finalize(request.floorNumber); diff --git a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.h b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.h index f777963970303c..6a6c29d57ff790 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.h @@ -42,10 +42,10 @@ static CHIP_ERROR Setup(const char * label, chip::app::Clusters::detail::Structs static void Finalize(chip::app::Clusters::detail::Structs::MeasurementAccuracyStruct::Type & request); -static CHIP_ERROR Setup(const char * label, chip::app::Clusters::detail::Structs::HomeLocationStruct::Type & request, +static CHIP_ERROR Setup(const char * label, chip::app::Clusters::detail::Structs::LocationDescriptorStruct::Type & request, Json::Value & value); -static void Finalize(chip::app::Clusters::detail::Structs::HomeLocationStruct::Type & request); +static void Finalize(chip::app::Clusters::detail::Structs::LocationDescriptorStruct::Type & request); static CHIP_ERROR Setup(const char * label, chip::app::Clusters::detail::Structs::DeviceTypeStruct::Type & request, Json::Value & value); diff --git a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp index 1fade3e28c9c22..76880f90f6cfd4 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp +++ b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp @@ -203,7 +203,7 @@ CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, } CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, - const chip::app::Clusters::detail::Structs::HomeLocationStruct::DecodableType & value) + const chip::app::Clusters::detail::Structs::LocationDescriptorStruct::DecodableType & value) { DataModelLogger::LogString(label, indent, "{"); { diff --git a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h index 7680e14c314316..e7faf1c9a35b88 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h @@ -33,7 +33,7 @@ static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::detail::Structs::MeasurementAccuracyStruct::DecodableType & value); static CHIP_ERROR LogValue(const char * label, size_t indent, - const chip::app::Clusters::detail::Structs::HomeLocationStruct::DecodableType & value); + const chip::app::Clusters::detail::Structs::LocationDescriptorStruct::DecodableType & value); static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::detail::Structs::DeviceTypeStruct::DecodableType & value); From 48be002741f2b87fe8554ec9961cbfc0d248b523 Mon Sep 17 00:00:00 2001 From: Alex Tsitsiura Date: Fri, 26 Jul 2024 19:26:59 +0300 Subject: [PATCH 35/49] Update compatible builds to docker version 66 (#34529) --- .github/workflows/bloat_check.yaml | 2 +- .github/workflows/build.yaml | 4 ++-- .github/workflows/chef.yaml | 8 ++++---- .github/workflows/doxygen.yaml | 2 +- .github/workflows/examples-asr.yaml | 2 +- .github/workflows/examples-efr32.yaml | 2 +- .github/workflows/examples-esp32.yaml | 4 ++-- .github/workflows/examples-linux-arm.yaml | 2 +- .github/workflows/examples-linux-standalone.yaml | 2 +- .../workflows/examples-linux-tv-casting-app.yaml | 2 +- .github/workflows/examples-mbed.yaml | 2 +- .github/workflows/examples-mw320.yaml | 2 +- .github/workflows/examples-nrfconnect.yaml | 2 +- .github/workflows/examples-nuttx.yaml | 2 +- .github/workflows/examples-nxp.yaml | 4 ++-- .github/workflows/examples-openiotsdk.yaml | 2 +- .github/workflows/examples-qpg.yaml | 2 +- .github/workflows/examples-rw61x.yaml | 2 +- .github/workflows/examples-stm32.yaml | 2 +- .github/workflows/examples-telink.yaml | 2 +- .github/workflows/examples-tizen.yaml | 2 +- .github/workflows/full-android.yaml | 2 +- .github/workflows/fuzzing-build.yaml | 2 +- .github/workflows/java-tests.yaml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/minimal-build.yaml | 4 ++-- .github/workflows/qemu.yaml | 2 +- .github/workflows/release_artifacts.yaml | 2 +- .github/workflows/smoketest-android.yaml | 2 +- .github/workflows/tests.yaml | 2 +- .github/workflows/unit_integration_test.yaml | 2 +- .github/workflows/zap_regeneration.yaml | 2 +- .github/workflows/zap_templates.yaml | 2 +- integrations/cloudbuild/chef.yaml | 8 ++++---- integrations/cloudbuild/smoke-test.yaml | 14 +++++++------- 35 files changed, 51 insertions(+), 51 deletions(-) diff --git a/.github/workflows/bloat_check.yaml b/.github/workflows/bloat_check.yaml index d9a2a506724188..12c7ee42e8faf7 100644 --- a/.github/workflows/bloat_check.yaml +++ b/.github/workflows/bloat_check.yaml @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-latest container: - image: ghcr.io/project-chip/chip-build:65 + image: ghcr.io/project-chip/chip-build:66 steps: - name: Checkout diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 5d8198013ed37b..10f638a67de092 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -42,7 +42,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build:65 + image: ghcr.io/project-chip/chip-build:66 volumes: - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" @@ -456,7 +456,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build:65 + image: ghcr.io/project-chip/chip-build:66 volumes: - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" diff --git a/.github/workflows/chef.yaml b/.github/workflows/chef.yaml index 70cb720eaa9d29..da12476696bce9 100644 --- a/.github/workflows/chef.yaml +++ b/.github/workflows/chef.yaml @@ -35,7 +35,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build:65 + image: ghcr.io/project-chip/chip-build:66 options: --user root steps: @@ -56,7 +56,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-esp32:65 + image: ghcr.io/project-chip/chip-build-esp32:66 options: --user root steps: @@ -77,7 +77,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-nrf-platform:65 + image: ghcr.io/project-chip/chip-build-nrf-platform:66 options: --user root steps: @@ -98,7 +98,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-telink:65 + image: ghcr.io/project-chip/chip-build-telink:66 options: --user root steps: diff --git a/.github/workflows/doxygen.yaml b/.github/workflows/doxygen.yaml index daaa70f158774a..df53d87c4a9d57 100644 --- a/.github/workflows/doxygen.yaml +++ b/.github/workflows/doxygen.yaml @@ -81,7 +81,7 @@ jobs: runs-on: ubuntu-latest container: - image: ghcr.io/project-chip/chip-build-doxygen:65 + image: ghcr.io/project-chip/chip-build-doxygen:66 if: github.actor != 'restyled-io[bot]' diff --git a/.github/workflows/examples-asr.yaml b/.github/workflows/examples-asr.yaml index 879ff5eb4b644d..b6fdcfd84945a0 100644 --- a/.github/workflows/examples-asr.yaml +++ b/.github/workflows/examples-asr.yaml @@ -36,7 +36,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-asr:65 + image: ghcr.io/project-chip/chip-build-asr:66 options: --user root steps: diff --git a/.github/workflows/examples-efr32.yaml b/.github/workflows/examples-efr32.yaml index 2d43e0f4a2eb0b..5a8f5359358063 100644 --- a/.github/workflows/examples-efr32.yaml +++ b/.github/workflows/examples-efr32.yaml @@ -40,7 +40,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-efr32:65 + image: ghcr.io/project-chip/chip-build-efr32:66 volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" steps: diff --git a/.github/workflows/examples-esp32.yaml b/.github/workflows/examples-esp32.yaml index 2ac5757d6fee0d..cdfdee65148aff 100644 --- a/.github/workflows/examples-esp32.yaml +++ b/.github/workflows/examples-esp32.yaml @@ -36,7 +36,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-esp32:65 + image: ghcr.io/project-chip/chip-build-esp32:66 volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" @@ -126,7 +126,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-esp32:65 + image: ghcr.io/project-chip/chip-build-esp32:66 volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" diff --git a/.github/workflows/examples-linux-arm.yaml b/.github/workflows/examples-linux-arm.yaml index 976ed23f564386..eec3e41ec875e8 100644 --- a/.github/workflows/examples-linux-arm.yaml +++ b/.github/workflows/examples-linux-arm.yaml @@ -36,7 +36,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-crosscompile:65 + image: ghcr.io/project-chip/chip-build-crosscompile:66 volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" diff --git a/.github/workflows/examples-linux-standalone.yaml b/.github/workflows/examples-linux-standalone.yaml index 0307eedc057bfd..5ece2df040e562 100644 --- a/.github/workflows/examples-linux-standalone.yaml +++ b/.github/workflows/examples-linux-standalone.yaml @@ -36,7 +36,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build:65 + image: ghcr.io/project-chip/chip-build:66 volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" diff --git a/.github/workflows/examples-linux-tv-casting-app.yaml b/.github/workflows/examples-linux-tv-casting-app.yaml index 744c40b2654421..08005687062a49 100644 --- a/.github/workflows/examples-linux-tv-casting-app.yaml +++ b/.github/workflows/examples-linux-tv-casting-app.yaml @@ -36,7 +36,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build:65 + image: ghcr.io/project-chip/chip-build:66 steps: - name: Checkout diff --git a/.github/workflows/examples-mbed.yaml b/.github/workflows/examples-mbed.yaml index e66757447d3e28..5b39c51e8fa0a8 100644 --- a/.github/workflows/examples-mbed.yaml +++ b/.github/workflows/examples-mbed.yaml @@ -42,7 +42,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-mbed-os:65 + image: ghcr.io/project-chip/chip-build-mbed-os:66 volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" diff --git a/.github/workflows/examples-mw320.yaml b/.github/workflows/examples-mw320.yaml index 4b07c513db5d24..cd4081c51cd164 100644 --- a/.github/workflows/examples-mw320.yaml +++ b/.github/workflows/examples-mw320.yaml @@ -39,7 +39,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build:65 + image: ghcr.io/project-chip/chip-build:66 volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" steps: diff --git a/.github/workflows/examples-nrfconnect.yaml b/.github/workflows/examples-nrfconnect.yaml index 591641c08e74eb..3e0452b5258dc8 100644 --- a/.github/workflows/examples-nrfconnect.yaml +++ b/.github/workflows/examples-nrfconnect.yaml @@ -39,7 +39,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-nrf-platform:65 + image: ghcr.io/project-chip/chip-build-nrf-platform:66 volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" diff --git a/.github/workflows/examples-nuttx.yaml b/.github/workflows/examples-nuttx.yaml index 91581f54ac39e0..3fc296f4a6478a 100644 --- a/.github/workflows/examples-nuttx.yaml +++ b/.github/workflows/examples-nuttx.yaml @@ -35,7 +35,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-nuttx:65 + image: ghcr.io/project-chip/chip-build-nuttx:66 volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" steps: diff --git a/.github/workflows/examples-nxp.yaml b/.github/workflows/examples-nxp.yaml index 5ee57c520d1e94..d3ea4af06df3dc 100644 --- a/.github/workflows/examples-nxp.yaml +++ b/.github/workflows/examples-nxp.yaml @@ -39,7 +39,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-k32w:65 + image: ghcr.io/project-chip/chip-build-k32w:66 volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" steps: @@ -104,7 +104,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-nxp-zephyr:65 + image: ghcr.io/project-chip/chip-build-nxp-zephyr:66 steps: - name: Checkout diff --git a/.github/workflows/examples-openiotsdk.yaml b/.github/workflows/examples-openiotsdk.yaml index 542fe97ee229bd..57577e02e774c7 100644 --- a/.github/workflows/examples-openiotsdk.yaml +++ b/.github/workflows/examples-openiotsdk.yaml @@ -40,7 +40,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-openiotsdk:65 + image: ghcr.io/project-chip/chip-build-openiotsdk:66 volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" options: --privileged diff --git a/.github/workflows/examples-qpg.yaml b/.github/workflows/examples-qpg.yaml index e015d3db3f5d08..9c6f300a861429 100644 --- a/.github/workflows/examples-qpg.yaml +++ b/.github/workflows/examples-qpg.yaml @@ -39,7 +39,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build:65 + image: ghcr.io/project-chip/chip-build:66 volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" steps: diff --git a/.github/workflows/examples-rw61x.yaml b/.github/workflows/examples-rw61x.yaml index 3e411c653f236b..8766a9b1958e10 100644 --- a/.github/workflows/examples-rw61x.yaml +++ b/.github/workflows/examples-rw61x.yaml @@ -39,7 +39,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-rw61x:65 + image: ghcr.io/project-chip/chip-build-rw61x:66 volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" steps: diff --git a/.github/workflows/examples-stm32.yaml b/.github/workflows/examples-stm32.yaml index 47435add440808..897f0477089151 100644 --- a/.github/workflows/examples-stm32.yaml +++ b/.github/workflows/examples-stm32.yaml @@ -40,7 +40,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build:65 + image: ghcr.io/project-chip/chip-build:66 volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" steps: diff --git a/.github/workflows/examples-telink.yaml b/.github/workflows/examples-telink.yaml index b26453fa1bbb9d..22007fb77a3085 100644 --- a/.github/workflows/examples-telink.yaml +++ b/.github/workflows/examples-telink.yaml @@ -38,7 +38,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-telink:65 + image: ghcr.io/project-chip/chip-build-telink:66 volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" diff --git a/.github/workflows/examples-tizen.yaml b/.github/workflows/examples-tizen.yaml index d0d08121dc7321..8c1dc592d68676 100644 --- a/.github/workflows/examples-tizen.yaml +++ b/.github/workflows/examples-tizen.yaml @@ -36,7 +36,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-tizen:65 + image: ghcr.io/project-chip/chip-build-tizen:66 options: --user root volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" diff --git a/.github/workflows/full-android.yaml b/.github/workflows/full-android.yaml index 6e08a429fc61fd..0acfb215576caa 100644 --- a/.github/workflows/full-android.yaml +++ b/.github/workflows/full-android.yaml @@ -38,7 +38,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-android:65 + image: ghcr.io/project-chip/chip-build-android:66 volumes: - "/tmp/log_output:/tmp/test_logs" diff --git a/.github/workflows/fuzzing-build.yaml b/.github/workflows/fuzzing-build.yaml index 17be73a9346d39..e9f2061a2c033b 100644 --- a/.github/workflows/fuzzing-build.yaml +++ b/.github/workflows/fuzzing-build.yaml @@ -33,7 +33,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build:65 + image: ghcr.io/project-chip/chip-build:66 volumes: - "/tmp/log_output:/tmp/test_logs" diff --git a/.github/workflows/java-tests.yaml b/.github/workflows/java-tests.yaml index 07001f59fb94da..c70cfa6c37826d 100644 --- a/.github/workflows/java-tests.yaml +++ b/.github/workflows/java-tests.yaml @@ -42,7 +42,7 @@ jobs: runs-on: ubuntu-latest container: - image: ghcr.io/project-chip/chip-build-java:65 + image: ghcr.io/project-chip/chip-build-java:66 options: --privileged --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=0 net.ipv6.conf.all.forwarding=0" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index fb37a3cfec4f01..98dd81edceca88 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -35,7 +35,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build:65 + image: ghcr.io/project-chip/chip-build:66 steps: - name: Checkout diff --git a/.github/workflows/minimal-build.yaml b/.github/workflows/minimal-build.yaml index 694a7de54ab850..57ccaef312e144 100644 --- a/.github/workflows/minimal-build.yaml +++ b/.github/workflows/minimal-build.yaml @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-latest container: - image: ghcr.io/project-chip/chip-build-minimal:65 + image: ghcr.io/project-chip/chip-build-minimal:66 steps: - name: Checkout @@ -55,7 +55,7 @@ jobs: runs-on: ubuntu-latest container: - image: ghcr.io/project-chip/chip-build-minimal:65 + image: ghcr.io/project-chip/chip-build-minimal:66 steps: - name: Checkout diff --git a/.github/workflows/qemu.yaml b/.github/workflows/qemu.yaml index d5a03d2667dff7..acced0da5d9849 100644 --- a/.github/workflows/qemu.yaml +++ b/.github/workflows/qemu.yaml @@ -40,7 +40,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-esp32-qemu:65 + image: ghcr.io/project-chip/chip-build-esp32-qemu:66 volumes: - "/tmp/log_output:/tmp/test_logs" diff --git a/.github/workflows/release_artifacts.yaml b/.github/workflows/release_artifacts.yaml index efd9ea7c3deba8..97468ec3b5ce0c 100644 --- a/.github/workflows/release_artifacts.yaml +++ b/.github/workflows/release_artifacts.yaml @@ -32,7 +32,7 @@ jobs: runs-on: ubuntu-latest container: - image: ghcr.io/project-chip/chip-build-esp32:65 + image: ghcr.io/project-chip/chip-build-esp32:66 steps: - name: Checkout diff --git a/.github/workflows/smoketest-android.yaml b/.github/workflows/smoketest-android.yaml index 62255501a43aa6..f6e06fc1dbbaac 100644 --- a/.github/workflows/smoketest-android.yaml +++ b/.github/workflows/smoketest-android.yaml @@ -37,7 +37,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-android:65 + image: ghcr.io/project-chip/chip-build-android:66 volumes: - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 51832814ac548d..7992047b40155b 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -446,7 +446,7 @@ jobs: runs-on: ubuntu-latest container: - image: ghcr.io/project-chip/chip-build:65 + image: ghcr.io/project-chip/chip-build:66 options: --privileged --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=0 net.ipv6.conf.all.forwarding=0" diff --git a/.github/workflows/unit_integration_test.yaml b/.github/workflows/unit_integration_test.yaml index 59e2dafc30cd63..33f9cefae684fe 100644 --- a/.github/workflows/unit_integration_test.yaml +++ b/.github/workflows/unit_integration_test.yaml @@ -39,7 +39,7 @@ jobs: runs-on: ubuntu-latest container: - image: ghcr.io/project-chip/chip-build:65 + image: ghcr.io/project-chip/chip-build:66 volumes: - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" diff --git a/.github/workflows/zap_regeneration.yaml b/.github/workflows/zap_regeneration.yaml index 356c8922b9419e..68faf6b066e750 100644 --- a/.github/workflows/zap_regeneration.yaml +++ b/.github/workflows/zap_regeneration.yaml @@ -30,7 +30,7 @@ jobs: runs-on: ubuntu-20.04 container: - image: ghcr.io/project-chip/chip-build:65 + image: ghcr.io/project-chip/chip-build:66 defaults: run: shell: sh diff --git a/.github/workflows/zap_templates.yaml b/.github/workflows/zap_templates.yaml index 2e9d5e82f1df90..8ffe08e158c611 100644 --- a/.github/workflows/zap_templates.yaml +++ b/.github/workflows/zap_templates.yaml @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-20.04 container: - image: ghcr.io/project-chip/chip-build:65 + image: ghcr.io/project-chip/chip-build:66 defaults: run: shell: sh diff --git a/integrations/cloudbuild/chef.yaml b/integrations/cloudbuild/chef.yaml index e9ce65dfd45723..0c4035fcb60973 100644 --- a/integrations/cloudbuild/chef.yaml +++ b/integrations/cloudbuild/chef.yaml @@ -1,5 +1,5 @@ steps: - - name: "ghcr.io/project-chip/chip-build-vscode:65" + - name: "ghcr.io/project-chip/chip-build-vscode:66" entrypoint: "bash" args: - "-c" @@ -7,7 +7,7 @@ steps: git config --global --add safe.directory "*" python scripts/checkout_submodules.py --shallow --recursive --platform esp32 nrfconnect silabs linux android id: Submodules - - name: "ghcr.io/project-chip/chip-build-vscode:65" + - name: "ghcr.io/project-chip/chip-build-vscode:66" # NOTE: silabs boostrap is NOT done with the rest as it requests a conflicting # jinja2 version (asks for 3.1.3 when constraints.txt asks for 3.0.3) env: @@ -23,7 +23,7 @@ steps: - name: pwenv path: /pwenv timeout: 900s - - name: "ghcr.io/project-chip/chip-build-vscode:65" + - name: "ghcr.io/project-chip/chip-build-vscode:66" env: - PW_ENVIRONMENT_ROOT=/pwenv args: @@ -38,7 +38,7 @@ steps: - name: pwenv path: /pwenv - - name: "ghcr.io/project-chip/chip-build-vscode:65" + - name: "ghcr.io/project-chip/chip-build-vscode:66" env: - PW_ENVIRONMENT_ROOT=/pwenv args: diff --git a/integrations/cloudbuild/smoke-test.yaml b/integrations/cloudbuild/smoke-test.yaml index 07a24892ba1863..ef69af5db73481 100644 --- a/integrations/cloudbuild/smoke-test.yaml +++ b/integrations/cloudbuild/smoke-test.yaml @@ -1,5 +1,5 @@ steps: - - name: "ghcr.io/project-chip/chip-build-vscode:65" + - name: "ghcr.io/project-chip/chip-build-vscode:66" entrypoint: "bash" args: - "-c" @@ -7,7 +7,7 @@ steps: git config --global --add safe.directory "*" python scripts/checkout_submodules.py --shallow --recursive --platform esp32 nrfconnect silabs linux android id: Submodules - - name: "ghcr.io/project-chip/chip-build-vscode:65" + - name: "ghcr.io/project-chip/chip-build-vscode:66" # NOTE: silabs boostrap is NOT done with the rest as it requests a conflicting # jinja2 version (asks for 3.1.3 when constraints.txt asks for 3.0.3) env: @@ -24,7 +24,7 @@ steps: path: /pwenv timeout: 900s - - name: "ghcr.io/project-chip/chip-build-vscode:65" + - name: "ghcr.io/project-chip/chip-build-vscode:66" id: ESP32 env: - PW_ENVIRONMENT_ROOT=/pwenv @@ -45,7 +45,7 @@ steps: volumes: - name: pwenv path: /pwenv - - name: "ghcr.io/project-chip/chip-build-vscode:65" + - name: "ghcr.io/project-chip/chip-build-vscode:66" id: NRFConnect env: - PW_ENVIRONMENT_ROOT=/pwenv @@ -66,7 +66,7 @@ steps: - name: pwenv path: /pwenv - - name: "ghcr.io/project-chip/chip-build-vscode:65" + - name: "ghcr.io/project-chip/chip-build-vscode:66" id: EFR32 env: - PW_ENVIRONMENT_ROOT=/pwenv @@ -88,7 +88,7 @@ steps: - name: pwenv path: /pwenv - - name: "ghcr.io/project-chip/chip-build-vscode:65" + - name: "ghcr.io/project-chip/chip-build-vscode:66" id: Linux env: - PW_ENVIRONMENT_ROOT=/pwenv @@ -141,7 +141,7 @@ steps: - name: pwenv path: /pwenv - - name: "ghcr.io/project-chip/chip-build-vscode:65" + - name: "ghcr.io/project-chip/chip-build-vscode:66" id: Android env: - PW_ENVIRONMENT_ROOT=/pwenv From ade5285dd69b41c952256670d1b4ec81257fd261 Mon Sep 17 00:00:00 2001 From: Youngho Yoon <34558998+yhoyoon@users.noreply.github.com> Date: Sat, 27 Jul 2024 02:25:04 +0900 Subject: [PATCH 36/49] Fix UNSUPPORTED_ENDPOINT of TC-BR-4's step 1h (#34499) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix UNSUPPORTED_ENDPOINT of TC-BR-4's step 1h When testing step 1h of TC-BR-4 with bridge-app on the latest TH, an UNSUPPORTED_ENDPOINT error occurs. This is because ComposedDevice and ComposedPowerSource are local variables in the ApplicationInit function, so they are not valid when the bridge-app runs. This change moves ComposedDevice and ComposedPowerSource to global scope. Co-authored-by: Charles Kim Co-authored-by: Sanghee Kim Co-authored-by: Hunsup Jung Co-authored-by: sanghyukko Co-authored-by: Jaehoon You Co-authored-by: Kyu-Wook Lim Signed-off-by: Youngho Yoon <34558998+yhoyoon@users.noreply.github.com> * Fix ambiguous build error of bridge app ComposedDevice has the same type and variable name, so an ambiguous build error occurs as shown below. This change renames the variable to gComposeDevice. ../../main.cpp: In function ‘void ApplicationInit()’: ../../main.cpp:924:5: error: reference to ‘ComposedDevice’ is ambiguous 924 | ComposedDevice.SetReachable(true); | ^~~~~~~~~~~~~~ ../../main.cpp:174:16: note: candidates are: ‘ComposedDevice {anonymous}::ComposedDevice’ 174 | ComposedDevice ComposedDevice("Composed Device", "Bedroom"); | ^~~~~~~~~~~~~~ In file included from ../../main.cpp:46: ../../include/Device.h:158:7: note: ‘class ComposedDevice’ 158 | class ComposedDevice : public Device | ^~~~~~~~~~~~~~ ../../main.cpp:954:24: error: reference to ‘ComposedDevice’ is ambiguous 954 | AddDeviceEndpoint(&ComposedDevice, &bridgedComposedDeviceEndpoint, Span(gBridgedComposedDeviceTypes), | ^~~~~~~~~~~~~~ ../../main.cpp:174:16: note: candidates are: ‘ComposedDevice {anonymous}::ComposedDevice’ 174 | ComposedDevice ComposedDevice("Composed Device", "Bedroom"); | ^~~~~~~~~~~~~~ In file included from ../../main.cpp:46: ../../include/Device.h:158:7: note: ‘class ComposedDevice’ 158 | class ComposedDevice : public Device | ^~~~~~~~~~~~~~ ../../main.cpp:958:76: error: reference to ‘ComposedDevice’ is ambiguous 958 | Span(gComposedTempSensor1DataVersions), ComposedDevice.GetEndpointId()); | ^~~~~~~~~~~~~~ ../../main.cpp:174:16: note: candidates are: ‘ComposedDevice {anonymous}::ComposedDevice’ 174 | ComposedDevice ComposedDevice("Composed Device", "Bedroom"); | ^~~~~~~~~~~~~~ In file included from ../../main.cpp:46: ../../include/Device.h:158:7: note: ‘class ComposedDevice’ 158 | class ComposedDevice : public Device | ^~~~~~~~~~~~~~ ../../main.cpp:961:76: error: reference to ‘ComposedDevice’ is ambiguous 961 | Span(gComposedTempSensor2DataVersions), ComposedDevice.GetEndpointId()); | ^~~~~~~~~~~~~~ ../../main.cpp:174:16: note: candidates are: ‘ComposedDevice {anonymous}::ComposedDevice’ 174 | ComposedDevice ComposedDevice("Composed Device", "Bedroom"); | ^~~~~~~~~~~~~~ In file included from ../../main.cpp:46: ../../include/Device.h:158:7: note: ‘class ComposedDevice’ 158 | class ComposedDevice : public Device | ^~~~~~~~~~~~~~ ../../main.cpp:977:28: error: reference to ‘ComposedDevice’ is ambiguous 977 | endpointList.push_back(ComposedDevice.GetEndpointId()); | ^~~~~~~~~~~~~~ ../../main.cpp:174:16: note: candidates are: ‘ComposedDevice {anonymous}::ComposedDevice’ 174 | ComposedDevice ComposedDevice("Composed Device", "Bedroom"); | ^~~~~~~~~~~~~~ In file included from ../../main.cpp:46: ../../include/Device.h:158:7: note: ‘class ComposedDevice’ 158 | class ComposedDevice : public Device | ^~~~~~~~~~~~~~ ../../main.cpp:981:39: error: reference to ‘ComposedDevice’ is ambiguous 981 | ComposedPowerSource.SetEndpointId(ComposedDevice.GetEndpointId()); | ^~~~~~~~~~~~~~ ../../main.cpp:174:16: note: candidates are: ‘ComposedDevice {anonymous}::ComposedDevice’ 174 | ComposedDevice ComposedDevice("Composed Device", "Bedroom"); | ^~~~~~~~~~~~~~ In file included from ../../main.cpp:46: ../../include/Device.h:158:7: note: ‘class ComposedDevice’ 158 | class ComposedDevice : public Device | ^~~~~~~~~~~~~~ At global scope: cc1plus: error: unrecognized command line option ‘-Wno-unknown-warning-option’ [-Werror] cc1plus: all warnings being treated as errors ninja: build stopped: subcommand failed. Signed-off-by: Youngho Yoon <34558998+yhoyoon@users.noreply.github.com> --------- Signed-off-by: Youngho Yoon <34558998+yhoyoon@users.noreply.github.com> Co-authored-by: Charles Kim Co-authored-by: Sanghee Kim Co-authored-by: Hunsup Jung Co-authored-by: sanghyukko Co-authored-by: Jaehoon You Co-authored-by: Kyu-Wook Lim Co-authored-by: Andrei Litvin --- examples/bridge-app/linux/main.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/examples/bridge-app/linux/main.cpp b/examples/bridge-app/linux/main.cpp index 699a87fa8c7938..c7e1dfb5a9c50b 100644 --- a/examples/bridge-app/linux/main.cpp +++ b/examples/bridge-app/linux/main.cpp @@ -159,9 +159,6 @@ DeviceOnOff Light2("Light 2", "Office"); DeviceTempSensor TempSensor1("TempSensor 1", "Office", minMeasuredValue, maxMeasuredValue, initialMeasuredValue); DeviceTempSensor TempSensor2("TempSensor 2", "Office", minMeasuredValue, maxMeasuredValue, initialMeasuredValue); -DeviceTempSensor ComposedTempSensor1("Composed TempSensor 1", "Bedroom", minMeasuredValue, maxMeasuredValue, initialMeasuredValue); -DeviceTempSensor ComposedTempSensor2("Composed TempSensor 2", "Bedroom", minMeasuredValue, maxMeasuredValue, initialMeasuredValue); - // Declare Bridged endpoints used for Action clusters DataVersion gActionLight1DataVersions[ArraySize(bridgedLightClusters)]; DataVersion gActionLight2DataVersions[ArraySize(bridgedLightClusters)]; @@ -173,6 +170,12 @@ DeviceOnOff ActionLight2("Action Light 2", "Room 1"); DeviceOnOff ActionLight3("Action Light 3", "Room 2"); DeviceOnOff ActionLight4("Action Light 4", "Room 2"); +// Setup composed device with two temperature sensors and a power source +ComposedDevice gComposedDevice("Composed Device", "Bedroom"); +DeviceTempSensor ComposedTempSensor1("Composed TempSensor 1", "Bedroom", minMeasuredValue, maxMeasuredValue, initialMeasuredValue); +DeviceTempSensor ComposedTempSensor2("Composed TempSensor 2", "Bedroom", minMeasuredValue, maxMeasuredValue, initialMeasuredValue); +DevicePowerSource ComposedPowerSource("Composed Power Source", "Bedroom", PowerSource::Feature::kBattery); + Room room1("Room 1", 0xE001, Actions::EndpointListTypeEnum::kRoom, true); Room room2("Room 2", 0xE002, Actions::EndpointListTypeEnum::kRoom, true); Room room3("Zone 3", 0xE003, Actions::EndpointListTypeEnum::kZone, false); @@ -918,11 +921,7 @@ void ApplicationInit() ActionLight3.SetChangeCallback(&HandleDeviceOnOffStatusChanged); ActionLight4.SetChangeCallback(&HandleDeviceOnOffStatusChanged); - // Setup composed device with two temperature sensors and a power source - ComposedDevice ComposedDevice("Composed Device", "Bedroom"); - DevicePowerSource ComposedPowerSource("Composed Power Source", "Bedroom", PowerSource::Feature::kBattery); - - ComposedDevice.SetReachable(true); + gComposedDevice.SetReachable(true); ComposedTempSensor1.SetReachable(true); ComposedTempSensor2.SetReachable(true); ComposedPowerSource.SetReachable(true); @@ -952,14 +951,14 @@ void ApplicationInit() Span(gTempSensor2DataVersions), 1); // Add composed Device with two temperature sensors and a power source - AddDeviceEndpoint(&ComposedDevice, &bridgedComposedDeviceEndpoint, Span(gBridgedComposedDeviceTypes), + AddDeviceEndpoint(&gComposedDevice, &bridgedComposedDeviceEndpoint, Span(gBridgedComposedDeviceTypes), Span(gComposedDeviceDataVersions), 1); AddDeviceEndpoint(&ComposedTempSensor1, &bridgedTempSensorEndpoint, Span(gComposedTempSensorDeviceTypes), - Span(gComposedTempSensor1DataVersions), ComposedDevice.GetEndpointId()); + Span(gComposedTempSensor1DataVersions), gComposedDevice.GetEndpointId()); AddDeviceEndpoint(&ComposedTempSensor2, &bridgedTempSensorEndpoint, Span(gComposedTempSensorDeviceTypes), - Span(gComposedTempSensor2DataVersions), ComposedDevice.GetEndpointId()); + Span(gComposedTempSensor2DataVersions), gComposedDevice.GetEndpointId()); // Add 4 lights for the Action Clusters tests AddDeviceEndpoint(&ActionLight1, &bridgedLightEndpoint, Span(gBridgedOnOffDeviceTypes), @@ -975,11 +974,11 @@ void ApplicationInit() gDevices[CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT] = &ComposedPowerSource; // This provides power for the composed endpoint std::vector endpointList; - endpointList.push_back(ComposedDevice.GetEndpointId()); + endpointList.push_back(gComposedDevice.GetEndpointId()); endpointList.push_back(ComposedTempSensor1.GetEndpointId()); endpointList.push_back(ComposedTempSensor2.GetEndpointId()); ComposedPowerSource.SetEndpointList(endpointList); - ComposedPowerSource.SetEndpointId(ComposedDevice.GetEndpointId()); + ComposedPowerSource.SetEndpointId(gComposedDevice.GetEndpointId()); gRooms.push_back(&room1); gRooms.push_back(&room2); From 63d38ea10a6b5eea9526fbcb06571944d8a3e529 Mon Sep 17 00:00:00 2001 From: C Freeman Date: Fri, 26 Jul 2024 14:09:56 -0400 Subject: [PATCH 37/49] TC-PS-2.3:Add (#34512) * TC-PS-2.3:Add Test plan: https://github.com/CHIP-Specifications/chip-test-plans/pull/4308 * Restyled by autopep8 * Restyled by isort * linter --------- Co-authored-by: Restyled.io --- .github/workflows/tests.yaml | 1 + src/python_testing/TC_PS_2_3.py | 71 ++++++++++++++++++++ src/python_testing/matter_testing_support.py | 16 ++++- 3 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 src/python_testing/TC_PS_2_3.py diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 7992047b40155b..651a74296154fb 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -578,6 +578,7 @@ jobs: scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_MWOCTRL_2_2.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_MWOCTRL_2_4.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_MWOM_1_2.py' + scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_PS_2_3.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_RVCRUNM_1_2.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_RVCRUNM_2_1.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_RVCRUNM_2_2.py' diff --git a/src/python_testing/TC_PS_2_3.py b/src/python_testing/TC_PS_2_3.py new file mode 100644 index 00000000000000..72b54877dc5f95 --- /dev/null +++ b/src/python_testing/TC_PS_2_3.py @@ -0,0 +1,71 @@ +# +# 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. +# + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${ALL_CLUSTERS_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# === END CI TEST ARGUMENTS === + +import logging +import time + +import chip.clusters as Clusters +from matter_testing_support import (ClusterAttributeChangeAccumulator, MatterBaseTest, TestStep, async_test_body, + default_matter_test_main) +from mobly import asserts + + +class TC_PS_2_3(MatterBaseTest): + + def pics_TC_PS_2_3(self) -> list[str]: + return ["PWRTL.S"] + + def steps_TC_PS_2_3(self): + return [TestStep(1, "Commission DUT to TH", "", is_commissioning=True), + TestStep(2, "Subscribe to all attributes of the PowerSource Cluster"), + TestStep(3, "Accumulate all attribute reports on the endpoint under test for 30 seconds", + "For each of the attributes in the set of BatTimeToFullCharge, BatPercentRemaining and BatTimeRemaining, verify that there are not more than 4 reports per attribute where the value is non-null over the period of accumulation.")] + + @async_test_body + async def test_TC_PS_2_3(self): + # Commissioning, already done. + self.step(1) + + self.step(2) + ps = Clusters.PowerSource + sub_handler = ClusterAttributeChangeAccumulator(ps) + await sub_handler.start(self.default_controller, self.dut_node_id, self.matter_test_config.endpoint) + + self.step(3) + logging.info("This test will now wait for 30 seconds.") + time.sleep(30) + + counts = sub_handler.attribute_report_counts + asserts.assert_less_equal(counts[ps.Attributes.BatTimeToFullCharge], 4, "Too many reports for BatTimeToFullCharge") + asserts.assert_less_equal(counts[ps.Attributes.BatPercentRemaining], 4, "Too many reports for BatPercentRemaining") + asserts.assert_less_equal(counts[ps.Attributes.BatTimeRemaining], 4, "Too many reports for BatTimeRemaining") + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index 0fe12e777bc866..28385b22400d27 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -321,6 +321,7 @@ class AttributeValue: endpoint_id: int attribute: ClusterObjects.ClusterAttributeDescriptor value: Any + timestamp_utc: datetime class ClusterAttributeChangeAccumulator: @@ -328,8 +329,13 @@ def __init__(self, expected_cluster: ClusterObjects.Cluster): self._q = queue.Queue() self._expected_cluster = expected_cluster self._subscription = None + self._attribute_report_counts = {} + attrs = [cls for name, cls in inspect.getmembers(expected_cluster.Attributes) if inspect.isclass( + cls) and issubclass(cls, ClusterObjects.ClusterAttributeDescriptor)] + for a in attrs: + self._attribute_report_counts[a] = 0 - async def start(self, dev_ctrl, node_id: int, endpoint: int, fabric_filtered: bool = False, min_interval_sec: int = 0, max_interval_sec: int = 30) -> Any: + async def start(self, dev_ctrl, node_id: int, endpoint: int, fabric_filtered: bool = False, min_interval_sec: int = 0, max_interval_sec: int = 5) -> Any: """This starts a subscription for attributes on the specified node_id and endpoint. The cluster is specified when the class instance is created.""" self._subscription = await dev_ctrl.ReadAttribute( nodeid=node_id, @@ -346,14 +352,20 @@ def __call__(self, path: TypedAttributePath, transaction: SubscriptionTransactio It checks the report is from the expected_cluster and then posts it into the queue for later processing.""" if path.ClusterType == self._expected_cluster: data = transaction.GetAttribute(path) - value = AttributeValue(endpoint_id=path.Path.EndpointId, attribute=path.AttributeType, value=data) + value = AttributeValue(endpoint_id=path.Path.EndpointId, attribute=path.AttributeType, + value=data, timestamp_utc=datetime.now(timezone.utc)) logging.info(f"Got subscription report for {path.AttributeType}: {data}") self._q.put(value) + self._attribute_report_counts[path.AttributeType] += 1 @property def attribute_queue(self) -> queue.Queue: return self._q + @property + def attribute_report_counts(self) -> dict[ClusterObjects.ClusterAttributeDescriptor, int]: + return self._attribute_report_counts + class InternalTestRunnerHooks(TestRunnerHooks): From 420af5c461397461eadc93fc9229076a050e9db3 Mon Sep 17 00:00:00 2001 From: Terence Hampson Date: Fri, 26 Jul 2024 14:39:56 -0400 Subject: [PATCH 38/49] Add EcosystemInfo to chip-repl (#34542) --- src/controller/python/chip/clusters/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controller/python/chip/clusters/__init__.py b/src/controller/python/chip/clusters/__init__.py index f633ca272a48a7..11b6f6e02bcf30 100644 --- a/src/controller/python/chip/clusters/__init__.py +++ b/src/controller/python/chip/clusters/__init__.py @@ -28,7 +28,7 @@ BinaryInputBasic, Binding, BooleanState, BooleanStateConfiguration, BridgedDeviceBasicInformation, CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, Channel, ColorControl, ContentControl, ContentLauncher, DemandResponseLoadControl, Descriptor, DeviceEnergyManagement, - DeviceEnergyManagementMode, DiagnosticLogs, DishwasherAlarm, DishwasherMode, DoorLock, + DeviceEnergyManagementMode, DiagnosticLogs, DishwasherAlarm, DishwasherMode, DoorLock, EcosystemInformation, ElectricalEnergyMeasurement, ElectricalMeasurement, ElectricalPowerMeasurement, EnergyEvse, EnergyEvseMode, EnergyPreference, EthernetNetworkDiagnostics, FanControl, FaultInjection, FixedLabel, FlowMeasurement, FormaldehydeConcentrationMeasurement, GeneralCommissioning, GeneralDiagnostics, GroupKeyManagement, Groups, @@ -54,7 +54,7 @@ BinaryInputBasic, Binding, BooleanState, BooleanStateConfiguration, BridgedDeviceBasicInformation, CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, Channel, ColorControl, ContentControl, ContentLauncher, DemandResponseLoadControl, Descriptor, DeviceEnergyManagementMode, DeviceEnergyManagement, DeviceEnergyManagementMode, DiagnosticLogs, DishwasherAlarm, DishwasherMode, - DoorLock, ElectricalEnergyMeasurement, ElectricalMeasurement, ElectricalPowerMeasurement, EnergyEvse, EnergyEvseMode, EnergyPreference, + DoorLock, EcosystemInformation, ElectricalEnergyMeasurement, ElectricalMeasurement, ElectricalPowerMeasurement, EnergyEvse, EnergyEvseMode, EnergyPreference, EthernetNetworkDiagnostics, FanControl, FaultInjection, FixedLabel, FlowMeasurement, FormaldehydeConcentrationMeasurement, GeneralCommissioning, GeneralDiagnostics, GroupKeyManagement, Groups, HepaFilterMonitoring, IcdManagement, Identify, IlluminanceMeasurement, KeypadInput, LaundryDryerControls, From 2f7b94106b83377f5ce4fdd6d2997c338fff80db Mon Sep 17 00:00:00 2001 From: fesseha-eve <88329315+fessehaeve@users.noreply.github.com> Date: Fri, 26 Jul 2024 20:47:11 +0200 Subject: [PATCH 39/49] - update constraints for LocalTemperatureCalibration (#34536) - update yaml test - added LocalTemperatureCalibration in all-cluster-app and updated ci-pics --- .../all-clusters-common/all-clusters-app.matter | 1 + .../all-clusters-common/all-clusters-app.zap | 16 ++++++++++++++++ .../suites/certification/Test_TC_TSTAT_2_1.yaml | 4 ++-- .../tests/suites/certification/ci-pics-values | 2 +- .../zcl/data-model/chip/thermostat-cluster.xml | 2 +- 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter index 4313908d4fcdcb..3144546a72b849 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter @@ -8645,6 +8645,7 @@ endpoint 1 { ram attribute absMaxHeatSetpointLimit default = 0x0BB8; ram attribute absMinCoolSetpointLimit default = 0x0640; ram attribute absMaxCoolSetpointLimit default = 0x0C80; + ram attribute localTemperatureCalibration default = 0x00; ram attribute occupiedCoolingSetpoint default = 0x0A28; ram attribute occupiedHeatingSetpoint default = 0x07D0; ram attribute minHeatSetpointLimit default = 0x02BC; diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap index 3c9dcb094f8eb4..2ed20d53a62605 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap @@ -16255,6 +16255,22 @@ "maxInterval": 65344, "reportableChange": 0 }, + { + "name": "LocalTemperatureCalibration", + "code": 16, + "mfgCode": null, + "side": "server", + "type": "int8s", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x00", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "OccupiedCoolingSetpoint", "code": 17, diff --git a/src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml b/src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml index faf34fdea43e92..2db131a5a4df55 100644 --- a/src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml +++ b/src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml @@ -243,8 +243,8 @@ tests: response: constraints: type: int8s - minValue: -25 - maxValue: 25 + minValue: -127 + maxValue: 127 - label: "Step 13a: TH reads attribute OccupiedCoolingSetpoint from the DUT" PICS: TSTAT.S.F01 && TSTAT.S.A0017 && TSTAT.S.A0018 diff --git a/src/app/tests/suites/certification/ci-pics-values b/src/app/tests/suites/certification/ci-pics-values index aa8fb52d289d2b..207999a74778b1 100644 --- a/src/app/tests/suites/certification/ci-pics-values +++ b/src/app/tests/suites/certification/ci-pics-values @@ -1926,7 +1926,7 @@ TSTAT.S.A0006=1 TSTAT.S.A0007=0 TSTAT.S.A0008=0 TSTAT.S.A0009=0 -TSTAT.S.A0010=0 +TSTAT.S.A0010=1 TSTAT.S.A0011=1 TSTAT.S.A0012=1 TSTAT.S.A0013=0 diff --git a/src/app/zap-templates/zcl/data-model/chip/thermostat-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/thermostat-cluster.xml index 91a89497d7c8cc..796209999f1121 100644 --- a/src/app/zap-templates/zcl/data-model/chip/thermostat-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/thermostat-cluster.xml @@ -337,7 +337,7 @@ limitations under the License. - + LocalTemperatureCalibration From 93e5e85ddd023e0043d632f7b04e39420c0852a4 Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Fri, 26 Jul 2024 14:56:40 -0400 Subject: [PATCH 40/49] Add global types (structs/enums/bitmaps) to matter file parsing (#34515) * Make the IDL parser parse global types * Restyle * Parsing ok, started adding compatibility unit tests (which fail) * backwards compatibility tests pass now * Start implementing global type merging. Still needs unit tests * Prepare matter tests * A first test for global parsing * Fix bugs and have a working unit test for the non-recursive bit * Recursive unit test passes * Fix lint * Fix lint * Add check for global type uniqueness * Restyle --------- Co-authored-by: Andrei Litvin --- .../matter_idl/backwards_compatibility.py | 6 + .../matter_idl/generators/idl/MatterIdl.jinja | 60 +++- .../matter_idl/matter_grammar.lark | 2 +- .../matter_idl/matter_idl_parser.py | 118 +++++++- .../matter_idl/matter_idl_types.py | 8 + .../test_backwards_compatibility.py | 77 +++++ .../matter_idl/test_data_model_xml.py | 1 + .../matter_idl/test_matter_idl_parser.py | 271 ++++++++++++++++-- 8 files changed, 502 insertions(+), 41 deletions(-) diff --git a/scripts/py_matter_idl/matter_idl/backwards_compatibility.py b/scripts/py_matter_idl/matter_idl/backwards_compatibility.py index 04377100312756..e431b6227575c7 100644 --- a/scripts/py_matter_idl/matter_idl/backwards_compatibility.py +++ b/scripts/py_matter_idl/matter_idl/backwards_compatibility.py @@ -318,6 +318,12 @@ def check(self): self._check_cluster_list_compatible( self._original_idl.clusters, self._updated_idl.clusters) + self._check_enum_list_compatible( + "", self._original_idl.global_enums, self._updated_idl.global_enums) + self._check_bitmap_list_compatible( + "", self._original_idl.global_bitmaps, self._updated_idl.global_bitmaps) + self._check_struct_list_compatible( + "", self._original_idl.global_structs, self._updated_idl.global_structs) return self.compatible diff --git a/scripts/py_matter_idl/matter_idl/generators/idl/MatterIdl.jinja b/scripts/py_matter_idl/matter_idl/generators/idl/MatterIdl.jinja index 38553bb0b7f3ee..73be97dd3ae5cb 100644 --- a/scripts/py_matter_idl/matter_idl/generators/idl/MatterIdl.jinja +++ b/scripts/py_matter_idl/matter_idl/generators/idl/MatterIdl.jinja @@ -32,13 +32,65 @@ // This IDL was auto-generated from a parsed data structure -{% for cluster in idl.clusters %} +{% for enum in idl.global_enums %} +enum {{enum.name}} : {{ enum.base_type}} { + {% for entry in enum.entries %} + {{entry.name}} = {{entry.code}}; + {% endfor %} +} + +{% endfor %} + +{%- for bitmap in idl.global_bitmaps %} +bitmap {{bitmap.name}} : {{ bitmap.base_type}} { + {% for entry in bitmap.entries %} + {{entry.name}} = 0x{{"%X" | format(entry.code)}}; + {% endfor %} +} + +{% endfor %} + +{%- for s in idl.global_structs %} +{{render_struct(s)}} + +{% endfor %} + +{%- for cluster in idl.clusters %} {% if cluster.description %}/** {{cluster.description}} */ {% endif %} {{cluster.api_maturity | idltxt}}cluster {{cluster.name}} = {{cluster.code}} { revision {{cluster.revision}}; - {% for enum in cluster.enums %} + {% for enum in cluster.enums | selectattr("is_global")%} + /* GLOBAL: + enum {{enum.name}} : {{ enum.base_type}} { + {% for entry in enum.entries %} + {{entry.name}} = {{entry.code}}; + {% endfor %} + } + */ + + {% endfor %} + + {%- for bitmap in cluster.bitmaps | selectattr("is_global")%} + /* GLOBAL: + bitmap {{bitmap.name}} : {{ bitmap.base_type}} { + {% for entry in bitmap.entries %} + {{entry.name}} = 0x{{"%X" | format(entry.code)}}; + {% endfor %} + } + */ + + {% endfor %} + + {%- for s in cluster.structs | selectattr("is_global") %} + /* GLOBAL: + {{render_struct(s)}} + */ + + {% endfor %} + + {%- for enum in cluster.enums | rejectattr("is_global")%} enum {{enum.name}} : {{ enum.base_type}} { {% for entry in enum.entries %} {{entry.name}} = {{entry.code}}; @@ -47,7 +99,7 @@ {% endfor %} - {%- for bitmap in cluster.bitmaps %} + {%- for bitmap in cluster.bitmaps | rejectattr("is_global")%} bitmap {{bitmap.name}} : {{ bitmap.base_type}} { {% for entry in bitmap.entries %} {{entry.name}} = 0x{{"%X" | format(entry.code)}}; @@ -56,7 +108,7 @@ {% endfor %} - {%- for s in cluster.structs | rejectattr("tag") %} + {%- for s in cluster.structs | rejectattr("tag") | rejectattr("is_global") %} {{render_struct(s)}} {% endfor %} diff --git a/scripts/py_matter_idl/matter_idl/matter_grammar.lark b/scripts/py_matter_idl/matter_idl/matter_grammar.lark index a2838e1fa8eb18..4013c51bb52a59 100644 --- a/scripts/py_matter_idl/matter_idl/matter_grammar.lark +++ b/scripts/py_matter_idl/matter_idl/matter_grammar.lark @@ -116,7 +116,7 @@ POSITIVE_INTEGER: /\d+/ HEX_INTEGER: /0x[A-Fa-f0-9]+/ ID: /[a-zA-Z_][a-zA-Z0-9_]*/ -idl: (cluster|endpoint)* +idl: (struct|enum|bitmap|cluster|endpoint)* %import common.ESCAPED_STRING %import common.WS diff --git a/scripts/py_matter_idl/matter_idl/matter_idl_parser.py b/scripts/py_matter_idl/matter_idl/matter_idl_parser.py index e33c0c7e872502..e071d008d1d726 100755 --- a/scripts/py_matter_idl/matter_idl/matter_idl_parser.py +++ b/scripts/py_matter_idl/matter_idl/matter_idl_parser.py @@ -1,8 +1,9 @@ #!/usr/bin/env python +import dataclasses import functools import logging -from typing import Dict, Optional +from typing import Dict, Optional, Set from lark import Lark from lark.lexer import Token @@ -504,15 +505,25 @@ def idl(self, items): clusters = [] endpoints = [] + global_bitmaps = [] + global_enums = [] + global_structs = [] + for item in items: if isinstance(item, Cluster): clusters.append(item) elif isinstance(item, Endpoint): endpoints.append(item) + elif isinstance(item, Enum): + global_enums.append(dataclasses.replace(item, is_global=True)) + elif isinstance(item, Bitmap): + global_bitmaps.append(dataclasses.replace(item, is_global=True)) + elif isinstance(item, Struct): + global_structs.append(dataclasses.replace(item, is_global=True)) else: raise Exception("UNKNOWN idl content item: %r" % item) - return Idl(clusters=clusters, endpoints=endpoints) + return Idl(clusters=clusters, endpoints=endpoints, global_bitmaps=global_bitmaps, global_enums=global_enums, global_structs=global_structs) def prefix_doc_comment(self): print("TODO: prefix") @@ -524,9 +535,92 @@ def c_comment(self, token: Token): self.doc_comments.append(PrefixCppDocComment(token)) +def _referenced_type_names(cluster: Cluster) -> Set[str]: + """ + Return the names of all data types referenced by the given cluster. + """ + types = set() + for s in cluster.structs: + for f in s.fields: + types.add(f.data_type.name) + + for e in cluster.events: + for f in e.fields: + types.add(f.data_type.name) + + for a in cluster.attributes: + types.add(a.definition.data_type.name) + + return types + + +class GlobalMapping: + """ + Maintains global type mapping from an IDL + """ + + def __init__(self, idl: Idl): + self.bitmap_map = {b.name: b for b in idl.global_bitmaps} + self.enum_map = {e.name: e for e in idl.global_enums} + self.struct_map = {s.name: s for s in idl.global_structs} + + self.global_types = set(self.bitmap_map.keys()).union(set(self.enum_map.keys())).union(set(self.struct_map.keys())) + + # Spec does not enforce unique naming in bitmap/enum/struct, however in practice + # if we have both enum Foo and bitmap Foo for example, it would be impossible + # to disambiguate `attribute Foo foo = 1` for the actual type we want. + # + # As a result, we do not try to namespace this and just error out + if len(self.global_types) != len(self.bitmap_map) + len(self.enum_map) + len(self.struct_map): + raise ValueError("Global type names are not unique.") + + def merge_global_types_into_cluster(self, cluster: Cluster) -> Cluster: + """ + Merges all referenced global types (bitmaps/enums/structs) into the cluster types. + This happens recursively. + """ + global_types_added = set() + + changed = True + while changed: + changed = False + for type_name in _referenced_type_names(cluster): + if type_name not in self.global_types: + continue # not a global type name + + if type_name in global_types_added: + continue # already added + + # check if this is a global type + if type_name in self.bitmap_map: + global_types_added.add(type_name) + changed = True + cluster.bitmaps.append(self.bitmap_map[type_name]) + elif type_name in self.enum_map: + global_types_added.add(type_name) + changed = True + cluster.enums.append(self.enum_map[type_name]) + elif type_name in self.struct_map: + global_types_added.add(type_name) + changed = True + cluster.structs.append(self.struct_map[type_name]) + + return cluster + + +def _merge_global_types_into_clusters(idl: Idl) -> Idl: + """ + Adds bitmaps/enums/structs from idl.global_* into clusters as long as + clusters reference those type names + """ + mapping = GlobalMapping(idl) + return dataclasses.replace(idl, clusters=[mapping.merge_global_types_into_cluster(cluster) for cluster in idl.clusters]) + + class ParserWithLines: - def __init__(self, skip_meta: bool): + def __init__(self, skip_meta: bool, merge_globals: bool): self.transformer = MatterIdlTransformer(skip_meta) + self.merge_globals = merge_globals # NOTE: LALR parser is fast. While Earley could parse more ambigous grammars, # earley is much slower: @@ -572,14 +666,28 @@ def parse(self, file: str, file_name: Optional[str] = None): for comment in self.transformer.doc_comments: comment.appply_to_idl(idl, file) + if self.merge_globals: + idl = _merge_global_types_into_clusters(idl) + return idl -def CreateParser(skip_meta: bool = False): +def CreateParser(skip_meta: bool = False, merge_globals=True): """ Generates a parser that will process a ".matter" file into a IDL + + Arguments: + skip_meta - do not add metadata (line position) for items. Metadata is + useful for error reporting, however it does not work well + for unit test comparisons + + merge_globals - places global items (enums/bitmaps/structs) into any + clusters that reference them, so that cluster types + are self-sufficient. Useful as a backwards-compatible + code generation if global definitions are not supported. + """ - return ParserWithLines(skip_meta) + return ParserWithLines(skip_meta, merge_globals) if __name__ == '__main__': diff --git a/scripts/py_matter_idl/matter_idl/matter_idl_types.py b/scripts/py_matter_idl/matter_idl/matter_idl_types.py index 3515e5e655bc7a..d4a2195457ed60 100644 --- a/scripts/py_matter_idl/matter_idl/matter_idl_types.py +++ b/scripts/py_matter_idl/matter_idl/matter_idl_types.py @@ -162,6 +162,7 @@ class Struct: code: Optional[int] = None # for responses only qualities: StructQuality = StructQuality.NONE api_maturity: ApiMaturity = ApiMaturity.STABLE + is_global: bool = False @dataclass @@ -193,6 +194,7 @@ class Enum: base_type: str entries: List[ConstantEntry] api_maturity: ApiMaturity = ApiMaturity.STABLE + is_global: bool = False @dataclass @@ -201,6 +203,7 @@ class Bitmap: base_type: str entries: List[ConstantEntry] api_maturity: ApiMaturity = ApiMaturity.STABLE + is_global: bool = False @dataclass @@ -290,5 +293,10 @@ class Idl: clusters: List[Cluster] = field(default_factory=list) endpoints: List[Endpoint] = field(default_factory=list) + # Global types + global_bitmaps: List[Bitmap] = field(default_factory=list) + global_enums: List[Enum] = field(default_factory=list) + global_structs: List[Struct] = field(default_factory=list) + # IDL file name is available only if parsing provides a file name parse_file_name: Optional[str] = field(default=None) diff --git a/scripts/py_matter_idl/matter_idl/test_backwards_compatibility.py b/scripts/py_matter_idl/matter_idl/test_backwards_compatibility.py index e94e79abd5fce5..e2657dd2e5d93d 100755 --- a/scripts/py_matter_idl/matter_idl/test_backwards_compatibility.py +++ b/scripts/py_matter_idl/matter_idl/test_backwards_compatibility.py @@ -90,6 +90,83 @@ def ValidateUpdate(self, name: str, old: str, new: str, flags: Compatibility): with self.subTest(direction="OLD-to-OLD"): self._AssumeCompatiblity(old, old, old_idl, old_idl, True) + def test_global_enums_delete(self): + self.ValidateUpdate( + "delete a top level enum", + "enum A: ENUM8{} enum B: ENUM8{}", + "enum A: ENUM8{}", + Compatibility.FORWARD_FAIL) + + def test_global_enums_change(self): + self.ValidateUpdate( + "change an enum type", + "enum A: ENUM8{}", + "enum A: ENUM16{}", + Compatibility.FORWARD_FAIL | Compatibility.BACKWARD_FAIL) + + def test_global_enums_add_remove(self): + self.ValidateUpdate( + "Adding enum values is ok, removing values is not", + "enum A: ENUM8 {A = 1; B = 2;}", + "enum A: ENUM8 {A = 1; }", + Compatibility.FORWARD_FAIL) + + def test_global_enums_code(self): + self.ValidateUpdate( + "Switching enum codes is never ok", + "enum A: ENUM8 {A = 1; B = 2; }", + "enum A: ENUM8 {A = 1; B = 3; }", + Compatibility.FORWARD_FAIL | Compatibility.BACKWARD_FAIL) + + def test_global_bitmaps_delete(self): + self.ValidateUpdate( + "Deleting a global bitmap", + "bitmap A: BITMAP8{} bitmap B: BITMAP8{}", + "bitmap A: BITMAP8{} ", + Compatibility.FORWARD_FAIL) + + def test_global_bitmaps_type(self): + self.ValidateUpdate( + "Changing a bitmap type is never ok", + " bitmap A: BITMAP8{}", + " bitmap A: BITMAP16{}", + Compatibility.FORWARD_FAIL | Compatibility.BACKWARD_FAIL) + + def test_global_bitmap_values(self): + self.ValidateUpdate( + "Adding bitmap values is ok, removing values is not", + " bitmap A: BITMAP8 { kA = 0x01; kB = 0x02; } ", + " bitmap A: BITMAP8 { kA = 0x01; } ", + Compatibility.FORWARD_FAIL) + + def test_global_struct_removal(self): + self.ValidateUpdate( + "Structure removal is not ok, but adding is ok", + "struct Foo {} struct Bar {} ", + "struct Foo {} ", + Compatibility.FORWARD_FAIL) + + def test_global_struct_content_type_change(self): + self.ValidateUpdate( + "Changing structure data types is never ok", + "struct Foo { int32u x = 1; }", + "struct Foo { int64u x = 1; }", + Compatibility.FORWARD_FAIL | Compatibility.BACKWARD_FAIL) + + def test_global_struct_content_rename_reorder(self): + self.ValidateUpdate( + "Structure content renames and reorder is ok.", + "struct Foo { int32u x = 1; int8u y = 2; }", + "struct Foo { int8u a = 2; int32u y = 1; }", + Compatibility.ALL_OK) + + def test_global_struct_content_add_remove(self): + self.ValidateUpdate( + "Structure content change is not ok.", + "struct Foo { int32u x = 1; }", + "struct Foo { int32u x = 1; int8u y = 2; }", + Compatibility.FORWARD_FAIL | Compatibility.BACKWARD_FAIL) + def test_basic_clusters_enum(self): self.ValidateUpdate( "Adding an enum is ok. Also validates code formatting", diff --git a/scripts/py_matter_idl/matter_idl/test_data_model_xml.py b/scripts/py_matter_idl/matter_idl/test_data_model_xml.py index 6bdb530f7cc9fc..7a606cc2ec9c6e 100755 --- a/scripts/py_matter_idl/matter_idl/test_data_model_xml.py +++ b/scripts/py_matter_idl/matter_idl/test_data_model_xml.py @@ -94,6 +94,7 @@ def assertIdlEqual(self, a: Idl, b: Idl): tofile='expected.matter', ) self.assertEqual(a, b, '\n' + ''.join(delta)) + self.fail("IDLs are not equal (above delta should have failed)") def testBasicInput(self): diff --git a/scripts/py_matter_idl/matter_idl/test_matter_idl_parser.py b/scripts/py_matter_idl/matter_idl/test_matter_idl_parser.py index e5de70e940eb19..ab79c279b0697f 100755 --- a/scripts/py_matter_idl/matter_idl/test_matter_idl_parser.py +++ b/scripts/py_matter_idl/matter_idl/test_matter_idl_parser.py @@ -14,6 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from difflib import unified_diff + try: from matter_idl.matter_idl_parser import CreateParser except ModuleNotFoundError: @@ -25,19 +27,62 @@ from matter_idl.matter_idl_parser import CreateParser import unittest +from typing import Optional +from matter_idl.generators import GeneratorStorage +from matter_idl.generators.idl import IdlGenerator from matter_idl.matter_idl_types import (AccessPrivilege, ApiMaturity, Attribute, AttributeInstantiation, AttributeQuality, AttributeStorage, Bitmap, Cluster, Command, CommandInstantiation, CommandQuality, ConstantEntry, DataType, DeviceType, Endpoint, Enum, Event, EventPriority, EventQuality, Field, FieldQuality, Idl, ParseMetaData, ServerClusterInstantiation, Struct, StructTag) -def parseText(txt, skip_meta=True): - return CreateParser(skip_meta=skip_meta).parse(txt) +class GeneratorContentStorage(GeneratorStorage): + def __init__(self): + super().__init__() + self.content: Optional[str] = None + + def get_existing_data(self, relative_path: str): + # Force re-generation each time + return None + + def write_new_data(self, relative_path: str, content: str): + if self.content: + raise Exception( + "Unexpected extra data: single file generation expected") + self.content = content + + +def RenderAsIdlTxt(idl: Idl) -> str: + storage = GeneratorContentStorage() + IdlGenerator(storage=storage, idl=idl).render(dry_run=False) + return storage.content or "" + + +def parseText(txt, skip_meta=True, merge_globals=True): + return CreateParser(skip_meta=skip_meta, merge_globals=merge_globals).parse(txt) class TestParser(unittest.TestCase): + def assertIdlEqual(self, a: Idl, b: Idl): + if a == b: + # seems the same. This will just pass + self.assertEqual(a, b) + return + + # Not the same. Try to make a human readable diff: + a_txt = RenderAsIdlTxt(a) + b_txt = RenderAsIdlTxt(b) + + delta = unified_diff(a_txt.splitlines(keepends=True), + b_txt.splitlines(keepends=True), + fromfile='actual.matter', + tofile='expected.matter', + ) + self.assertEqual(a, b, '\n' + ''.join(delta)) + self.fail("IDLs are not equal (above delta should have failed)") + def test_skips_comments(self): actual = parseText(""" // this is a single line comment @@ -49,7 +94,7 @@ def test_skips_comments(self): """) expected = Idl() - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_cluster_attribute(self): actual = parseText(""" @@ -75,7 +120,7 @@ def test_cluster_attribute(self): data_type=DataType(name="int8s"), code=0xAB, name="isNullable", qualities=FieldQuality.NULLABLE)), ] )]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_doc_comments(self): actual = parseText(""" @@ -96,12 +141,12 @@ def test_doc_comments(self): # meta_data may not match but is required for doc comments. Clean it up # Metadata parsing varies line/column, so only check doc comments - self.assertEqual( + self.assertIdlEqual( actual.clusters[0].description, "Documentation for MyCluster") - self.assertEqual( + self.assertIdlEqual( actual.clusters[1].description, "Documentation for MyCluster #2") self.assertIsNone(actual.clusters[1].commands[0].description) - self.assertEqual( + self.assertIdlEqual( actual.clusters[1].commands[1].description, "Some command doc comment") def test_sized_attribute(self): @@ -122,7 +167,7 @@ def test_sized_attribute(self): data_type=DataType(name="octet_string", max_length=33), code=2, name="attr2", is_list=True)), ] )]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_timed_attributes(self): actual = parseText(""" @@ -148,7 +193,7 @@ def test_timed_attributes(self): data_type=DataType(name="octet_string", max_length=44), code=4, name="attr4", is_list=True)), ] )]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_attribute_access(self): actual = parseText(""" @@ -190,7 +235,7 @@ def test_attribute_access(self): ), ] )]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_cluster_commands(self): actual = parseText(""" @@ -232,7 +277,7 @@ def test_cluster_commands(self): qualities=CommandQuality.TIMED_INVOKE | CommandQuality.FABRIC_SCOPED), ], )]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_cluster_command_access(self): actual = parseText(""" @@ -268,7 +313,7 @@ def test_cluster_command_access(self): ), ], )]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_cluster_enum(self): actual = parseText(""" @@ -289,7 +334,7 @@ def test_cluster_enum(self): ConstantEntry(name="B", code=0x234), ])], )]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_event_field_api_maturity(self): actual = parseText(""" @@ -316,7 +361,7 @@ def test_event_field_api_maturity(self): ]), ], )]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_enum_constant_maturity(self): actual = parseText(""" @@ -341,7 +386,7 @@ def test_enum_constant_maturity(self): name="kInternal", code=0x345, api_maturity=ApiMaturity.INTERNAL), ])], )]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_bitmap_constant_maturity(self): actual = parseText(""" @@ -366,7 +411,7 @@ def test_bitmap_constant_maturity(self): name="kProvisional", code=0x4, api_maturity=ApiMaturity.PROVISIONAL), ])], )]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_struct_field_api_maturity(self): actual = parseText(""" @@ -393,7 +438,7 @@ def test_struct_field_api_maturity(self): ]), ], )]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_cluster_entry_maturity(self): actual = parseText(""" @@ -510,7 +555,7 @@ def test_cluster_entry_maturity(self): data_type=DataType(name="int32u"), code=31, name="rwForcedStable", is_list=True), api_maturity=ApiMaturity.STABLE), ] )]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_cluster_bitmap(self): actual = parseText(""" @@ -531,7 +576,7 @@ def test_cluster_bitmap(self): ConstantEntry(name="kSecond", code=0x2), ])], )]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_cluster_events(self): actual = parseText(""" @@ -556,7 +601,7 @@ def test_cluster_events(self): Event(priority=EventPriority.DEBUG, name="GoodBye", code=2, fields=[]), ])]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_cluster_event_acl(self): actual = parseText(""" @@ -577,7 +622,7 @@ def test_cluster_event_acl(self): Event(priority=EventPriority.DEBUG, readacl=AccessPrivilege.ADMINISTER, name="AdminEvent", code=3, fields=[]), ])]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_fabric_sensitive_event(self): actual = parseText(""" @@ -598,7 +643,7 @@ def test_fabric_sensitive_event(self): Event(priority=EventPriority.DEBUG, readacl=AccessPrivilege.ADMINISTER, name="AdminEvent", code=3, fields=[], qualities=EventQuality.FABRIC_SENSITIVE), ])]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_parsing_metadata_for_cluster(self): actual = CreateParser(skip_meta=False).parse(""" @@ -614,7 +659,7 @@ def test_parsing_metadata_for_cluster(self): Cluster(parse_meta=ParseMetaData(line=5, column=4, start_pos=87), name="B", code=2), ]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_multiple_clusters(self): actual = parseText(""" @@ -628,7 +673,7 @@ def test_multiple_clusters(self): Cluster(name="B", code=2), Cluster(name="C", code=3), ]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_endpoints(self): actual = parseText(""" @@ -658,7 +703,7 @@ def test_endpoints(self): ], client_bindings=["Bar", "Test"],) ]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_cluster_instantiation(self): actual = parseText(""" @@ -693,7 +738,7 @@ def test_cluster_instantiation(self): ], client_bindings=[],) ]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_multi_endpoints(self): actual = parseText(""" @@ -709,7 +754,7 @@ def test_multi_endpoints(self): Endpoint(number=10), Endpoint(number=100), ]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_cluster_api_maturity(self): actual = parseText(""" @@ -723,7 +768,171 @@ def test_cluster_api_maturity(self): Cluster(name="B", code=2, api_maturity=ApiMaturity.INTERNAL), Cluster(name="C", code=3), ]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) + + def test_just_globals(self): + actual = parseText(""" + enum TestEnum : ENUM16 { A = 0x123; B = 0x234; } + bitmap TestBitmap : BITMAP32 { + kStable = 0x1; + internal kInternal = 0x2; + provisional kProvisional = 0x4; + } + struct TestStruct { + nullable int16u someStableMember = 0; + provisional nullable int16u someProvisionalMember = 1; + internal nullable int16u someInternalMember = 2; + } + """) + + expected = Idl( + global_enums=[ + Enum(name="TestEnum", base_type="ENUM16", + entries=[ + ConstantEntry(name="A", code=0x123), + ConstantEntry(name="B", code=0x234), + ], + is_global=True, + )], + global_bitmaps=[ + Bitmap(name="TestBitmap", base_type="BITMAP32", + entries=[ + ConstantEntry(name="kStable", code=0x1), + ConstantEntry( + name="kInternal", code=0x2, api_maturity=ApiMaturity.INTERNAL), + ConstantEntry( + name="kProvisional", code=0x4, api_maturity=ApiMaturity.PROVISIONAL), + ], + is_global=True, + )], + global_structs=[ + Struct(name="TestStruct", fields=[ + Field(name="someStableMember", code=0, data_type=DataType( + name="int16u"), qualities=FieldQuality.NULLABLE), + Field(name="someProvisionalMember", code=1, data_type=DataType( + name="int16u"), qualities=FieldQuality.NULLABLE, api_maturity=ApiMaturity.PROVISIONAL), + Field(name="someInternalMember", code=2, data_type=DataType( + name="int16u"), qualities=FieldQuality.NULLABLE, api_maturity=ApiMaturity.INTERNAL), + + ], + is_global=True, + )], + ) + self.assertIdlEqual(actual, expected) + + def test_cluster_reference_globals(self): + actual = parseText(""" + enum TestEnum : ENUM16 {} + bitmap TestBitmap : BITMAP32 {} + struct TestStruct {} + + server cluster Foo = 1 { + info event BitmapEvent = 0 { + TestBitmap someBitmap = 0; + } + struct MyStruct { + nullable TestStruct subStruct = 0; + } + readonly attribute TestEnum enumAttribute = 1; + } + """) + + global_enum = Enum(name="TestEnum", base_type="ENUM16", entries=[], is_global=True) + global_bitmap = Bitmap(name="TestBitmap", base_type="BITMAP32", entries=[], is_global=True) + global_struct = Struct(name="TestStruct", fields=[], is_global=True) + expected = Idl( + global_enums=[global_enum], + global_bitmaps=[global_bitmap], + global_structs=[global_struct], + clusters=[ + Cluster( + name="Foo", + code=1, + enums=[global_enum], + bitmaps=[global_bitmap], + events=[ + Event(priority=EventPriority.INFO, + name="BitmapEvent", code=0, fields=[ + Field(data_type=DataType(name="TestBitmap"), + code=0, name="someBitmap"), + ]), + ], + structs=[ + Struct(name="MyStruct", fields=[ + Field(name="subStruct", code=0, data_type=DataType(name="TestStruct"), qualities=FieldQuality.NULLABLE), ], + ), + global_struct, + ], + attributes=[ + Attribute(qualities=AttributeQuality.READABLE, definition=Field( + data_type=DataType(name="TestEnum"), code=1, name="enumAttribute")), + ], + ) + ], + ) + self.assertIdlEqual(actual, expected) + + def test_cluster_reference_globals_recursive(self): + actual = parseText(""" + enum TestEnum : ENUM16 {} + bitmap TestBitmap : BITMAP32 {} + + struct TestStruct1 { + TestEnum enumField = 0; + } + + struct TestStruct2 { + TestStruct1 substruct = 0; + } + + struct TestStruct3 { + TestStruct2 substruct = 0; + TestBitmap bmp = 1; + } + + server cluster Foo = 1 { + attribute TestStruct3 structAttr = 1; + } + """) + + global_enum = Enum(name="TestEnum", base_type="ENUM16", entries=[], is_global=True) + global_bitmap = Bitmap(name="TestBitmap", base_type="BITMAP32", entries=[], is_global=True) + global_struct1 = Struct(name="TestStruct1", fields=[ + Field(name="enumField", code=0, data_type=DataType(name="TestEnum")), + + ], is_global=True) + global_struct2 = Struct(name="TestStruct2", fields=[ + Field(name="substruct", code=0, data_type=DataType(name="TestStruct1")), + + ], is_global=True) + global_struct3 = Struct(name="TestStruct3", fields=[ + Field(name="substruct", code=0, data_type=DataType(name="TestStruct2")), + Field(name="bmp", code=1, data_type=DataType(name="TestBitmap")), + ], is_global=True) + expected = Idl( + global_enums=[global_enum], + global_bitmaps=[global_bitmap], + global_structs=[global_struct1, global_struct2, global_struct3], + clusters=[ + Cluster( + name="Foo", + code=1, + enums=[global_enum], + bitmaps=[global_bitmap], + structs=[ + global_struct3, + global_struct2, + global_struct1, + ], + attributes=[ + Attribute( + qualities=AttributeQuality.READABLE | AttributeQuality.WRITABLE, + definition=Field(data_type=DataType(name="TestStruct3"), code=1, name="structAttr")), + ], + ) + ], + ) + self.assertIdlEqual(actual, expected) def test_emits_events(self): actual = parseText(""" @@ -753,7 +962,7 @@ def test_emits_events(self): ]) ]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_revision(self): actual = parseText(""" @@ -769,7 +978,7 @@ def test_revision(self): Cluster(name="C", code=3, revision=2), Cluster(name="D", code=4, revision=123), ]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) def test_handle_commands(self): actual = parseText(""" @@ -801,7 +1010,7 @@ def test_handle_commands(self): ]) ]) - self.assertEqual(actual, expected) + self.assertIdlEqual(actual, expected) if __name__ == '__main__': From 693ffba534100ae3bc6d1adea1edae8358055819 Mon Sep 17 00:00:00 2001 From: C Freeman Date: Fri, 26 Jul 2024 15:35:40 -0400 Subject: [PATCH 41/49] TC-SWTCH-2.3: Implement (#34526) * TC-SWTCH-2.3: Implement * Update src/python_testing/matter_testing_support.py * Restyled by autopep8 * Restyled by isort --------- Co-authored-by: Restyled.io --- src/python_testing/TC_SWTCH.py | 115 ++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 2 deletions(-) diff --git a/src/python_testing/TC_SWTCH.py b/src/python_testing/TC_SWTCH.py index 867897ec54ec9d..5fb968b724e098 100644 --- a/src/python_testing/TC_SWTCH.py +++ b/src/python_testing/TC_SWTCH.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2023 Project CHIP Authors +# Copyright (c) 2024 Project CHIP Authors # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,13 +30,15 @@ import logging import queue import time +from datetime import datetime, timedelta from typing import Any import chip.clusters as Clusters +import test_plan_support from chip.clusters import ClusterObjects as ClusterObjects from chip.clusters.Attribute import EventReadResult, TypedAttributePath from matter_testing_support import (AttributeValue, ClusterAttributeChangeAccumulator, EventChangeCallback, MatterBaseTest, - async_test_body, default_matter_test_main) + TestStep, async_test_body, default_matter_test_main) from mobly import asserts logger = logging.getLogger(__name__) @@ -93,6 +95,25 @@ def _ask_for_long_press(self, endpoint_id: int, pressed_position: int): "ButtonId": pressed_position, "LongPressDelayMillis": 5000, "LongPressDurationMillis": 5500} self._send_named_pipe_command(command_dict) + def _ask_for_keep_pressed(self, endpoint_id: int, pressed_position: int): + if not self._use_button_simulator(): + self.wait_for_user_input( + prompt_msg=f"Press switch position {pressed_position} for a long time (around 5 seconds) on the DUT, then release it.") + else: + # Using the long press here with a long duration so we can check the intermediate value. + command_dict = {"Name": "SimulateActionSwitchLongPress", "EndpointId": endpoint_id, + "ButtonId": pressed_position, "LongPressDelayMillis": 0, "LongPressDurationMillis": self.keep_pressed_delay} + self._send_named_pipe_command(command_dict) + + def _ask_for_release(self): + # Since we used a long press for this, "ask for release" on the button simulator just means waiting out the delay + if not self._use_button_simulator(): + self.wait_for_user_input( + prompt_msg="Release the button." + ) + else: + time.sleep(self.keep_pressed_delay/1000) + def _placeholder_for_step(self, step_id: str): # TODO: Global search an replace of `self._placeholder_for_step` with `self.step` when done. logging.info(f"Step {step_id}") @@ -285,6 +306,96 @@ async def test_TC_SWTCH_2_4(self): self._await_sequence_of_events(event_queue=event_listener.event_queue, endpoint_id=endpoint_id, sequence=expected_events, timeout_sec=post_prompt_settle_delay_seconds) + def _received_event(self, event_listener: EventChangeCallback, target_event: ClusterObjects.ClusterEvent, timeout_s: int) -> bool: + """ + Returns true if this event was received, false otherwise + """ + remaining = timedelta(seconds=timeout_s) + end_time = datetime.now() + remaining + while (remaining.seconds > 0): + try: + event = event_listener.event_queue.get(timeout=remaining.seconds) + except queue.Empty: + return False + + if event.Header.EventId == target_event.event_id: + return True + remaining = end_time - datetime.now() + return False + + def pics_TC_SWTCH_2_3(self): + return ['SWTCH.S.F01'] + + def steps_TC_SWTCH_2_3(self): + return [TestStep(1, test_plan_support.commission_if_required(), "", is_commissioning=True), + TestStep(2, "Set up subscription to all events of Switch cluster on the endpoint"), + TestStep(3, "Operator does not operate switch on the DUT"), + TestStep(4, "TH reads the CurrentPosition attribute from the DUT", "Verify that the value is 0"), + TestStep(5, "Operator operates switch (keep it pressed)", + "Verify that the TH receives InitialPress event with NewPosition set to 1 on the DUT"), + TestStep(6, "TH reads the CurrentPosition attribute from the DUT", "Verify that the value is 1"), + TestStep(7, "Operator releases switch on the DUT"), + TestStep("8a", "If the DUT implements the MSR feature, verify that the TH receives ShortRelease event with NewPosition set to 0 on the DUT", "Event received"), + TestStep( + "8b", "If the DUT implements the AS feature, verify that the TH does not receive ShortRelease event on the DUT", "No event received"), + TestStep(9, "TH reads the CurrentPosition attribute from the DUT", "Verify that the value is 0"), + ] + + @async_test_body + async def test_TC_SWTCH_2_3(self): + # Commissioning - already done + self.step(1) + cluster = Clusters.Switch + feature_map = await self.read_single_attribute_check_success(cluster, attribute=cluster.Attributes.FeatureMap) + + has_msr_feature = (feature_map & cluster.Bitmaps.Feature.kMomentarySwitchRelease) != 0 + has_as_feature = (feature_map & cluster.Bitmaps.Feature.kActionSwitch) != 0 + + endpoint_id = self.matter_test_config.endpoint + + self.step(2) + event_listener = EventChangeCallback(cluster) + await event_listener.start(self.default_controller, self.dut_node_id, endpoint=endpoint_id) + + self.step(3) + self._ask_for_switch_idle() + + self.step(4) + button_val = await self.read_single_attribute_check_success(cluster=cluster, attribute=cluster.Attributes.CurrentPosition) + asserts.assert_equal(button_val, 0, "Button value is not 0") + + self.step(5) + # We're using a long press here with a very long duration (in computer-land). This will let us check the intermediate values. + # This is 1s larger than the subscription ceiling + self.keep_pressed_delay = 6000 + self.pressed_position = 1 + self._ask_for_keep_pressed(endpoint_id, self.pressed_position) + event_listener.wait_for_event_report(cluster.Events.InitialPress) + + self.step(6) + button_val = await self.read_single_attribute_check_success(cluster=cluster, attribute=cluster.Attributes.CurrentPosition) + asserts.assert_equal(button_val, self.pressed_position, f"Button value is not {self.pressed_position}") + + self.step(7) + self._ask_for_release() + + self.step("8a") + if has_msr_feature: + asserts.assert_true(self._received_event(event_listener, cluster.Events.ShortRelease, 10), + "Did not receive short release") + else: + self.mark_current_step_skipped() + + self.step("8b") + if has_as_feature: + asserts.assert_false(self._received_event(event_listener, cluster.Events.ShortRelease, 10), "Received short release") + else: + self.mark_current_step_skipped() + + self.step(9) + button_val = await self.read_single_attribute_check_success(cluster=cluster, attribute=cluster.Attributes.CurrentPosition) + asserts.assert_equal(button_val, 0, "Button value is not 0") + if __name__ == "__main__": default_matter_test_main() From 686a7153a59fae84b8d757412b9cfa691f1b4302 Mon Sep 17 00:00:00 2001 From: lpbeliveau-silabs <112982107+lpbeliveau-silabs@users.noreply.github.com> Date: Fri, 26 Jul 2024 16:01:11 -0400 Subject: [PATCH 42/49] [Color Control] Scenes Integration test scripts (#34464) * Added scene integration test in level control cluster * Update src/app/tests/suites/certification/Test_TC_CC_10_1.yaml Co-authored-by: C Freeman * Removed 100ms delays where not necessary * Converted CC_10_1 to python test * Removed hard codded endpoint, use read_single and added setup/teardown --------- Co-authored-by: C Freeman --- .github/workflows/tests.yaml | 1 + src/python_testing/TC_CC_10_1.py | 511 +++++++++++++++++++++++++++++++ 2 files changed, 512 insertions(+) create mode 100644 src/python_testing/TC_CC_10_1.py diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 651a74296154fb..2ff4d548c1484a 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -502,6 +502,7 @@ jobs: scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_ACE_1_4.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_ACE_1_5.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_AccessChecker.py' + scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_CC_10_1.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_CGEN_2_4.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_CNET_1_4.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_DA_1_2.py' diff --git a/src/python_testing/TC_CC_10_1.py b/src/python_testing/TC_CC_10_1.py new file mode 100644 index 00000000000000..3f1e40278bb0c6 --- /dev/null +++ b/src/python_testing/TC_CC_10_1.py @@ -0,0 +1,511 @@ +# +# 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. +# + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${ALL_CLUSTERS_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --endpoint 1 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# === END CI TEST ARGUMENTS === + +import asyncio +from typing import List + +import chip.clusters as Clusters +from chip.interaction_model import Status +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts + +kCCAttributeValueIDs = [0x0001, 0x0003, 0x0004, 0x0007, 0x4000, 0x4001, 0x4002, 0x4003, 0x4004] + + +class TC_CC_10_1(MatterBaseTest): + + # + # Class Helper functions + # + def _prepare_cc_extension_field_set(self, attribute_value_list: List[Clusters.ScenesManagement.Structs.AttributeValuePairStruct]) -> Clusters.ScenesManagement.Structs.ExtensionFieldSet: + efs_attribute_value_list: List[Clusters.ScenesManagement.Structs.AttributeValuePairStruct] = [] + for attribute_id in kCCAttributeValueIDs: + # Attempt to find the attribute in the input list + found = False + for pair in attribute_value_list: + if pair.attributeID == attribute_id: + efs_attribute_value_list.append(pair) + found = True + break + + if not found: + if attribute_id == 0x0001 or attribute_id == 0x4001 or attribute_id == 0x4002 or attribute_id == 0x4003: + empty_attribute_value = Clusters.ScenesManagement.Structs.AttributeValuePairStruct( + attributeID=attribute_id, + valueUnsigned8=0x00, + ) + elif attribute_id == 0x0003 or attribute_id == 0x0004 or attribute_id == 0x0007 or attribute_id == 0x4004: + empty_attribute_value = Clusters.ScenesManagement.Structs.AttributeValuePairStruct( + attributeID=attribute_id, + valueUnsigned16=0x0000, + ) + efs_attribute_value_list.append(empty_attribute_value) + + extension_field_set = Clusters.ScenesManagement.Structs.ExtensionFieldSet( + clusterID=Clusters.Objects.ColorControl.id, + attributeValueList=efs_attribute_value_list + ) + + return extension_field_set + + def desc_TC_CC_10_1(self) -> str: + """Returns a description of this test""" + return "4.2.29. [TC_CC_10_1] Scenes Management Cluster Interaction with DUT as Server" + + def pics_TC_CC_10_1(self): + """ This function returns a list of PICS for this test case that must be True for the test to be run""" + return ["CC.S", "S.S"] + + def steps_TC_CC_10_1(self) -> list[TestStep]: + steps = [ + TestStep("0", "Commissioning, already done", is_commissioning=True), + TestStep("0a", "TH sends KeySetWrite command in the GroupKeyManagement cluster to DUT using a key that is pre-installed on the TH. GroupKeySet fields are as follows: GroupKeySetID: 0x01a1, GroupKeySecurityPolicy: TrustFirst (0), EpochKey0: a0a1a2a3a4a5a6a7a8a9aaabacadaeaf, EpochStartTime0: 1110000, EpochKey1: b0b1b2b3b4b5b6b7b8b9babbbcbdbebf, EpochStartTime1: 1110001, EpochKey2: c0c1c2c3c4c5c6c7c8c9cacbcccdcecf, EpochStartTime2: 1110002"), + TestStep("0b", "TH binds GroupIds 0x0001 with GroupKeySetID 0x01a1 in the GroupKeyMap attribute list on GroupKeyManagement cluster by writing the GroupKeyMap attribute with two entries as follows: * List item 1: - FabricIndex: 1 - GroupId: 0x0001 - GroupKeySetId: 0x01a1"), + TestStep("0c", "TH sends a _RemoveAllGroups_ command to DUT."), + TestStep("1a", "TH sends a _AddGroup_ command to DUT with the _GroupID_ field set to _G~1~_."), + TestStep("1b", "TH sends a _RemoveAllScenes_ command to DUT with the _GroupID_ field set to _G~1~_."), + TestStep("1c", "TH sends a _GetSceneMembership_ command to DUT with the _GroupID_ field set to _G~1~_."), + TestStep("1d", "TH reads ColorTempPhysicalMinMireds attribute from DUT."), + TestStep("1e", "TH reads ColorTempPhysicalMaxMireds attribute from DUT."), + TestStep("2a", "TH sends _MoveToHueAndSaturation command_ to DUT with _Hue_=200, _Saturation_=50 and _TransitionTime_=0 (immediately)."), + TestStep("2b", "TH reads _CurrentHue and CurrentSaturation attributes_ from DUT."), + TestStep("2c", "TH sends _MoveToColor command_ to DUT, with: ColorX = 32768/0x8000 (x=0.5) (purple), ColorY = 19660/0x4CCC (y=0.3), TransitionTime = 0 (immediate)"), + TestStep("2d", "TH reads _CurrentX and CurrentY attributes_ from DUT."), + TestStep("2e", "TH sends _MoveToColorTemperature command_ to DUT with _ColorTemperatureMireds_=(_ColorTempPhysicalMinMireds_ + _ColorTempPhysicalMaxMireds_)/2"), + TestStep("2f", "TH sends _MoveColorTemperature command_ to DUT with _MoveMode_ = 0x01 (up), _Rate_ = (_ColorTempPhysicalMaxMireds_ - _ColorTempPhysicalMinMireds_)/40"), + TestStep("2g", "After 10 seconds, TH reads _ColorTemperatureMireds attribute_ from DUT."), + TestStep("2h", "TH sends _EnhancedMoveToHueAndSaturation command_ to DUT with _EnhancedHue_=20000, _Saturation_=50 and _TransitionTime_=0 (immediately)."), + TestStep("2i", "TH reads _EnhancedCurrentHue and CurrentSaturation attributes_ from DUT."), + TestStep("3", "TH sends a _StoreScene_ command to DUT with the _GroupID_ field set to _G~1~_ and the _SceneID_ field set to 0x01."), + TestStep("4", "TH sends a _ViewScene_ command to DUT with the _GroupID_ field set to _G~1~_ and the _SceneID_ field set to 0x01."), + TestStep( + "5a", "TH sends a _AddScene_ command to DUT with the _GroupID_ field set to _G~1~_, the _SceneID_ field set to 0x02, the TransitionTime field set to 0 and the ExtensionFieldSets set to: '[{ ClusterID: 0x0300, AttributeValueList: [{ AttributeID: 0x4001, ValueUnsigned8: 0x00 }, { AttributeID: 0x0001, ValueUnsigned8: 0xFE }]}]'"), + TestStep("5b", "TH sends a _RecallScene_ command to DUT with the _GroupID_ field set to _G~1~_, the _SceneID_ field set to 0x02 and the _TransitionTime_ omitted."), + TestStep("5c", "TH reads the _CurrentSaturation attribute_ from DUT."), + TestStep( + "6a", "TH sends a _AddScene_ command to DUT with the _GroupID_ field set to _G~1~_, the _SceneID_ field set to 0x03, the TransitionTime field set to 0 and the ExtensionFieldSets set to: '[{ ClusterID: 0x0300, AttributeValueList: [{ AttributeID: 0x4001, ValueUnsigned8: 0x01 }, { AttributeID: 0x0003, ValueUnsigned16: 16334 },{ AttributeID: 0x0004, ValueUnsigned16: 13067 }]}]'"), + TestStep("6b", "TH sends a _RecallScene_ command to DUT with the _GroupID_ field set to _G~1~_, the _SceneID_ field set to 0x03 and the _TransitionTime_ omitted."), + TestStep("6c", "TH reads _CurrentX and CurrentY attributes_ from DUT."), + TestStep( + "7a", "TH sends a _AddScene_ command to DUT with the _GroupID_ field set to _G~1~_, the _SceneID_ field set to 0x04, the TransitionTime field set to 0 and the ExtensionFieldSets set to: '[{ ClusterID: 0x0300, AttributeValueList: [{ AttributeID: 0x4001, ValueUnsigned8: 0x02 }, { AttributeID: 0x0007, ValueUnsigned16: 175 }]}]'"), + TestStep("7b", "TH sends a _RecallScene_ command to DUT with the _GroupID_ field set to _G~1~_, the _SceneID_ field set to 0x04 and the _TransitionTime_ omitted."), + TestStep("7c", "TH reads _ColorTemperatureMireds attribute_ from DUT."), + TestStep( + "8a", "TH sends a _AddScene_ command to DUT with the _GroupID_ field set to _G~1~_, the _SceneID_ field set to 0x05, the TransitionTime field set to 0 and the ExtensionFieldSets set to: '[{ ClusterID: 0x0300, AttributeValueList: [{ AttributeID: 0x4001, ValueUnsigned8: 0x03 }, { AttributeID: 0x4000, ValueUnsigned16: 12000 }, { AttributeID: 0x0001, ValueUnsigned16: 70 }]}]'"), + TestStep("8b", "TH sends a _RecallScene_ command to DUT with the _GroupID_ field set to _G~1~_, the _SceneID_ field set to 0x05 and the _TransitionTime_ omitted."), + TestStep("8c", "TH reads _EnhancedCurrentHue and CurrentSaturation attributes_ from DUT."), + TestStep( + "9a", "TH sends a _AddScene_ command to DUT with the _GroupID_ field set to _G~1~_, the _SceneID_ field set to 0x06, the TransitionTime field set to 0 and the ExtensionFieldSets set to: '[{ ClusterID: 0x0300, AttributeValueList: [{ AttributeID: 0x4002, ValueUnsigned16: 1 }, { AttributeID: 0x4002, ValueUnsigned16: 1 }, { AttributeID: 0x4004, ValueUnsigned16: 5 }]}]'"), + TestStep("9b", "TH sends a _RecallScene_ command to DUT with the _GroupID_ field set to _G~1~_, the _SceneID_ field set to 0x05 and the _TransitionTime_ omitted."), + TestStep("9c", "TH read _ColorLoopActive attribute_ from DUT."), + TestStep("9d", "TH read _ColorLoopDirection attribute_ from DUT."), + TestStep("9e", "TH read _ColorLoopTime attribute_ from DUT."), + ] + return steps + + @async_test_body + async def setup_test(self): + super().setup_test() + # Pre-Condition: Commissioning + self.step("0") + + self.step("0a") + + self.TH1 = self.default_controller + self.kGroupKeyset1 = 0x01a1 + self.groupKey = Clusters.GroupKeyManagement.Structs.GroupKeySetStruct( + groupKeySetID=self.kGroupKeyset1, + groupKeySecurityPolicy=Clusters.GroupKeyManagement.Enums.GroupKeySecurityPolicyEnum.kTrustFirst, + epochKey0="0123456789abcdef".encode(), + epochStartTime0=1110000, + epochKey1="0123456789abcdef".encode(), + epochStartTime1=1110001, + epochKey2="0123456789abcdef".encode(), + epochStartTime2=1110002) + + await self.TH1.SendCommand(self.dut_node_id, 0, Clusters.GroupKeyManagement.Commands.KeySetWrite(self.groupKey)) + + self.step("0b") + self.kGroup1 = 0x0001 + mapping_structs: List[Clusters.GroupKeyManagement.Structs.GroupKeyMapStruct] = [] + + mapping_structs.append(Clusters.GroupKeyManagement.Structs.GroupKeyMapStruct( + groupId=self.kGroup1, + groupKeySetID=self.kGroupKeyset1, + fabricIndex=1)) + result = await self.TH1.WriteAttribute(self.dut_node_id, [(0, Clusters.GroupKeyManagement.Attributes.GroupKeyMap(mapping_structs))]) + asserts.assert_equal(result[0].Status, Status.Success, "GroupKeyMap write failed") + + self.step("0c") + await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, Clusters.Groups.Commands.RemoveAllGroups()) + + self.step("1a") + result = await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, Clusters.Groups.Commands.AddGroup(self.kGroup1, "Group1")) + asserts.assert_equal(result.status, Status.Success, "Adding Group 1 failed") + + self.step("1b") + result = await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, Clusters.ScenesManagement.Commands.RemoveAllScenes(self.kGroup1)) + asserts.assert_equal(result.status, Status.Success, "Remove All Scenes failed on status") + asserts.assert_equal(result.groupID, self.kGroup1, "Remove All Scenes failed on groupID") + + self.step("1c") + result = await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, Clusters.ScenesManagement.Commands.GetSceneMembership(self.kGroup1)) + asserts.assert_equal(result.status, Status.Success, "Get Scene Membership failed on status") + asserts.assert_equal(result.groupID, self.kGroup1, "Get Scene Membership failed on groupID") + asserts.assert_equal(result.sceneList, [], "Get Scene Membership failed on sceneList") + + @async_test_body + async def teardown_test(self): + result = await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, Clusters.ScenesManagement.Commands.RemoveAllScenes(self.kGroup1)) + asserts.assert_equal(result.status, Status.Success, "Remove All Scenes failed on status") + asserts.assert_equal(result.groupID, self.kGroup1, "Remove All Scenes failed on groupID") + await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, Clusters.Groups.Commands.RemoveAllGroups()) + super().teardown_test() + + @async_test_body + async def test_TC_CC_10_1(self): + cluster = Clusters.Objects.ColorControl + attributes = cluster.Attributes + + self.step("1d") + if self.pics_guard(self.check_pics("CC.S.F04")): + ColorTempPhysicalMinMiredsValue = await self.read_single_attribute_check_success(cluster, attributes.ColorTempPhysicalMinMireds) + + self.step("1e") + if self.pics_guard(self.check_pics("CC.S.F04")): + ColorTempPhysicalMaxMiredsValue = await self.read_single_attribute_check_success(cluster, attributes.ColorTempPhysicalMaxMireds) + + self.step("2a") + if self.pics_guard(self.check_pics("CC.S.F00")): + await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, cluster.Commands.MoveToHueAndSaturation(200, 50, 0, 1, 1)) + + self.step("2b") + if self.pics_guard(self.check_pics("CC.S.F00")): + result = await self.TH1.ReadAttribute(self.dut_node_id, [(self.matter_test_config.endpoint, attributes.CurrentHue), (self.matter_test_config.endpoint, attributes.CurrentSaturation)]) + asserts.assert_less_equal(result[self.matter_test_config.endpoint][cluster] + [attributes.CurrentHue], 230, "CurrentHue is not less than or equal to 230") + asserts.assert_greater_equal(result[self.matter_test_config.endpoint][cluster][attributes.CurrentHue], 170, + "CurrentHue is not greater than or equal to 170") + asserts.assert_less_equal(result[self.matter_test_config.endpoint][cluster] + [attributes.CurrentSaturation], 58, "CurrentSaturation is not 58") + asserts.assert_greater_equal(result[self.matter_test_config.endpoint][cluster] + [attributes.CurrentSaturation], 42, "CurrentSaturation is not 42") + + self.step("2c") + if self.pics_guard(self.check_pics("CC.S.F03")): + await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, cluster.Commands.MoveToColor(32768, 19660, 0, 1, 1)) + + self.step("2d") + if self.pics_guard(self.check_pics("CC.S.F03")): + result = await self.TH1.ReadAttribute(self.dut_node_id, [(self.matter_test_config.endpoint, attributes.CurrentX), (self.matter_test_config.endpoint, attributes.CurrentY)]) + asserts.assert_less_equal(result[self.matter_test_config.endpoint][cluster] + [attributes.CurrentX], 35000, "CurrentX is not less than or equal to 35000") + asserts.assert_greater_equal(result[self.matter_test_config.endpoint][cluster][attributes.CurrentX], 31000, + "CurrentX is not greater than or equal to 31000") + asserts.assert_less_equal(result[self.matter_test_config.endpoint][cluster] + [attributes.CurrentY], 21000, "CurrentY is not less than or equal to 21000") + asserts.assert_greater_equal(result[self.matter_test_config.endpoint][cluster][attributes.CurrentY], 17000, + "CurrentY is not greater than or equal to 17000") + + self.step("2e") + if self.pics_guard(self.check_pics("CC.S.F04")): + await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, cluster.Commands.MoveToColorTemperature((ColorTempPhysicalMinMiredsValue + ColorTempPhysicalMaxMiredsValue) / 2, 0, 1, 1)) + await asyncio.sleep(1) + + self.step("2f") + if self.pics_guard(self.check_pics("CC.S.F04")): + await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, cluster.Commands.MoveColorTemperature(self.matter_test_config.endpoint, (ColorTempPhysicalMaxMiredsValue - ColorTempPhysicalMinMiredsValue) / 40, 1, 1)) + await asyncio.sleep(10) + + self.step("2g") + if self.pics_guard(self.check_pics("CC.S.F04")): + ColorTemperatureMireds = await self.read_single_attribute_check_success(cluster, attributes.ColorTemperatureMireds) + asserts.assert_less_equal(ColorTemperatureMireds, ColorTempPhysicalMaxMiredsValue, + "ColorTemperatureMireds is not less than or equal to ColorTempPhysicalMaxMireds") + asserts.assert_greater_equal(ColorTemperatureMireds, ColorTempPhysicalMinMiredsValue, + "ColorTemperatureMireds is not greater than or equal to ColorTempPhysicalMinMireds") + + self.step("2h") + if self.pics_guard(self.check_pics("CC.S.F01")): + await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, cluster.Commands.EnhancedMoveToHueAndSaturation(20000, 50, 0, 1, 1)) + + self.step("2i") + if self.pics_guard(self.check_pics("CC.S.F01")): + result = await self.TH1.ReadAttribute(self.dut_node_id, [(self.matter_test_config.endpoint, attributes.EnhancedCurrentHue), (self.matter_test_config.endpoint, attributes.CurrentSaturation)]) + asserts.assert_less_equal(result[self.matter_test_config.endpoint][cluster][attributes.EnhancedCurrentHue], 21800, + "EnhancedCurrentHue is not less than or equal to 21800") + asserts.assert_greater_equal(result[self.matter_test_config.endpoint][cluster][attributes.EnhancedCurrentHue], 18200, + "EnhancedCurrentHue is not greater than or equal to 18200") + asserts.assert_less_equal(result[self.matter_test_config.endpoint][cluster] + [attributes.CurrentSaturation], 58, "CurrentSaturation is not 58") + asserts.assert_greater_equal(result[self.matter_test_config.endpoint][cluster] + [attributes.CurrentSaturation], 42, "CurrentSaturation is not 42") + + self.step("3") + result = await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, Clusters.ScenesManagement.Commands.StoreScene(self.kGroup1, 0x01)) + asserts.assert_equal(result.status, Status.Success, "Store Scene failed on status") + asserts.assert_equal(result.groupID, self.kGroup1, "Store Scene failed on groupID") + asserts.assert_equal(result.sceneID, 0x01, "Store Scene failed on sceneID") + + self.step("4") + result = await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, Clusters.ScenesManagement.Commands.ViewScene(self.kGroup1, 0x01)) + asserts.assert_equal(result.status, Status.Success, "View Scene failed on status") + asserts.assert_equal(result.groupID, self.kGroup1, "View Scene failed on groupID") + asserts.assert_equal(result.sceneID, 0x01, "View Scene failed on sceneID") + asserts.assert_equal(result.transitionTime, 0, "View Scene failed on transitionTime") + + for EFS in result.extensionFieldSets: + if EFS.clusterID != 0x0300: + continue + + for AV in EFS.attributeValueList: + if AV.attributeID == 0x0001 and self.pics_guard(self.check_pics("CC.S.F00")): + asserts.assert_less_equal(AV.valueUnsigned8, 58, "View Scene failed on Satuation above limit") + asserts.assert_greater_equal(AV.valueUnsigned8, 42, "View Scene failed on Satuation below limit") + + if AV.attributeID == 0x0003 and self.pics_guard(self.check_pics("CC.S.F03")): + asserts.assert_less_equal(AV.valueUnsigned16, 35000, "View Scene failed on CurrentX above limit") + asserts.assert_greater_equal(AV.valueUnsigned16, 31000, "View Scene failed on CurrentX below limit") + + if AV.attributeID == 0x0004 and self.pics_guard(self.check_pics("CC.S.F03")): + asserts.assert_less_equal(AV.valueUnsigned16, 21000, "View Scene failed on CurrentY above limit") + asserts.assert_greater_equal(AV.valueUnsigned16, 17000, "View Scene failed on CurrentY below limit") + + if AV.attributeID == 0x0007 and self.pics_guard(self.check_pics("CC.S.F04")): + asserts.assert_less_equal(AV.valueUnsigned16, ColorTempPhysicalMaxMiredsValue, + "View Scene failed on ColorTemperatureMireds above limit") + asserts.assert_greater_equal(AV.valueUnsigned16, ColorTempPhysicalMinMiredsValue, + "View Scene failed on ColorTemperatureMireds below limit") + + if AV.attributeID == 0x4000 and self.pics_guard(self.check_pics("CC.S.F01")): + asserts.assert_less_equal(AV.valueUnsigned16, 21800, "View Scene failed on EnhancedHue above limit") + asserts.assert_greater_equal(AV.valueUnsigned16, 18200, "View Scene failed on EnhancedHue below limit") + + self.step("5a") + if self.pics_guard(self.check_pics("CC.S.F00")): + result = await self.TH1.SendCommand( + self.dut_node_id, self.matter_test_config.endpoint, + Clusters.ScenesManagement.Commands.AddScene( + self.kGroup1, + 0x02, + 0, + "Sat Scene", + [ + self._prepare_cc_extension_field_set( + [ + Clusters.ScenesManagement.Structs.AttributeValuePairStruct(attributeID=0x4001, valueUnsigned8=0x00), + Clusters.ScenesManagement.Structs.AttributeValuePairStruct(attributeID=0x0001, valueUnsigned8=0xFE) + ] + ) + ] + + ) + ) + asserts.assert_equal(result.status, Status.Success, "Add Scene failed on status") + asserts.assert_equal(result.groupID, self.kGroup1, "Add Scene failed on groupID") + asserts.assert_equal(result.sceneID, 0x02, "Add Scene failed on sceneID") + + self.step("5b") + if self.pics_guard(self.check_pics("CC.S.F00")): + await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, Clusters.ScenesManagement.Commands.RecallScene(self.kGroup1, 0x02)) + + self.step("5c") + if self.pics_guard(self.check_pics("CC.S.F00")): + CurrentSaturation = await self.read_single_attribute_check_success(cluster, attributes.CurrentSaturation) + asserts.assert_less_equal(CurrentSaturation, 0xFE, "CurrentSaturation is above limit") + asserts.assert_greater_equal(CurrentSaturation, 0xF6, "CurrentSaturation is below limit") + + self.step("6a") + if self.pics_guard(self.check_pics("CC.S.F03")): + result = await self.TH1.SendCommand( + self.dut_node_id, self.matter_test_config.endpoint, + Clusters.ScenesManagement.Commands.AddScene( + self.kGroup1, + 0x03, + 0, + "XY Scene", + [ + self._prepare_cc_extension_field_set( + [ + Clusters.ScenesManagement.Structs.AttributeValuePairStruct(attributeID=0x4001, valueUnsigned8=0x01), + Clusters.ScenesManagement.Structs.AttributeValuePairStruct( + attributeID=0x0003, valueUnsigned16=16334), + Clusters.ScenesManagement.Structs.AttributeValuePairStruct( + attributeID=0x0004, valueUnsigned16=13067) + ] + ) + ] + + ) + ) + asserts.assert_equal(result.status, Status.Success, "Add Scene failed on status") + asserts.assert_equal(result.groupID, self.kGroup1, "Add Scene failed on groupID") + asserts.assert_equal(result.sceneID, 0x03, "Add Scene failed on sceneID") + + self.step("6b") + if self.pics_guard(self.check_pics("CC.S.F03")): + await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, Clusters.ScenesManagement.Commands.RecallScene(self.kGroup1, 0x03)) + + self.step("6c") + if self.pics_guard(self.check_pics("CC.S.F03")): + result = await self.TH1.ReadAttribute(self.dut_node_id, [(self.matter_test_config.endpoint, attributes.CurrentX), (self.matter_test_config.endpoint, attributes.CurrentY)]) + asserts.assert_less_equal(result[self.matter_test_config.endpoint][cluster] + [attributes.CurrentX], 18000, "CurrentX is above limit") + asserts.assert_greater_equal(result[self.matter_test_config.endpoint][cluster] + [attributes.CurrentX], 14000, "CurrentX is below limit") + asserts.assert_less_equal(result[self.matter_test_config.endpoint][cluster] + [attributes.CurrentY], 15000, "CurrentY is above limit") + asserts.assert_greater_equal(result[self.matter_test_config.endpoint][cluster] + [attributes.CurrentY], 11000, "CurrentY is below limit") + + self.step("7a") + if self.pics_guard(self.check_pics("CC.S.F04")): + result = await self.TH1.SendCommand( + self.dut_node_id, self.matter_test_config.endpoint, + Clusters.ScenesManagement.Commands.AddScene( + self.kGroup1, + 0x04, + 0, + "Temp Scene", + [ + self._prepare_cc_extension_field_set( + [ + Clusters.ScenesManagement.Structs.AttributeValuePairStruct(attributeID=0x4001, valueUnsigned8=0x02), + Clusters.ScenesManagement.Structs.AttributeValuePairStruct(attributeID=0x0007, valueUnsigned16=175) + ] + ) + ] + + ) + ) + asserts.assert_equal(result.status, Status.Success, "Add Scene failed on status") + asserts.assert_equal(result.groupID, self.kGroup1, "Add Scene failed on groupID") + asserts.assert_equal(result.sceneID, 0x04, "Add Scene failed on sceneID") + + self.step("7b") + if self.pics_guard(self.check_pics("CC.S.F04")): + await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, Clusters.ScenesManagement.Commands.RecallScene(self.kGroup1, 0x04)) + + self.step("7c") + if self.pics_guard(self.check_pics("CC.S.F04")): + ColorTemperatureMireds = await self.read_single_attribute_check_success(cluster, attributes.ColorTemperatureMireds) + asserts.assert_less_equal(ColorTemperatureMireds, + ColorTempPhysicalMaxMiredsValue, "ColorTemperatureMireds is above limit") + asserts.assert_greater_equal(ColorTemperatureMireds, + ColorTempPhysicalMinMiredsValue, "ColorTemperatureMireds is below limit") + + self.step("8a") + if self.pics_guard(self.check_pics("CC.S.F01")): + result = await self.TH1.SendCommand( + self.dut_node_id, self.matter_test_config.endpoint, + Clusters.ScenesManagement.Commands.AddScene( + self.kGroup1, + 0x05, + 0, + "Enh Scene", + [ + self._prepare_cc_extension_field_set( + [ + Clusters.ScenesManagement.Structs.AttributeValuePairStruct(attributeID=0x4001, valueUnsigned8=0x03), + Clusters.ScenesManagement.Structs.AttributeValuePairStruct( + attributeID=0x4000, valueUnsigned16=12000), + Clusters.ScenesManagement.Structs.AttributeValuePairStruct(attributeID=0x0001, valueUnsigned8=70) + ] + ) + ] + + ) + ) + asserts.assert_equal(result.status, Status.Success, "Add Scene failed on status") + asserts.assert_equal(result.groupID, self.kGroup1, "Add Scene failed on groupID") + asserts.assert_equal(result.sceneID, 0x05, "Add Scene failed on sceneID") + + self.step("8b") + if self.pics_guard(self.check_pics("CC.S.F01")): + await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, Clusters.ScenesManagement.Commands.RecallScene(self.kGroup1, 0x05)) + + self.step("8c") + if self.pics_guard(self.check_pics("CC.S.F01")): + result = await self.TH1.ReadAttribute(self.dut_node_id, [(self.matter_test_config.endpoint, attributes.EnhancedCurrentHue), (self.matter_test_config.endpoint, attributes.CurrentSaturation)]) + asserts.assert_less_equal(result[self.matter_test_config.endpoint][cluster] + [attributes.EnhancedCurrentHue], 13800, "EnhancedCurrentHue is above limit") + asserts.assert_greater_equal(result[self.matter_test_config.endpoint][cluster][attributes.EnhancedCurrentHue], + 10200, "EnhancedCurrentHue is below limit") + asserts.assert_less_equal(result[self.matter_test_config.endpoint][cluster] + [attributes.CurrentSaturation], 78, "CurrentSaturation is above limit") + asserts.assert_greater_equal(result[self.matter_test_config.endpoint][cluster] + [attributes.CurrentSaturation], 62, "CurrentSaturation is below limit") + + self.step("9a") + if self.pics_guard(self.check_pics("CC.S.F02")): + result = await self.TH1.SendCommand( + self.dut_node_id, self.matter_test_config.endpoint, + Clusters.ScenesManagement.Commands.AddScene( + self.kGroup1, + 0x06, + 0, + "Loop Scene", + [ + self._prepare_cc_extension_field_set( + [ + Clusters.ScenesManagement.Structs.AttributeValuePairStruct(attributeID=0x4002, valueUnsigned8=0x01), + Clusters.ScenesManagement.Structs.AttributeValuePairStruct(attributeID=0x4003, valueUnsigned8=0x01), + Clusters.ScenesManagement.Structs.AttributeValuePairStruct(attributeID=0x4004, valueUnsigned16=5) + ] + ) + ] + + ) + ) + asserts.assert_equal(result.status, Status.Success, "Add Scene failed on status") + asserts.assert_equal(result.groupID, self.kGroup1, "Add Scene failed on groupID") + asserts.assert_equal(result.sceneID, 0x06, "Add Scene failed on sceneID") + + self.step("9b") + if self.pics_guard(self.check_pics("CC.S.F02")): + await self.TH1.SendCommand(self.dut_node_id, self.matter_test_config.endpoint, Clusters.ScenesManagement.Commands.RecallScene(self.kGroup1, 0x06)) + + self.step("9c") + if self.pics_guard(self.check_pics("CC.S.F02")): + ColorLoopActive = await self.read_single_attribute_check_success(cluster, attributes.ColorLoopActive) + asserts.assert_equal(ColorLoopActive, 1, "ColorLoopActive is not 1") + + self.step("9d") + if self.pics_guard(self.check_pics("CC.S.F02")): + ColorLoopDirection = await self.read_single_attribute_check_success(cluster, attributes.ColorLoopDirection) + asserts.assert_equal(ColorLoopDirection, 1, "ColorLoopDirection is not 1") + + self.step("9e") + if self.pics_guard(self.check_pics("CC.S.C44.Rsp")): + ColorLoopTime = await self.read_single_attribute_check_success(cluster, attributes.ColorLoopTime) + asserts.assert_equal(ColorLoopTime, 5, "ColorLoopTime is not 5") + + +if __name__ == "__main__": + default_matter_test_main() From 55786a0e0f18fa593d3f270885568aab5e54ae4c Mon Sep 17 00:00:00 2001 From: Terence Hampson Date: Fri, 26 Jul 2024 16:30:48 -0400 Subject: [PATCH 43/49] Add Ecosystem Information Cluster Server implementation (#34459) --- scripts/tools/check_includes_config.py | 2 + .../ecosystem-information-server.cpp | 377 ++++++++++++++++++ .../ecosystem-information-server.h | 205 ++++++++++ src/app/zap_cluster_list.json | 2 +- 4 files changed, 585 insertions(+), 1 deletion(-) create mode 100644 src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp create mode 100644 src/app/clusters/ecosystem-information-server/ecosystem-information-server.h diff --git a/scripts/tools/check_includes_config.py b/scripts/tools/check_includes_config.py index 20c853b9906df8..d897c86e32cfe8 100644 --- a/scripts/tools/check_includes_config.py +++ b/scripts/tools/check_includes_config.py @@ -128,6 +128,8 @@ 'src/app/clusters/application-launcher-server/application-launcher-server.cpp': {'string'}, 'src/app/clusters/application-launcher-server/application-launcher-delegate.h': {'list'}, 'src/app/clusters/audio-output-server/audio-output-delegate.h': {'list'}, + # EcosystemInformationCluster is for Fabric Sync and is intended to run on device that are capable of handling these types. + 'src/app/clusters/ecosystem-information-server/ecosystem-information-server.h': {'map', 'string', 'vector'}, 'src/app/clusters/channel-server/channel-delegate.h': {'list'}, 'src/app/clusters/content-launch-server/content-launch-delegate.h': {'list'}, 'src/app/clusters/content-launch-server/content-launch-server.cpp': {'list'}, diff --git a/src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp b/src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp new file mode 100644 index 00000000000000..26f1c96cd65fa6 --- /dev/null +++ b/src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp @@ -0,0 +1,377 @@ +/* + * + * 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 "ecosystem-information-server.h" + +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace EcosystemInformation { +namespace { + +constexpr size_t kDeviceNameMaxSize = 64; +constexpr size_t kUniqueLocationIdMaxSize = 64; +constexpr size_t kUniqueLocationIdsListMaxSize = 64; +constexpr size_t kLocationDescriptorNameMaxSize = 128; + +constexpr size_t kDeviceDirectoryMaxSize = 256; +constexpr size_t kLocationDirectoryMaxSize = 64; + +class AttrAccess : public AttributeAccessInterface +{ +public: + // Register for the EcosystemInformationCluster on all endpoints. + AttrAccess() : AttributeAccessInterface(Optional::Missing(), Clusters::EcosystemInformation::Id) {} + + CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; +}; + +CHIP_ERROR AttrAccess::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + VerifyOrDie(aPath.mClusterId == Clusters::EcosystemInformation::Id); + switch (aPath.mAttributeId) + { + case Attributes::RemovedOn::Id: + return EcosystemInformationServer::Instance().EncodeRemovedOnAttribute(aPath.mEndpointId, aEncoder); + case Attributes::DeviceDirectory ::Id: + return EcosystemInformationServer::Instance().EncodeDeviceDirectoryAttribute(aPath.mEndpointId, aEncoder); + case Attributes::LocationDirectory ::Id: + return EcosystemInformationServer::Instance().EncodeLocationStructAttribute(aPath.mEndpointId, aEncoder); + default: + break; + } + return CHIP_NO_ERROR; +} + +// WARNING: caller is expected to use the returned LocationDescriptorStruct::Type immediately. Caller must +// be certain that the provided aLocationDescriptor has not been destroyed, prior to using the return +// struct to encode. +// TODO(#33223) To improve safety we could make GetEncodableLocationDescriptorStruct a private +// memeber method where we explicitly delete member method for the parameter that matches +// (LocationDescriptorStruct && aLocationDescriptor). +Structs::LocationDescriptorStruct::Type GetEncodableLocationDescriptorStruct(const LocationDescriptorStruct & aLocationDescriptor) +{ + Structs::LocationDescriptorStruct::Type locationDescriptor; + // This would imply data is either not properly validated before being + // stored here or corruption has occurred. + VerifyOrDie(!aLocationDescriptor.mLocationName.empty()); + locationDescriptor.locationName = CharSpan(aLocationDescriptor.mLocationName.c_str(), aLocationDescriptor.mLocationName.size()); + + if (aLocationDescriptor.mFloorNumber.has_value()) + { + locationDescriptor.floorNumber.SetNonNull(aLocationDescriptor.mFloorNumber.value()); + } + else + { + locationDescriptor.floorNumber.SetNull(); + } + + if (aLocationDescriptor.mAreaType.has_value()) + { + locationDescriptor.areaType.SetNonNull(aLocationDescriptor.mAreaType.value()); + } + else + { + locationDescriptor.areaType.SetNull(); + } + return locationDescriptor; +} + +} // namespace + +EcosystemDeviceStruct::Builder & EcosystemDeviceStruct::Builder::SetDeviceName(std::string aDeviceName, + uint64_t aDeviceNameLastEditEpochUs) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mDeviceName = std::move(aDeviceName); + mDeviceNameLastEditEpochUs = aDeviceNameLastEditEpochUs; + return *this; +} + +EcosystemDeviceStruct::Builder & EcosystemDeviceStruct::Builder::SetBrigedEndpoint(EndpointId aBridgedEndpoint) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mBridgedEndpoint = aBridgedEndpoint; + return *this; +} + +EcosystemDeviceStruct::Builder & EcosystemDeviceStruct::Builder::SetOriginalEndpoint(EndpointId aOriginalEndpoint) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mOriginalEndpoint = aOriginalEndpoint; + return *this; +} + +EcosystemDeviceStruct::Builder & EcosystemDeviceStruct::Builder::AddDeviceType(Structs::DeviceTypeStruct::Type aDeviceType) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mDeviceTypes.push_back(std::move(aDeviceType)); + return *this; +} + +EcosystemDeviceStruct::Builder & EcosystemDeviceStruct::Builder::AddUniqueLocationId(std::string aUniqueLocationId, + uint64_t aUniqueLocationIdsLastEditEpochUs) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mUniqueLocationIds.push_back(std::move(aUniqueLocationId)); + mUniqueLocationIdsLastEditEpochUs = aUniqueLocationIdsLastEditEpochUs; + return *this; +} + +std::unique_ptr EcosystemDeviceStruct::Builder::Build() +{ + VerifyOrReturnValue(!mIsAlreadyBuilt, nullptr, ChipLogError(Zcl, "Build() already called")); + VerifyOrReturnValue(mDeviceName.size() <= kDeviceNameMaxSize, nullptr, ChipLogError(Zcl, "Device name too large")); + VerifyOrReturnValue(mOriginalEndpoint != kInvalidEndpointId, nullptr, ChipLogError(Zcl, "Invalid original endpoint")); + VerifyOrReturnValue(!mDeviceTypes.empty(), nullptr, ChipLogError(Zcl, "No device types added")); + VerifyOrReturnValue(mUniqueLocationIds.size() <= kUniqueLocationIdsListMaxSize, nullptr, + ChipLogError(Zcl, "Too many location ids")); + + for (auto & locationId : mUniqueLocationIds) + { + VerifyOrReturnValue(locationId.size() <= kUniqueLocationIdMaxSize, nullptr, ChipLogError(Zcl, "Location id too long")); + } + + // std::make_unique does not have access to private constructor we workaround with using new + std::unique_ptr ret{ new EcosystemDeviceStruct( + std::move(mDeviceName), mDeviceNameLastEditEpochUs, mBridgedEndpoint, mOriginalEndpoint, std::move(mDeviceTypes), + std::move(mUniqueLocationIds), mUniqueLocationIdsLastEditEpochUs) }; + mIsAlreadyBuilt = true; + return ret; +} + +CHIP_ERROR EcosystemDeviceStruct::Encode(const AttributeValueEncoder::ListEncodeHelper & aEncoder, const FabricIndex & aFabricIndex) +{ + Structs::EcosystemDeviceStruct::Type deviceStruct; + if (!mDeviceName.empty()) + { + deviceStruct.deviceName.SetValue(CharSpan(mDeviceName.c_str(), mDeviceName.size())); + // When there is a device name we also include mDeviceNameLastEditEpochUs + deviceStruct.deviceNameLastEdit.SetValue(mDeviceNameLastEditEpochUs); + } + deviceStruct.bridgedEndpoint = mBridgedEndpoint; + deviceStruct.originalEndpoint = mOriginalEndpoint; + deviceStruct.deviceTypes = DataModel::List(mDeviceTypes.data(), mDeviceTypes.size()); + + std::vector locationIds; + locationIds.reserve(mUniqueLocationIds.size()); + for (auto & id : mUniqueLocationIds) + { + locationIds.push_back(CharSpan(id.c_str(), id.size())); + } + deviceStruct.uniqueLocationIDs = DataModel::List(locationIds.data(), locationIds.size()); + + deviceStruct.uniqueLocationIDsLastEdit = mUniqueLocationIdsLastEditEpochUs; + + // TODO(#33223) this is a hack, use mFabricIndex when it exists. + deviceStruct.SetFabricIndex(aFabricIndex); + return aEncoder.Encode(deviceStruct); +} + +EcosystemLocationStruct::Builder & EcosystemLocationStruct::Builder::SetLocationName(std::string aLocationName) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mLocationDescriptor.mLocationName = std::move(aLocationName); + return *this; +} + +EcosystemLocationStruct::Builder & EcosystemLocationStruct::Builder::SetFloorNumber(std::optional aFloorNumber) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mLocationDescriptor.mFloorNumber = aFloorNumber; + return *this; +} + +EcosystemLocationStruct::Builder & EcosystemLocationStruct::Builder::SetAreaTypeTag(std::optional aAreaTypeTag) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mLocationDescriptor.mAreaType = aAreaTypeTag; + return *this; +} + +EcosystemLocationStruct::Builder & +EcosystemLocationStruct::Builder::SetLocationDescriptorLastEdit(uint64_t aLocationDescriptorLastEditEpochUs) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mLocationDescriptorLastEditEpochUs = aLocationDescriptorLastEditEpochUs; + return *this; +} + +std::unique_ptr EcosystemLocationStruct::Builder::Build() +{ + VerifyOrReturnValue(!mIsAlreadyBuilt, nullptr, ChipLogError(Zcl, "Build() already called")); + VerifyOrReturnValue(!mLocationDescriptor.mLocationName.empty(), nullptr, ChipLogError(Zcl, "Must Provided Location Name")); + VerifyOrReturnValue(mLocationDescriptor.mLocationName.size() <= kLocationDescriptorNameMaxSize, nullptr, + ChipLogError(Zcl, "Must Location Name must be less than 64 bytes")); + + // std::make_unique does not have access to private constructor we workaround with using new + std::unique_ptr ret{ new EcosystemLocationStruct(std::move(mLocationDescriptor), + mLocationDescriptorLastEditEpochUs) }; + mIsAlreadyBuilt = true; + return ret; +} + +CHIP_ERROR EcosystemLocationStruct::Encode(const AttributeValueEncoder::ListEncodeHelper & aEncoder, + const std::string & aUniqueLocationId, const FabricIndex & aFabricIndex) +{ + Structs::EcosystemLocationStruct::Type locationStruct; + VerifyOrDie(!aUniqueLocationId.empty()); + locationStruct.uniqueLocationID = CharSpan(aUniqueLocationId.c_str(), aUniqueLocationId.size()); + locationStruct.locationDescriptor = GetEncodableLocationDescriptorStruct(mLocationDescriptor); + locationStruct.locationDescriptorLastEdit = mLocationDescriptorLastEditEpochUs; + + // TODO(#33223) this is a hack, use mFabricIndex when it exists. + locationStruct.SetFabricIndex(aFabricIndex); + return aEncoder.Encode(locationStruct); +} + +EcosystemInformationServer EcosystemInformationServer::mInstance; + +EcosystemInformationServer & EcosystemInformationServer::Instance() +{ + return mInstance; +} + +CHIP_ERROR EcosystemInformationServer::AddDeviceInfo(EndpointId aEndpoint, std::unique_ptr aDevice) +{ + VerifyOrReturnError(aDevice, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError((aEndpoint != kRootEndpointId && aEndpoint != kInvalidEndpointId), CHIP_ERROR_INVALID_ARGUMENT); + + auto & deviceInfo = mDevicesMap[aEndpoint]; + VerifyOrReturnError((deviceInfo.mDeviceDirectory.size() < kDeviceDirectoryMaxSize), CHIP_ERROR_NO_MEMORY); + deviceInfo.mDeviceDirectory.push_back(std::move(aDevice)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR EcosystemInformationServer::AddLocationInfo(EndpointId aEndpoint, const std::string & aLocationId, + std::unique_ptr aLocation) +{ + VerifyOrReturnError(aLocation, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError((aEndpoint != kRootEndpointId && aEndpoint != kInvalidEndpointId), CHIP_ERROR_INVALID_ARGUMENT); + + auto & deviceInfo = mDevicesMap[aEndpoint]; + VerifyOrReturnError((deviceInfo.mLocationDirectory.find(aLocationId) == deviceInfo.mLocationDirectory.end()), + CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError((deviceInfo.mLocationDirectory.size() < kLocationDirectoryMaxSize), CHIP_ERROR_NO_MEMORY); + deviceInfo.mLocationDirectory[aLocationId] = std::move(aLocation); + return CHIP_NO_ERROR; +} + +CHIP_ERROR EcosystemInformationServer::RemoveDevice(EndpointId aEndpoint, uint64_t aEpochUs) +{ + auto it = mDevicesMap.find(aEndpoint); + VerifyOrReturnError((it != mDevicesMap.end()), CHIP_ERROR_INVALID_ARGUMENT); + auto & deviceInfo = it->second; + deviceInfo.mRemovedOn.SetValue(aEpochUs); + return CHIP_NO_ERROR; +} + +CHIP_ERROR EcosystemInformationServer::EncodeRemovedOnAttribute(EndpointId aEndpoint, AttributeValueEncoder & aEncoder) +{ + auto it = mDevicesMap.find(aEndpoint); + if (it == mDevicesMap.end()) + { + // We are always going to be given a valid endpoint. If the endpoint + // doesn't exist in our map that indicate that the cluster was not + // added on this endpoint, hence UnsupportedCluster. + return CHIP_IM_GLOBAL_STATUS(UnsupportedCluster); + } + + auto & deviceInfo = it->second; + if (!deviceInfo.mRemovedOn.HasValue()) + { + aEncoder.EncodeNull(); + return CHIP_NO_ERROR; + } + + aEncoder.Encode(deviceInfo.mRemovedOn.Value()); + return CHIP_NO_ERROR; +} + +CHIP_ERROR EcosystemInformationServer::EncodeDeviceDirectoryAttribute(EndpointId aEndpoint, AttributeValueEncoder & aEncoder) +{ + + auto it = mDevicesMap.find(aEndpoint); + if (it == mDevicesMap.end()) + { + // We are always going to be given a valid endpoint. If the endpoint + // doesn't exist in our map that indicate that the cluster was not + // added on this endpoint, hence UnsupportedCluster. + return CHIP_IM_GLOBAL_STATUS(UnsupportedCluster); + } + + auto & deviceInfo = it->second; + if (deviceInfo.mDeviceDirectory.empty() || deviceInfo.mRemovedOn.HasValue()) + { + return aEncoder.EncodeEmptyList(); + } + + FabricIndex fabricIndex = aEncoder.AccessingFabricIndex(); + return aEncoder.EncodeList([&](const auto & encoder) -> CHIP_ERROR { + for (auto & device : deviceInfo.mDeviceDirectory) + { + ReturnErrorOnFailure(device->Encode(encoder, fabricIndex)); + } + return CHIP_NO_ERROR; + }); +} + +CHIP_ERROR EcosystemInformationServer::EncodeLocationStructAttribute(EndpointId aEndpoint, AttributeValueEncoder & aEncoder) +{ + auto it = mDevicesMap.find(aEndpoint); + if (it == mDevicesMap.end()) + { + // We are always going to be given a valid endpoint. If the endpoint + // doesn't exist in our map that indicate that the cluster was not + // added on this endpoint, hence UnsupportedCluster. + return CHIP_IM_GLOBAL_STATUS(UnsupportedCluster); + } + + auto & deviceInfo = it->second; + if (deviceInfo.mLocationDirectory.empty() || deviceInfo.mRemovedOn.HasValue()) + { + return aEncoder.EncodeEmptyList(); + } + + FabricIndex fabricIndex = aEncoder.AccessingFabricIndex(); + return aEncoder.EncodeList([&](const auto & encoder) -> CHIP_ERROR { + for (auto & [id, device] : deviceInfo.mLocationDirectory) + { + ReturnErrorOnFailure(device->Encode(encoder, id, fabricIndex)); + } + return CHIP_NO_ERROR; + }); + return CHIP_NO_ERROR; +} + +} // namespace EcosystemInformation +} // namespace Clusters +} // namespace app +} // namespace chip + +// ----------------------------------------------------------------------------- +// Plugin initialization + +chip::app::Clusters::EcosystemInformation::AttrAccess gAttrAccess; + +void MatterEcosystemInformationPluginServerInitCallback() +{ + registerAttributeAccessOverride(&gAttrAccess); +} diff --git a/src/app/clusters/ecosystem-information-server/ecosystem-information-server.h b/src/app/clusters/ecosystem-information-server/ecosystem-information-server.h new file mode 100644 index 00000000000000..f8407861992d3c --- /dev/null +++ b/src/app/clusters/ecosystem-information-server/ecosystem-information-server.h @@ -0,0 +1,205 @@ +/* + * + * 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. + */ +#pragma once + +// This cluster is targeted by devices that are not resource constrained, for +// that reason we use std containers to simplify implementation of the cluster. +#include +#include +#include +#include +#include + +#include + +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace EcosystemInformation { + +// This intentionally mirrors Structs::EcosystemDeviceStruct::Type but has ownership +// of underlying types. +class EcosystemDeviceStruct +{ +public: + class Builder + { + public: + Builder(){}; + + Builder & SetDeviceName(std::string aDeviceName, uint64_t aDeviceNameLastEditEpochUs); + Builder & SetBrigedEndpoint(EndpointId aBridgedEndpoint); + Builder & SetOriginalEndpoint(EndpointId aOriginalEndpoint); + Builder & AddDeviceType(Structs::DeviceTypeStruct::Type aDeviceType); + Builder & AddUniqueLocationId(std::string aUniqueLocationId, uint64_t aUniqueLocationIdsLastEditEpochUs); + + // Upon success this object will have moved all ownership of underlying + // types to EcosystemDeviceStruct and should not be used afterwards. + std::unique_ptr Build(); + + private: + std::string mDeviceName; + uint64_t mDeviceNameLastEditEpochUs = 0; + EndpointId mBridgedEndpoint = kInvalidEndpointId; + EndpointId mOriginalEndpoint = kInvalidEndpointId; + std::vector mDeviceTypes; + std::vector mUniqueLocationIds; + uint64_t mUniqueLocationIdsLastEditEpochUs = 0; + bool mIsAlreadyBuilt = false; + }; + + CHIP_ERROR Encode(const AttributeValueEncoder::ListEncodeHelper & aEncoder, const FabricIndex & aFabricIndex); + +private: + // Constructor is intentionally private. This is to ensure that it is only constructed with + // values that conform to the spec. + explicit EcosystemDeviceStruct(std::string && aDeviceName, uint64_t aDeviceNameLastEditEpochUs, EndpointId aBridgedEndpoint, + EndpointId aOriginalEndpoint, std::vector && aDeviceTypes, + std::vector && aUniqueLocationIds, uint64_t aUniqueLocationIdsLastEditEpochUs) : + mDeviceName(std::move(aDeviceName)), + mDeviceNameLastEditEpochUs(aDeviceNameLastEditEpochUs), mBridgedEndpoint(aBridgedEndpoint), + mOriginalEndpoint(aOriginalEndpoint), mDeviceTypes(std::move(aDeviceTypes)), + mUniqueLocationIds(std::move(aUniqueLocationIds)), mUniqueLocationIdsLastEditEpochUs(aUniqueLocationIdsLastEditEpochUs) + {} + + const std::string mDeviceName; + uint64_t mDeviceNameLastEditEpochUs; + EndpointId mBridgedEndpoint; + EndpointId mOriginalEndpoint; + std::vector mDeviceTypes; + std::vector mUniqueLocationIds; + uint64_t mUniqueLocationIdsLastEditEpochUs; + // TODO(#33223) This structure needs to contain fabric index to be spec compliant. + // To keep initial PR smaller, we are going to assume that all entries + // here are for any fabric. This will allow follow up PR introducing + // fabric scoped to be more throughly reviewed with focus on fabric scoping. +}; + +struct LocationDescriptorStruct +{ + std::string mLocationName; + std::optional mFloorNumber; + std::optional mAreaType; +}; + +// This intentionally mirrors Structs::EcosystemLocationStruct::Type but has ownership +// of underlying types. +class EcosystemLocationStruct +{ +public: + class Builder + { + public: + Builder(){}; + + Builder & SetLocationName(std::string aLocationName); + Builder & SetFloorNumber(std::optional aFloorNumber); + Builder & SetAreaTypeTag(std::optional aAreaTypeTag); + Builder & SetLocationDescriptorLastEdit(uint64_t aLocationDescriptorLastEditEpochUs); + + // Upon success this object will have moved all ownership of underlying + // types to EcosystemDeviceStruct and should not be used afterwards. + std::unique_ptr Build(); + + private: + LocationDescriptorStruct mLocationDescriptor; + uint64_t mLocationDescriptorLastEditEpochUs = 0; + bool mIsAlreadyBuilt = false; + }; + + CHIP_ERROR Encode(const AttributeValueEncoder::ListEncodeHelper & aEncoder, const std::string & aUniqueLocationId, + const FabricIndex & aFabricIndex); + +private: + // Constructor is intentionally private. This is to ensure that it is only constructed with + // values that conform to the spec. + explicit EcosystemLocationStruct(LocationDescriptorStruct && aLocationDescriptor, uint64_t aLocationDescriptorLastEditEpochUs) : + mLocationDescriptor(aLocationDescriptor), mLocationDescriptorLastEditEpochUs(aLocationDescriptorLastEditEpochUs) + {} + // EcosystemLocationStruct is used as a value in a key-value map. + // Because UniqueLocationId is manditory when an entry exist, and + // it is unique, we use it as a key to the key-value pair and is why it is + // not explicitly in this struct. + LocationDescriptorStruct mLocationDescriptor; + uint64_t mLocationDescriptorLastEditEpochUs; + // TODO(#33223) This structure needs to contain fabric index to be spec compliant. + // To keep initial PR smaller, we are going to assume that all entries + // here are for any fabric. This will allow follow up PR introducing + // fabric scoped to be more throughly reviewed with focus on fabric scoping. +}; + +class EcosystemInformationServer +{ +public: + static EcosystemInformationServer & Instance(); + + /** + * @brief Adds device as entry to DeviceDirectory list Attribute. + * + * @param[in] aEndpoint Which endpoint is the device being added to the device directory. + * @param[in] aDevice Device information. + * @return #CHIP_NO_ERROR on success. + * @return Other CHIP_ERROR associated with issue. + */ + CHIP_ERROR AddDeviceInfo(EndpointId aEndpoint, std::unique_ptr aDevice); + /** + * @brief Adds location as entry to LocationDirectory list Attribute. + * + * @param[in] aEndpoint Which endpoint is the location being added to the location directory. + * @param[in] aLocationId LocationID associated with location. + * @param[in] aLocation Location information. + * @return #CHIP_NO_ERROR on success. + * @return Other CHIP_ERROR associated with issue. + */ + CHIP_ERROR AddLocationInfo(EndpointId aEndpoint, const std::string & aLocationId, + std::unique_ptr aLocation); + + /** + * @brief Removes device at the provided endpoint. + * + * @param aEndpoint Endpoint of the associated device that has been removed. + * @param aEpochUs Epoch time in micro seconds assoicated with when device was removed. + * @return #CHIP_NO_ERROR on success. + * @return Other CHIP_ERROR associated with issue. + */ + CHIP_ERROR RemoveDevice(EndpointId aEndpoint, uint64_t aEpochUs); + // TODO(#33223) Add removal and update counterparts to AddDeviceInfo and AddLocationInfo. + + CHIP_ERROR EncodeRemovedOnAttribute(EndpointId aEndpoint, AttributeValueEncoder & aEncoder); + CHIP_ERROR EncodeDeviceDirectoryAttribute(EndpointId aEndpoint, AttributeValueEncoder & aEncoder); + CHIP_ERROR EncodeLocationStructAttribute(EndpointId aEndpoint, AttributeValueEncoder & aEncoder); + +private: + struct DeviceInfo + { + Optional mRemovedOn; + std::vector> mDeviceDirectory; + // Map key is using the UniqueLocationId + std::map> mLocationDirectory; + }; + std::map mDevicesMap; + + static EcosystemInformationServer mInstance; +}; + +} // namespace EcosystemInformation +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/zap_cluster_list.json b/src/app/zap_cluster_list.json index 285bd6e6398b73..4852b22b3bfa68 100644 --- a/src/app/zap_cluster_list.json +++ b/src/app/zap_cluster_list.json @@ -186,7 +186,7 @@ "DISHWASHER_MODE_CLUSTER": ["mode-base-server"], "MICROWAVE_OVEN_MODE_CLUSTER": ["mode-base-server"], "DOOR_LOCK_CLUSTER": ["door-lock-server"], - "ECOSYSTEM_INFORMATION_CLUSTER": [], + "ECOSYSTEM_INFORMATION_CLUSTER": ["ecosystem-information-server"], "ELECTRICAL_ENERGY_MEASUREMENT_CLUSTER": [ "electrical-energy-measurement-server" ], From a25f19115f8a855d335c904c6203158f31655c19 Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Fri, 26 Jul 2024 19:33:24 -0400 Subject: [PATCH 44/49] Generate `.matter` IDL to contain global structures (#34545) * Starting a template implementation * Move file to propper location * Start adding some global types in codegen * Fix indent * Undo large changes and fix spacing * zap regen * Prepare to remove the extra Bitmap types * Different style workaround * Fix the condition ending to make it work * zap regen * Undo file change * Undo file change * Matter IDL indent is a bit better * Fighting with zap/handlebars intend... this is terrible * zap-regen * Self code review cleanup --------- Co-authored-by: Andrei Litvin --- .../air-purifier-app.matter | 17 +++++++++++ .../air-quality-sensor-app.matter | 17 +++++++++++ .../all-clusters-app.matter | 17 +++++++++++ .../all-clusters-minimal-app.matter | 17 +++++++++++ .../bridge-common/bridge-app.matter | 17 +++++++++++ ...p_rootnode_dimmablelight_bCwGYSDpoe.matter | 17 +++++++++++ .../rootnode_airpurifier_73a6fe2651.matter | 17 +++++++++++ ...umiditysensor_thermostat_56de3d5f45.matter | 17 +++++++++++ ...ootnode_airqualitysensor_e63187f6c9.matter | 17 +++++++++++ ...ootnode_basicvideoplayer_0ff86e943b.matter | 17 +++++++++++ ...de_colortemperaturelight_hbUnzYVeyn.matter | 17 +++++++++++ .../rootnode_contactsensor_27f76aeaf5.matter | 17 +++++++++++ .../rootnode_contactsensor_lFAGG1bfRO.matter | 17 +++++++++++ .../rootnode_dimmablelight_bCwGYSDpoe.matter | 17 +++++++++++ ...tnode_dimmablepluginunit_f8a9a0b9d4.matter | 17 +++++++++++ .../rootnode_dishwasher_cc105034fe.matter | 17 +++++++++++ .../rootnode_doorlock_aNKYAreMXE.matter | 17 +++++++++++ ...tnode_extendedcolorlight_8lcaaYJVAa.matter | 17 +++++++++++ .../devices/rootnode_fan_7N2TobIlOX.matter | 17 +++++++++++ .../rootnode_flowsensor_1zVxHedlaV.matter | 17 +++++++++++ .../rootnode_genericswitch_2dfff6e516.matter | 17 +++++++++++ .../rootnode_genericswitch_9866e35d0b.matter | 17 +++++++++++ ...tnode_heatingcoolingunit_ncdGai1E5a.matter | 17 +++++++++++ .../rootnode_humiditysensor_Xyj4gda6Hb.matter | 17 +++++++++++ .../rootnode_laundrywasher_fb10d238c8.matter | 17 +++++++++++ .../rootnode_lightsensor_lZQycTFcJK.matter | 17 +++++++++++ ...rootnode_occupancysensor_iHyVgifZuo.matter | 17 +++++++++++ .../rootnode_onofflight_bbs1b7IaOV.matter | 17 +++++++++++ .../rootnode_onofflight_samplemei.matter | 17 +++++++++++ ...ootnode_onofflightswitch_FsPlMr090Q.matter | 17 +++++++++++ ...rootnode_onoffpluginunit_Wtf8ss5EBY.matter | 17 +++++++++++ .../rootnode_pressuresensor_s0qC9wLH4k.matter | 17 +++++++++++ .../devices/rootnode_pump_5f904818cc.matter | 17 +++++++++++ .../devices/rootnode_pump_a811bb33a0.matter | 17 +++++++++++ ...eraturecontrolledcabinet_ffdb696680.matter | 17 +++++++++++ ...ode_roboticvacuumcleaner_1807ff0c49.matter | 17 +++++++++++ ...tnode_roomairconditioner_9cf3607804.matter | 17 +++++++++++ .../rootnode_smokecoalarm_686fe0dcb8.matter | 17 +++++++++++ .../rootnode_speaker_RpzeXdimqA.matter | 17 +++++++++++ ...otnode_temperaturesensor_Qy1zkNW7c3.matter | 17 +++++++++++ .../rootnode_thermostat_bm3fb8dhYi.matter | 17 +++++++++++ .../rootnode_windowcovering_RLCxaGi9Yx.matter | 17 +++++++++++ .../contact-sensor-app.matter | 17 +++++++++++ .../nxp/zap-lit/contact-sensor-app.matter | 17 +++++++++++ .../nxp/zap-sit/contact-sensor-app.matter | 17 +++++++++++ .../dishwasher-common/dishwasher-app.matter | 17 +++++++++++ .../energy-management-app.matter | 17 +++++++++++ .../fabric-bridge-app.matter | 17 +++++++++++ .../nxp/zap/laundry-washer-app.matter | 17 +++++++++++ .../light-switch-app.matter | 17 +++++++++++ .../light-switch-app/qpg/zap/switch.matter | 17 +++++++++++ .../data_model/lighting-app-ethernet.matter | 17 +++++++++++ .../data_model/lighting-app-thread.matter | 17 +++++++++++ .../data_model/lighting-app-wifi.matter | 17 +++++++++++ .../lighting-common/lighting-app.matter | 17 +++++++++++ .../nxp/zap/lighting-on-off.matter | 17 +++++++++++ examples/lighting-app/qpg/zap/light.matter | 17 +++++++++++ .../data_model/lighting-thread-app.matter | 17 +++++++++++ .../data_model/lighting-wifi-app.matter | 17 +++++++++++ .../lit-icd-common/lit-icd-server-app.matter | 17 +++++++++++ examples/lock-app/lock-common/lock-app.matter | 17 +++++++++++ examples/lock-app/nxp/zap/lock-app.matter | 17 +++++++++++ examples/lock-app/qpg/zap/lock.matter | 17 +++++++++++ .../log-source-common/log-source-app.matter | 17 +++++++++++ .../microwave-oven-app.matter | 17 +++++++++++ .../network-manager-app.matter | 17 +++++++++++ .../ota-provider-app.matter | 17 +++++++++++ .../ota-requestor-app.matter | 17 +++++++++++ .../placeholder/linux/apps/app1/config.matter | 17 +++++++++++ .../placeholder/linux/apps/app2/config.matter | 17 +++++++++++ examples/pump-app/pump-common/pump-app.matter | 17 +++++++++++ .../silabs/data_model/pump-thread-app.matter | 17 +++++++++++ .../silabs/data_model/pump-wifi-app.matter | 17 +++++++++++ .../pump-controller-app.matter | 17 +++++++++++ .../refrigerator-app.matter | 17 +++++++++++ examples/rvc-app/rvc-common/rvc-app.matter | 17 +++++++++++ .../smoke-co-alarm-app.matter | 17 +++++++++++ .../temperature-measurement.matter | 17 +++++++++++ .../nxp/zap/thermostat_matter_thread.matter | 17 +++++++++++ .../nxp/zap/thermostat_matter_wifi.matter | 17 +++++++++++ .../qpg/zap/thermostaticRadiatorValve.matter | 17 +++++++++++ .../thermostat-common/thermostat.matter | 17 +++++++++++ examples/tv-app/tv-common/tv-app.matter | 17 +++++++++++ .../tv-casting-common/tv-casting-app.matter | 17 +++++++++++ .../virtual-device-app.matter | 17 +++++++++++ examples/window-app/common/window-app.matter | 17 +++++++++++ .../matter_idl/generators/idl/MatterIdl.jinja | 24 +++++++-------- src/app/zap-templates/matter-idl-client.json | 4 +++ src/app/zap-templates/matter-idl-server.json | 4 +++ .../partials/idl/global_types.zapt | 30 +++++++++++++++++++ .../partials/idl/structure_definition.zapt | 7 +++++ .../templates/app/MatterIDL_Client.zapt | 1 + .../templates/app/MatterIDL_Server.zapt | 1 + .../data_model/controller-clusters.matter | 17 +++++++++++ 94 files changed, 1538 insertions(+), 12 deletions(-) create mode 100644 src/app/zap-templates/partials/idl/global_types.zapt diff --git a/examples/air-purifier-app/air-purifier-common/air-purifier-app.matter b/examples/air-purifier-app/air-purifier-common/air-purifier-app.matter index 4e9d31f7df3b8e..864e3115bc06a9 100644 --- a/examples/air-purifier-app/air-purifier-common/air-purifier-app.matter +++ b/examples/air-purifier-app/air-purifier-common/air-purifier-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/air-quality-sensor-app/air-quality-sensor-common/air-quality-sensor-app.matter b/examples/air-quality-sensor-app/air-quality-sensor-common/air-quality-sensor-app.matter index 1191c5f55c0c67..294d3a19cb5772 100644 --- a/examples/air-quality-sensor-app/air-quality-sensor-common/air-quality-sensor-app.matter +++ b/examples/air-quality-sensor-app/air-quality-sensor-common/air-quality-sensor-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter index 3144546a72b849..c4a159841dc100 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/all-clusters-minimal-app/all-clusters-common/all-clusters-minimal-app.matter b/examples/all-clusters-minimal-app/all-clusters-common/all-clusters-minimal-app.matter index 80fd60040a9142..946733b5ebe9da 100644 --- a/examples/all-clusters-minimal-app/all-clusters-common/all-clusters-minimal-app.matter +++ b/examples/all-clusters-minimal-app/all-clusters-common/all-clusters-minimal-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/bridge-app/bridge-common/bridge-app.matter b/examples/bridge-app/bridge-common/bridge-app.matter index 8f91a116cb0bb1..049e42d6f61e4f 100644 --- a/examples/bridge-app/bridge-common/bridge-app.matter +++ b/examples/bridge-app/bridge-common/bridge-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/noip_rootnode_dimmablelight_bCwGYSDpoe.matter b/examples/chef/devices/noip_rootnode_dimmablelight_bCwGYSDpoe.matter index 83413127c088db..a8de6fab823b82 100644 --- a/examples/chef/devices/noip_rootnode_dimmablelight_bCwGYSDpoe.matter +++ b/examples/chef/devices/noip_rootnode_dimmablelight_bCwGYSDpoe.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_airpurifier_73a6fe2651.matter b/examples/chef/devices/rootnode_airpurifier_73a6fe2651.matter index 7d9b2a51326aa1..f00c909ce0a882 100644 --- a/examples/chef/devices/rootnode_airpurifier_73a6fe2651.matter +++ b/examples/chef/devices/rootnode_airpurifier_73a6fe2651.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_airpurifier_airqualitysensor_temperaturesensor_humiditysensor_thermostat_56de3d5f45.matter b/examples/chef/devices/rootnode_airpurifier_airqualitysensor_temperaturesensor_humiditysensor_thermostat_56de3d5f45.matter index d56dff0afcf204..1976a0d6af169b 100644 --- a/examples/chef/devices/rootnode_airpurifier_airqualitysensor_temperaturesensor_humiditysensor_thermostat_56de3d5f45.matter +++ b/examples/chef/devices/rootnode_airpurifier_airqualitysensor_temperaturesensor_humiditysensor_thermostat_56de3d5f45.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_airqualitysensor_e63187f6c9.matter b/examples/chef/devices/rootnode_airqualitysensor_e63187f6c9.matter index bac48320116f79..a6d9cf022022f8 100644 --- a/examples/chef/devices/rootnode_airqualitysensor_e63187f6c9.matter +++ b/examples/chef/devices/rootnode_airqualitysensor_e63187f6c9.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_basicvideoplayer_0ff86e943b.matter b/examples/chef/devices/rootnode_basicvideoplayer_0ff86e943b.matter index 9621d615447e40..961eeaade9e1d6 100644 --- a/examples/chef/devices/rootnode_basicvideoplayer_0ff86e943b.matter +++ b/examples/chef/devices/rootnode_basicvideoplayer_0ff86e943b.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_colortemperaturelight_hbUnzYVeyn.matter b/examples/chef/devices/rootnode_colortemperaturelight_hbUnzYVeyn.matter index c5059bc7a07bad..88ff138f11e1f0 100644 --- a/examples/chef/devices/rootnode_colortemperaturelight_hbUnzYVeyn.matter +++ b/examples/chef/devices/rootnode_colortemperaturelight_hbUnzYVeyn.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_contactsensor_27f76aeaf5.matter b/examples/chef/devices/rootnode_contactsensor_27f76aeaf5.matter index 49df10bf2cd3aa..8e62555955a2e1 100644 --- a/examples/chef/devices/rootnode_contactsensor_27f76aeaf5.matter +++ b/examples/chef/devices/rootnode_contactsensor_27f76aeaf5.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_contactsensor_lFAGG1bfRO.matter b/examples/chef/devices/rootnode_contactsensor_lFAGG1bfRO.matter index 37d2d4554e7156..9f08c5a23aa601 100644 --- a/examples/chef/devices/rootnode_contactsensor_lFAGG1bfRO.matter +++ b/examples/chef/devices/rootnode_contactsensor_lFAGG1bfRO.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_dimmablelight_bCwGYSDpoe.matter b/examples/chef/devices/rootnode_dimmablelight_bCwGYSDpoe.matter index da3fb96aae5575..66c3432e2de60d 100644 --- a/examples/chef/devices/rootnode_dimmablelight_bCwGYSDpoe.matter +++ b/examples/chef/devices/rootnode_dimmablelight_bCwGYSDpoe.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_dimmablepluginunit_f8a9a0b9d4.matter b/examples/chef/devices/rootnode_dimmablepluginunit_f8a9a0b9d4.matter index 2cc78bab075179..d395b259d4a9d6 100644 --- a/examples/chef/devices/rootnode_dimmablepluginunit_f8a9a0b9d4.matter +++ b/examples/chef/devices/rootnode_dimmablepluginunit_f8a9a0b9d4.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_dishwasher_cc105034fe.matter b/examples/chef/devices/rootnode_dishwasher_cc105034fe.matter index e0587dbd481463..62da7396ee7c8f 100644 --- a/examples/chef/devices/rootnode_dishwasher_cc105034fe.matter +++ b/examples/chef/devices/rootnode_dishwasher_cc105034fe.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_doorlock_aNKYAreMXE.matter b/examples/chef/devices/rootnode_doorlock_aNKYAreMXE.matter index 3b4feb2216aa0e..d808e2d25f478b 100644 --- a/examples/chef/devices/rootnode_doorlock_aNKYAreMXE.matter +++ b/examples/chef/devices/rootnode_doorlock_aNKYAreMXE.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_extendedcolorlight_8lcaaYJVAa.matter b/examples/chef/devices/rootnode_extendedcolorlight_8lcaaYJVAa.matter index 48c250ea3fd4b5..19a62b4feb5e1c 100644 --- a/examples/chef/devices/rootnode_extendedcolorlight_8lcaaYJVAa.matter +++ b/examples/chef/devices/rootnode_extendedcolorlight_8lcaaYJVAa.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_fan_7N2TobIlOX.matter b/examples/chef/devices/rootnode_fan_7N2TobIlOX.matter index c98aa70af8f82e..3ff6ab9cd9c23c 100644 --- a/examples/chef/devices/rootnode_fan_7N2TobIlOX.matter +++ b/examples/chef/devices/rootnode_fan_7N2TobIlOX.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_flowsensor_1zVxHedlaV.matter b/examples/chef/devices/rootnode_flowsensor_1zVxHedlaV.matter index e46fa09b3111fb..73390f105c1b8f 100644 --- a/examples/chef/devices/rootnode_flowsensor_1zVxHedlaV.matter +++ b/examples/chef/devices/rootnode_flowsensor_1zVxHedlaV.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_genericswitch_2dfff6e516.matter b/examples/chef/devices/rootnode_genericswitch_2dfff6e516.matter index 1c58822f13aa71..81b9bf1c21cc17 100644 --- a/examples/chef/devices/rootnode_genericswitch_2dfff6e516.matter +++ b/examples/chef/devices/rootnode_genericswitch_2dfff6e516.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_genericswitch_9866e35d0b.matter b/examples/chef/devices/rootnode_genericswitch_9866e35d0b.matter index 58eb58d22deb9f..11f915e6c4f860 100644 --- a/examples/chef/devices/rootnode_genericswitch_9866e35d0b.matter +++ b/examples/chef/devices/rootnode_genericswitch_9866e35d0b.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_heatingcoolingunit_ncdGai1E5a.matter b/examples/chef/devices/rootnode_heatingcoolingunit_ncdGai1E5a.matter index 45bdce794448c0..b0011ffb37983b 100644 --- a/examples/chef/devices/rootnode_heatingcoolingunit_ncdGai1E5a.matter +++ b/examples/chef/devices/rootnode_heatingcoolingunit_ncdGai1E5a.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_humiditysensor_Xyj4gda6Hb.matter b/examples/chef/devices/rootnode_humiditysensor_Xyj4gda6Hb.matter index 23a9bc8a578a95..32ab67c21b8593 100644 --- a/examples/chef/devices/rootnode_humiditysensor_Xyj4gda6Hb.matter +++ b/examples/chef/devices/rootnode_humiditysensor_Xyj4gda6Hb.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_laundrywasher_fb10d238c8.matter b/examples/chef/devices/rootnode_laundrywasher_fb10d238c8.matter index 4b4762ce78bb46..34c4a8284e7851 100644 --- a/examples/chef/devices/rootnode_laundrywasher_fb10d238c8.matter +++ b/examples/chef/devices/rootnode_laundrywasher_fb10d238c8.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_lightsensor_lZQycTFcJK.matter b/examples/chef/devices/rootnode_lightsensor_lZQycTFcJK.matter index 7e744cafc5409c..cfa2faa440b95f 100644 --- a/examples/chef/devices/rootnode_lightsensor_lZQycTFcJK.matter +++ b/examples/chef/devices/rootnode_lightsensor_lZQycTFcJK.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_occupancysensor_iHyVgifZuo.matter b/examples/chef/devices/rootnode_occupancysensor_iHyVgifZuo.matter index 4406f572f99dfb..9425ccba0c313a 100644 --- a/examples/chef/devices/rootnode_occupancysensor_iHyVgifZuo.matter +++ b/examples/chef/devices/rootnode_occupancysensor_iHyVgifZuo.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_onofflight_bbs1b7IaOV.matter b/examples/chef/devices/rootnode_onofflight_bbs1b7IaOV.matter index b5a953cb331994..7360ede44aff51 100644 --- a/examples/chef/devices/rootnode_onofflight_bbs1b7IaOV.matter +++ b/examples/chef/devices/rootnode_onofflight_bbs1b7IaOV.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_onofflight_samplemei.matter b/examples/chef/devices/rootnode_onofflight_samplemei.matter index 201ad01c70d944..58811796ed547a 100644 --- a/examples/chef/devices/rootnode_onofflight_samplemei.matter +++ b/examples/chef/devices/rootnode_onofflight_samplemei.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_onofflightswitch_FsPlMr090Q.matter b/examples/chef/devices/rootnode_onofflightswitch_FsPlMr090Q.matter index bf818da2a37cb9..b02e20255349fe 100644 --- a/examples/chef/devices/rootnode_onofflightswitch_FsPlMr090Q.matter +++ b/examples/chef/devices/rootnode_onofflightswitch_FsPlMr090Q.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_onoffpluginunit_Wtf8ss5EBY.matter b/examples/chef/devices/rootnode_onoffpluginunit_Wtf8ss5EBY.matter index 37ab328bad6886..03716c4ce00425 100644 --- a/examples/chef/devices/rootnode_onoffpluginunit_Wtf8ss5EBY.matter +++ b/examples/chef/devices/rootnode_onoffpluginunit_Wtf8ss5EBY.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_pressuresensor_s0qC9wLH4k.matter b/examples/chef/devices/rootnode_pressuresensor_s0qC9wLH4k.matter index 83b7eba578d8b0..5c413241adced8 100644 --- a/examples/chef/devices/rootnode_pressuresensor_s0qC9wLH4k.matter +++ b/examples/chef/devices/rootnode_pressuresensor_s0qC9wLH4k.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_pump_5f904818cc.matter b/examples/chef/devices/rootnode_pump_5f904818cc.matter index 89a69529465401..9a30a686479625 100644 --- a/examples/chef/devices/rootnode_pump_5f904818cc.matter +++ b/examples/chef/devices/rootnode_pump_5f904818cc.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_pump_a811bb33a0.matter b/examples/chef/devices/rootnode_pump_a811bb33a0.matter index c748c1ed56cd84..10c5bd7aa2cd59 100644 --- a/examples/chef/devices/rootnode_pump_a811bb33a0.matter +++ b/examples/chef/devices/rootnode_pump_a811bb33a0.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_refrigerator_temperaturecontrolledcabinet_temperaturecontrolledcabinet_ffdb696680.matter b/examples/chef/devices/rootnode_refrigerator_temperaturecontrolledcabinet_temperaturecontrolledcabinet_ffdb696680.matter index b8664ec2e38a0b..2d046beded5e96 100644 --- a/examples/chef/devices/rootnode_refrigerator_temperaturecontrolledcabinet_temperaturecontrolledcabinet_ffdb696680.matter +++ b/examples/chef/devices/rootnode_refrigerator_temperaturecontrolledcabinet_temperaturecontrolledcabinet_ffdb696680.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_roboticvacuumcleaner_1807ff0c49.matter b/examples/chef/devices/rootnode_roboticvacuumcleaner_1807ff0c49.matter index eaf2ff0d0dd339..3285f99388b3c5 100644 --- a/examples/chef/devices/rootnode_roboticvacuumcleaner_1807ff0c49.matter +++ b/examples/chef/devices/rootnode_roboticvacuumcleaner_1807ff0c49.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_roomairconditioner_9cf3607804.matter b/examples/chef/devices/rootnode_roomairconditioner_9cf3607804.matter index 81770335f21b94..5a8aefa22762f6 100644 --- a/examples/chef/devices/rootnode_roomairconditioner_9cf3607804.matter +++ b/examples/chef/devices/rootnode_roomairconditioner_9cf3607804.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_smokecoalarm_686fe0dcb8.matter b/examples/chef/devices/rootnode_smokecoalarm_686fe0dcb8.matter index 60b79acd4c07b9..28669954fab360 100644 --- a/examples/chef/devices/rootnode_smokecoalarm_686fe0dcb8.matter +++ b/examples/chef/devices/rootnode_smokecoalarm_686fe0dcb8.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_speaker_RpzeXdimqA.matter b/examples/chef/devices/rootnode_speaker_RpzeXdimqA.matter index 8cbcd9852df88f..b6898778855ad6 100644 --- a/examples/chef/devices/rootnode_speaker_RpzeXdimqA.matter +++ b/examples/chef/devices/rootnode_speaker_RpzeXdimqA.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_temperaturesensor_Qy1zkNW7c3.matter b/examples/chef/devices/rootnode_temperaturesensor_Qy1zkNW7c3.matter index 51eb8ddd45a282..f91f03cc02ffad 100644 --- a/examples/chef/devices/rootnode_temperaturesensor_Qy1zkNW7c3.matter +++ b/examples/chef/devices/rootnode_temperaturesensor_Qy1zkNW7c3.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_thermostat_bm3fb8dhYi.matter b/examples/chef/devices/rootnode_thermostat_bm3fb8dhYi.matter index 6ae0a08d75721a..f7ff21337d4e96 100644 --- a/examples/chef/devices/rootnode_thermostat_bm3fb8dhYi.matter +++ b/examples/chef/devices/rootnode_thermostat_bm3fb8dhYi.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/chef/devices/rootnode_windowcovering_RLCxaGi9Yx.matter b/examples/chef/devices/rootnode_windowcovering_RLCxaGi9Yx.matter index f69d11d92bd19a..aaf2dca3c3fbb7 100644 --- a/examples/chef/devices/rootnode_windowcovering_RLCxaGi9Yx.matter +++ b/examples/chef/devices/rootnode_windowcovering_RLCxaGi9Yx.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/contact-sensor-app/contact-sensor-common/contact-sensor-app.matter b/examples/contact-sensor-app/contact-sensor-common/contact-sensor-app.matter index 03b8cdf10bc55c..5c2830b39540bb 100644 --- a/examples/contact-sensor-app/contact-sensor-common/contact-sensor-app.matter +++ b/examples/contact-sensor-app/contact-sensor-common/contact-sensor-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/contact-sensor-app/nxp/zap-lit/contact-sensor-app.matter b/examples/contact-sensor-app/nxp/zap-lit/contact-sensor-app.matter index dcfae7fdb2cbc5..2606c314ff1581 100644 --- a/examples/contact-sensor-app/nxp/zap-lit/contact-sensor-app.matter +++ b/examples/contact-sensor-app/nxp/zap-lit/contact-sensor-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/contact-sensor-app/nxp/zap-sit/contact-sensor-app.matter b/examples/contact-sensor-app/nxp/zap-sit/contact-sensor-app.matter index 0fb89660b37975..afb602e1abc18d 100644 --- a/examples/contact-sensor-app/nxp/zap-sit/contact-sensor-app.matter +++ b/examples/contact-sensor-app/nxp/zap-sit/contact-sensor-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/dishwasher-app/dishwasher-common/dishwasher-app.matter b/examples/dishwasher-app/dishwasher-common/dishwasher-app.matter index db68c2ddea702d..6ed8e87a7e75d1 100644 --- a/examples/dishwasher-app/dishwasher-common/dishwasher-app.matter +++ b/examples/dishwasher-app/dishwasher-common/dishwasher-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/energy-management-app/energy-management-common/energy-management-app.matter b/examples/energy-management-app/energy-management-common/energy-management-app.matter index 623c247391a6a1..eae8f9991fa967 100644 --- a/examples/energy-management-app/energy-management-common/energy-management-app.matter +++ b/examples/energy-management-app/energy-management-common/energy-management-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.matter b/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.matter index 46bd51d578e6ee..b0f11a4039aa18 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.matter +++ b/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/laundry-washer-app/nxp/zap/laundry-washer-app.matter b/examples/laundry-washer-app/nxp/zap/laundry-washer-app.matter index 727e4c3b097302..e8913aeb7e11b3 100644 --- a/examples/laundry-washer-app/nxp/zap/laundry-washer-app.matter +++ b/examples/laundry-washer-app/nxp/zap/laundry-washer-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/light-switch-app/light-switch-common/light-switch-app.matter b/examples/light-switch-app/light-switch-common/light-switch-app.matter index b437fbdb633d2e..65796ceebd4063 100644 --- a/examples/light-switch-app/light-switch-common/light-switch-app.matter +++ b/examples/light-switch-app/light-switch-common/light-switch-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/light-switch-app/qpg/zap/switch.matter b/examples/light-switch-app/qpg/zap/switch.matter index 73968414b217e9..58459bccdd7a50 100644 --- a/examples/light-switch-app/qpg/zap/switch.matter +++ b/examples/light-switch-app/qpg/zap/switch.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/lighting-app/bouffalolab/data_model/lighting-app-ethernet.matter b/examples/lighting-app/bouffalolab/data_model/lighting-app-ethernet.matter index f55a79c699b676..0197bf3889fdea 100644 --- a/examples/lighting-app/bouffalolab/data_model/lighting-app-ethernet.matter +++ b/examples/lighting-app/bouffalolab/data_model/lighting-app-ethernet.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/lighting-app/bouffalolab/data_model/lighting-app-thread.matter b/examples/lighting-app/bouffalolab/data_model/lighting-app-thread.matter index 76615a2e64fd88..d7b7fe74b541f5 100644 --- a/examples/lighting-app/bouffalolab/data_model/lighting-app-thread.matter +++ b/examples/lighting-app/bouffalolab/data_model/lighting-app-thread.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/lighting-app/bouffalolab/data_model/lighting-app-wifi.matter b/examples/lighting-app/bouffalolab/data_model/lighting-app-wifi.matter index 47736182e30ed2..3f36ce92b119da 100644 --- a/examples/lighting-app/bouffalolab/data_model/lighting-app-wifi.matter +++ b/examples/lighting-app/bouffalolab/data_model/lighting-app-wifi.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/lighting-app/lighting-common/lighting-app.matter b/examples/lighting-app/lighting-common/lighting-app.matter index 61fa92b32778cd..32c4eaa0dc80cb 100644 --- a/examples/lighting-app/lighting-common/lighting-app.matter +++ b/examples/lighting-app/lighting-common/lighting-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/lighting-app/nxp/zap/lighting-on-off.matter b/examples/lighting-app/nxp/zap/lighting-on-off.matter index 11b4298d8fad5e..da5e202dd6f39e 100644 --- a/examples/lighting-app/nxp/zap/lighting-on-off.matter +++ b/examples/lighting-app/nxp/zap/lighting-on-off.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/lighting-app/qpg/zap/light.matter b/examples/lighting-app/qpg/zap/light.matter index b2160c8fae6ddb..78c8e02efd12fe 100644 --- a/examples/lighting-app/qpg/zap/light.matter +++ b/examples/lighting-app/qpg/zap/light.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/lighting-app/silabs/data_model/lighting-thread-app.matter b/examples/lighting-app/silabs/data_model/lighting-thread-app.matter index 3ed9a2273a3436..0974c4e01cd111 100644 --- a/examples/lighting-app/silabs/data_model/lighting-thread-app.matter +++ b/examples/lighting-app/silabs/data_model/lighting-thread-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/lighting-app/silabs/data_model/lighting-wifi-app.matter b/examples/lighting-app/silabs/data_model/lighting-wifi-app.matter index e89bd41f1278b2..7f4ad51da871b3 100644 --- a/examples/lighting-app/silabs/data_model/lighting-wifi-app.matter +++ b/examples/lighting-app/silabs/data_model/lighting-wifi-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.matter b/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.matter index 4f8b1b55b51f42..e7ba31bf3bc30e 100644 --- a/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.matter +++ b/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/lock-app/lock-common/lock-app.matter b/examples/lock-app/lock-common/lock-app.matter index c42fb812cb8d41..a1eb98313bcfa9 100644 --- a/examples/lock-app/lock-common/lock-app.matter +++ b/examples/lock-app/lock-common/lock-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/lock-app/nxp/zap/lock-app.matter b/examples/lock-app/nxp/zap/lock-app.matter index cef586575e2bb3..047f200ffb6f4d 100644 --- a/examples/lock-app/nxp/zap/lock-app.matter +++ b/examples/lock-app/nxp/zap/lock-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/lock-app/qpg/zap/lock.matter b/examples/lock-app/qpg/zap/lock.matter index f8794650f14d17..4a067071261767 100644 --- a/examples/lock-app/qpg/zap/lock.matter +++ b/examples/lock-app/qpg/zap/lock.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/log-source-app/log-source-common/log-source-app.matter b/examples/log-source-app/log-source-common/log-source-app.matter index de444f53751547..b4ae99ffdb58bb 100644 --- a/examples/log-source-app/log-source-common/log-source-app.matter +++ b/examples/log-source-app/log-source-common/log-source-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** The Access Control Cluster exposes a data model view of a Node's Access Control List (ACL), which codifies the rules used to manage and enforce Access Control for the Node's endpoints and their associated diff --git a/examples/microwave-oven-app/microwave-oven-common/microwave-oven-app.matter b/examples/microwave-oven-app/microwave-oven-common/microwave-oven-app.matter index c50956a64841c2..3d03cf5fffcdca 100644 --- a/examples/microwave-oven-app/microwave-oven-common/microwave-oven-app.matter +++ b/examples/microwave-oven-app/microwave-oven-common/microwave-oven-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/network-manager-app/network-manager-common/network-manager-app.matter b/examples/network-manager-app/network-manager-common/network-manager-app.matter index 22fee8337e549e..dd37ffc02a635c 100644 --- a/examples/network-manager-app/network-manager-common/network-manager-app.matter +++ b/examples/network-manager-app/network-manager-common/network-manager-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/ota-provider-app/ota-provider-common/ota-provider-app.matter b/examples/ota-provider-app/ota-provider-common/ota-provider-app.matter index 17c6e8fb8dbea3..4f298e5d5ab176 100644 --- a/examples/ota-provider-app/ota-provider-common/ota-provider-app.matter +++ b/examples/ota-provider-app/ota-provider-common/ota-provider-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** The Descriptor Cluster is meant to replace the support from the Zigbee Device Object (ZDO) for describing a node, its endpoints and clusters. */ cluster Descriptor = 29 { revision 2; diff --git a/examples/ota-requestor-app/ota-requestor-common/ota-requestor-app.matter b/examples/ota-requestor-app/ota-requestor-common/ota-requestor-app.matter index a0ae180bbc1307..dfc59294f9c43a 100644 --- a/examples/ota-requestor-app/ota-requestor-common/ota-requestor-app.matter +++ b/examples/ota-requestor-app/ota-requestor-common/ota-requestor-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/placeholder/linux/apps/app1/config.matter b/examples/placeholder/linux/apps/app1/config.matter index 1955b363c01a4d..9c1a05687d3c6a 100644 --- a/examples/placeholder/linux/apps/app1/config.matter +++ b/examples/placeholder/linux/apps/app1/config.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/placeholder/linux/apps/app2/config.matter b/examples/placeholder/linux/apps/app2/config.matter index 8debe20800dc40..a48b8accff55c5 100644 --- a/examples/placeholder/linux/apps/app2/config.matter +++ b/examples/placeholder/linux/apps/app2/config.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/pump-app/pump-common/pump-app.matter b/examples/pump-app/pump-common/pump-app.matter index 242d64e29ddd95..8fe8106230ba4a 100644 --- a/examples/pump-app/pump-common/pump-app.matter +++ b/examples/pump-app/pump-common/pump-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/pump-app/silabs/data_model/pump-thread-app.matter b/examples/pump-app/silabs/data_model/pump-thread-app.matter index 907af129f460aa..5284799e2c4bdb 100644 --- a/examples/pump-app/silabs/data_model/pump-thread-app.matter +++ b/examples/pump-app/silabs/data_model/pump-thread-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/pump-app/silabs/data_model/pump-wifi-app.matter b/examples/pump-app/silabs/data_model/pump-wifi-app.matter index 907af129f460aa..5284799e2c4bdb 100644 --- a/examples/pump-app/silabs/data_model/pump-wifi-app.matter +++ b/examples/pump-app/silabs/data_model/pump-wifi-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/pump-controller-app/pump-controller-common/pump-controller-app.matter b/examples/pump-controller-app/pump-controller-common/pump-controller-app.matter index b23e832c0d7d88..b7a8759d59b2ba 100644 --- a/examples/pump-controller-app/pump-controller-common/pump-controller-app.matter +++ b/examples/pump-controller-app/pump-controller-common/pump-controller-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/refrigerator-app/refrigerator-common/refrigerator-app.matter b/examples/refrigerator-app/refrigerator-common/refrigerator-app.matter index 685893ec111f89..d79448d76f5c6e 100644 --- a/examples/refrigerator-app/refrigerator-common/refrigerator-app.matter +++ b/examples/refrigerator-app/refrigerator-common/refrigerator-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** The Descriptor Cluster is meant to replace the support from the Zigbee Device Object (ZDO) for describing a node, its endpoints and clusters. */ cluster Descriptor = 29 { revision 2; diff --git a/examples/rvc-app/rvc-common/rvc-app.matter b/examples/rvc-app/rvc-common/rvc-app.matter index 3c9ef5e4d817c1..3887f9c2abcbe2 100644 --- a/examples/rvc-app/rvc-common/rvc-app.matter +++ b/examples/rvc-app/rvc-common/rvc-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/smoke-co-alarm-app/smoke-co-alarm-common/smoke-co-alarm-app.matter b/examples/smoke-co-alarm-app/smoke-co-alarm-common/smoke-co-alarm-app.matter index 8e2f8a41993005..84c9d0e8eb2831 100644 --- a/examples/smoke-co-alarm-app/smoke-co-alarm-common/smoke-co-alarm-app.matter +++ b/examples/smoke-co-alarm-app/smoke-co-alarm-common/smoke-co-alarm-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/temperature-measurement-app/temperature-measurement-common/temperature-measurement.matter b/examples/temperature-measurement-app/temperature-measurement-common/temperature-measurement.matter index ea8faa4892d428..e34c00dfd2e2eb 100644 --- a/examples/temperature-measurement-app/temperature-measurement-common/temperature-measurement.matter +++ b/examples/temperature-measurement-app/temperature-measurement-common/temperature-measurement.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** The Descriptor Cluster is meant to replace the support from the Zigbee Device Object (ZDO) for describing a node, its endpoints and clusters. */ cluster Descriptor = 29 { revision 2; diff --git a/examples/thermostat/nxp/zap/thermostat_matter_thread.matter b/examples/thermostat/nxp/zap/thermostat_matter_thread.matter index 43cae6aee44148..00917f2d9a593e 100644 --- a/examples/thermostat/nxp/zap/thermostat_matter_thread.matter +++ b/examples/thermostat/nxp/zap/thermostat_matter_thread.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/thermostat/nxp/zap/thermostat_matter_wifi.matter b/examples/thermostat/nxp/zap/thermostat_matter_wifi.matter index 356347c1e7bf02..a8a65c2c4ad611 100644 --- a/examples/thermostat/nxp/zap/thermostat_matter_wifi.matter +++ b/examples/thermostat/nxp/zap/thermostat_matter_wifi.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/thermostat/qpg/zap/thermostaticRadiatorValve.matter b/examples/thermostat/qpg/zap/thermostaticRadiatorValve.matter index 7d90277b06091e..a775e536ac841e 100644 --- a/examples/thermostat/qpg/zap/thermostaticRadiatorValve.matter +++ b/examples/thermostat/qpg/zap/thermostaticRadiatorValve.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/thermostat/thermostat-common/thermostat.matter b/examples/thermostat/thermostat-common/thermostat.matter index 3246155268aca9..2312df8c61fdc1 100644 --- a/examples/thermostat/thermostat-common/thermostat.matter +++ b/examples/thermostat/thermostat-common/thermostat.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/tv-app/tv-common/tv-app.matter b/examples/tv-app/tv-common/tv-app.matter index 69c714a56bf7da..5f9fa9c0ab2b1a 100644 --- a/examples/tv-app/tv-common/tv-app.matter +++ b/examples/tv-app/tv-common/tv-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for switching devices between 'On' and 'Off' states. */ cluster OnOff = 6 { revision 6; diff --git a/examples/tv-casting-app/tv-casting-common/tv-casting-app.matter b/examples/tv-casting-app/tv-casting-common/tv-casting-app.matter index e7bbc1e6c2eee4..910beaa7eedf7b 100644 --- a/examples/tv-casting-app/tv-casting-common/tv-casting-app.matter +++ b/examples/tv-casting-app/tv-casting-common/tv-casting-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/virtual-device-app/virtual-device-common/virtual-device-app.matter b/examples/virtual-device-app/virtual-device-common/virtual-device-app.matter index 21e19e2640fbcb..95aa68635b91ed 100644 --- a/examples/virtual-device-app/virtual-device-common/virtual-device-app.matter +++ b/examples/virtual-device-app/virtual-device-common/virtual-device-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/examples/window-app/common/window-app.matter b/examples/window-app/common/window-app.matter index 8c272c7c17dacd..d0b9d9a24608a8 100644 --- a/examples/window-app/common/window-app.matter +++ b/examples/window-app/common/window-app.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; diff --git a/scripts/py_matter_idl/matter_idl/generators/idl/MatterIdl.jinja b/scripts/py_matter_idl/matter_idl/generators/idl/MatterIdl.jinja index 73be97dd3ae5cb..0e132e581b7712 100644 --- a/scripts/py_matter_idl/matter_idl/generators/idl/MatterIdl.jinja +++ b/scripts/py_matter_idl/matter_idl/generators/idl/MatterIdl.jinja @@ -18,15 +18,15 @@ {% macro render_struct(s) -%}{# Macro for the output of a complete struct #} - {%- if s.tag %}{{s.tag | idltxt}} {% endif -%} - {% if s.qualities %}{{s.qualities | idltxt}} {% endif -%} - struct {{s.name}} {##} - {%- if s.code is not none %}= {{s.code}} {% endif -%} - { - {% for field in s.fields %} - {{render_field(field)}} - {% endfor %} - } +{%- if s.tag %}{{s.tag | idltxt}} {% endif -%} +{% if s.qualities %}{{s.qualities | idltxt}} {% endif -%} +struct {{s.name}} {##} +{%- if s.code is not none %}= {{s.code}} {% endif -%} +{ + {% for field in s.fields %} + {{render_field(field)}} + {% endfor %} +} {%- endmacro -%} @@ -85,7 +85,7 @@ bitmap {{bitmap.name}} : {{ bitmap.base_type}} { {%- for s in cluster.structs | selectattr("is_global") %} /* GLOBAL: - {{render_struct(s)}} + {{render_struct(s) | indent(2)}} */ {% endfor %} @@ -109,7 +109,7 @@ bitmap {{bitmap.name}} : {{ bitmap.base_type}} { {% endfor %} {%- for s in cluster.structs | rejectattr("tag") | rejectattr("is_global") %} - {{render_struct(s)}} + {{render_struct(s) | indent(2)}} {% endfor %} @@ -129,7 +129,7 @@ bitmap {{bitmap.name}} : {{ bitmap.base_type}} { {%- for s in cluster.structs | selectattr("tag") %} - {{render_struct(s)}} + {{render_struct(s) | indent(2)}} {% endfor %} {%- for c in cluster.commands %} diff --git a/src/app/zap-templates/matter-idl-client.json b/src/app/zap-templates/matter-idl-client.json index 98bddf36ffaad1..8c918bc54260ca 100644 --- a/src/app/zap-templates/matter-idl-client.json +++ b/src/app/zap-templates/matter-idl-client.json @@ -38,6 +38,10 @@ { "name": "idl_cluster_definition", "path": "partials/idl/cluster_definition.zapt" + }, + { + "name": "idl_global_types", + "path": "partials/idl/global_types.zapt" } ], "templates": [ diff --git a/src/app/zap-templates/matter-idl-server.json b/src/app/zap-templates/matter-idl-server.json index 6b39f04826d6ee..047e4d6759e879 100644 --- a/src/app/zap-templates/matter-idl-server.json +++ b/src/app/zap-templates/matter-idl-server.json @@ -38,6 +38,10 @@ { "name": "idl_cluster_definition", "path": "partials/idl/cluster_definition.zapt" + }, + { + "name": "idl_global_types", + "path": "partials/idl/global_types.zapt" } ], "templates": [ diff --git a/src/app/zap-templates/partials/idl/global_types.zapt b/src/app/zap-templates/partials/idl/global_types.zapt new file mode 100644 index 00000000000000..bd72b43b2b36b5 --- /dev/null +++ b/src/app/zap-templates/partials/idl/global_types.zapt @@ -0,0 +1,30 @@ +{{#zcl_enums}} +{{#if has_no_clusters}} +enum {{asUpperCamelCase name preserveAcronyms=true}} : enum{{multiply size 8}} { + {{#zcl_enum_items}} + k{{asUpperCamelCase label preserveAcronyms=true}} = {{value}}; + {{/zcl_enum_items}} +} + +{{/if}} +{{/zcl_enums}} +{{#zcl_bitmaps}} +{{#if has_no_clusters}} +{{#if_is_atomic name}} +{{! Work around https://github.com/project-chip/zap/issues/1370 and manually filter out built-in bitmap types. }} +{{else}} +bitmap {{asUpperCamelCase name preserveAcronyms=true}} : bitmap{{multiply size 8}} { + {{#zcl_bitmap_items}} + k{{asUpperCamelCase label preserveAcronyms=true}} = {{asHex mask}}; + {{/zcl_bitmap_items}} +} + +{{/if_is_atomic}} +{{/if}} +{{/zcl_bitmaps}} +{{#zcl_structs}} +{{#if has_no_clusters}} +{{~>idl_structure_definition extraIndent=0}} + +{{/if}} +{{/zcl_structs}} diff --git a/src/app/zap-templates/partials/idl/structure_definition.zapt b/src/app/zap-templates/partials/idl/structure_definition.zapt index 453efe65d9cdbb..4045cdf11e98a8 100644 --- a/src/app/zap-templates/partials/idl/structure_definition.zapt +++ b/src/app/zap-templates/partials/idl/structure_definition.zapt @@ -1,7 +1,14 @@ {{indent extraIndent~}} {{#if isFabricScoped~}} fabric_scoped {{/if~}} struct {{name}} { +{{#if has_no_clusters}} +{{#zcl_struct_items}} + {{> idl_structure_member}} + +{{/zcl_struct_items}} +{{else}} {{#zcl_struct_items}} {{indent extraIndent~}} {{> idl_structure_member}} {{/zcl_struct_items}} +{{/if}} {{indent extraIndent~}} } diff --git a/src/app/zap-templates/templates/app/MatterIDL_Client.zapt b/src/app/zap-templates/templates/app/MatterIDL_Client.zapt index 143e065c1b459d..b3a93844e49a0e 100644 --- a/src/app/zap-templates/templates/app/MatterIDL_Client.zapt +++ b/src/app/zap-templates/templates/app/MatterIDL_Client.zapt @@ -1,6 +1,7 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +{{>idl_global_types}} {{#zcl_clusters~}} {{>idl_cluster_definition}} {{/zcl_clusters}} diff --git a/src/app/zap-templates/templates/app/MatterIDL_Server.zapt b/src/app/zap-templates/templates/app/MatterIDL_Server.zapt index 9531fc268a189a..ce0d2bdb5b1600 100644 --- a/src/app/zap-templates/templates/app/MatterIDL_Server.zapt +++ b/src/app/zap-templates/templates/app/MatterIDL_Server.zapt @@ -1,6 +1,7 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +{{>idl_global_types}} {{#all_user_clusters~}} {{>idl_cluster_definition}} {{/all_user_clusters}} diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter index 6831539e0c9def..dd93ed7fe2cc9b 100644 --- a/src/controller/data_model/controller-clusters.matter +++ b/src/controller/data_model/controller-clusters.matter @@ -1,6 +1,23 @@ // This IDL was generated automatically by ZAP. // It is for view/code review purposes only. +enum TestGlobalEnum : enum8 { + kSomeValue = 0; + kSomeOtherValue = 1; + kFinalValue = 2; +} + +bitmap TestGlobalBitmap : bitmap32 { + kFirstBit = 0x1; + kSecondBit = 0x2; +} + +struct TestGlobalStruct { + char_string<128> name = 0; + nullable TestGlobalBitmap myBitmap = 1; + optional nullable TestGlobalEnum myEnum = 2; +} + /** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ cluster Identify = 3 { revision 4; From 5eba3944e017cb3ffc0fa2186478081c5b5f9498 Mon Sep 17 00:00:00 2001 From: mkardous-silabs <84793247+mkardous-silabs@users.noreply.github.com> Date: Fri, 26 Jul 2024 19:42:45 -0400 Subject: [PATCH 45/49] [ICD] Implement the support of the ICD Check-In BackOff (#34482) * Add MaximunCheckInBackOff attribute to ICDM cluster * Add injectable ICD Check-In BackOff strategy * Fix gn dependency * Fix non CIP builds * Fix switch case and add test * Update zcl with extensions * Fix zap generation * Refactor ICDManager init to leverage a builder pattern * Fix non LIT test builds * Apply suggestions from code review Co-authored-by: Boris Zbarsky --------- Co-authored-by: Boris Zbarsky --- .../lit-icd-common/lit-icd-server-app.matter | 1 + .../lit-icd-common/lit-icd-server-app.zap | 30 ++++++--- .../icd-management-server.cpp | 9 +++ src/app/icd/server/BUILD.gn | 17 +++++ .../server/DefaultICDCheckInBackOffStrategy.h | 63 +++++++++++++++++++ .../icd/server/ICDCheckInBackOffStrategy.h | 62 ++++++++++++++++++ src/app/icd/server/ICDConfigurationData.h | 8 +++ src/app/icd/server/ICDManager.cpp | 49 +++++++-------- src/app/icd/server/ICDManager.h | 60 +++++++++++++++--- src/app/icd/server/tests/BUILD.gn | 2 + .../TestDefaultICDCheckInBackOffStrategy.cpp | 57 +++++++++++++++++ src/app/icd/server/tests/TestICDManager.cpp | 34 +++++++++- src/app/server/BUILD.gn | 6 ++ src/app/server/Server.cpp | 15 ++++- src/app/server/Server.h | 18 ++++++ .../zcl/zcl-with-test-extensions.json | 3 +- src/app/zap-templates/zcl/zcl.json | 3 +- src/lib/core/CHIPConfig.h | 9 +++ src/python_testing/TC_ICDManagementCluster.py | 10 +++ .../zap-generated/attributes/Accessors.cpp | 46 -------------- .../zap-generated/attributes/Accessors.h | 6 -- 21 files changed, 408 insertions(+), 100 deletions(-) create mode 100644 src/app/icd/server/DefaultICDCheckInBackOffStrategy.h create mode 100644 src/app/icd/server/ICDCheckInBackOffStrategy.h create mode 100644 src/app/icd/server/tests/TestDefaultICDCheckInBackOffStrategy.cpp diff --git a/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.matter b/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.matter index e7ba31bf3bc30e..7c6ef7908ad7d6 100644 --- a/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.matter +++ b/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.matter @@ -1813,6 +1813,7 @@ endpoint 0 { ram attribute userActiveModeTriggerHint default = 0x111D; ram attribute userActiveModeTriggerInstruction default = "Restart the application"; ram attribute operatingMode default = 0; + callback attribute maximumCheckInBackOff; callback attribute generatedCommandList; callback attribute acceptedCommandList; callback attribute eventList; diff --git a/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.zap b/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.zap index 2a75af9025f0d5..bc32dbce677fb2 100644 --- a/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.zap +++ b/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.zap @@ -17,13 +17,6 @@ } ], "package": [ - { - "pathRelativity": "relativeToZap", - "path": "../../../src/app/zap-templates/app-templates.json", - "type": "gen-templates-json", - "category": "matter", - "version": "chip-v1" - }, { "pathRelativity": "relativeToZap", "path": "../../../src/app/zap-templates/zcl/zcl.json", @@ -31,6 +24,13 @@ "category": "matter", "version": 1, "description": "Matter SDK ZCL data" + }, + { + "pathRelativity": "relativeToZap", + "path": "../../../src/app/zap-templates/app-templates.json", + "type": "gen-templates-json", + "category": "matter", + "version": "chip-v1" } ], "endpointTypes": [ @@ -3552,6 +3552,22 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "MaximumCheckInBackOff", + "code": 9, + "mfgCode": null, + "side": "server", + "type": "int32u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "GeneratedCommandList", "code": 65528, diff --git a/src/app/clusters/icd-management-server/icd-management-server.cpp b/src/app/clusters/icd-management-server/icd-management-server.cpp index 55f6b5129c0a41..e98cc5e31718b2 100644 --- a/src/app/clusters/icd-management-server/icd-management-server.cpp +++ b/src/app/clusters/icd-management-server/icd-management-server.cpp @@ -69,6 +69,7 @@ class IcdManagementAttributeAccess : public AttributeAccessInterface CHIP_ERROR ReadRegisteredClients(EndpointId endpoint, AttributeValueEncoder & encoder); CHIP_ERROR ReadICDCounter(EndpointId endpoint, AttributeValueEncoder & encoder); CHIP_ERROR ReadClientsSupportedPerFabric(EndpointId endpoint, AttributeValueEncoder & encoder); + CHIP_ERROR ReadMaximumCheckInBackOff(EndpointId endpoint, AttributeValueEncoder & encoder); PersistentStorageDelegate * mStorage = nullptr; Crypto::SymmetricKeystore * mSymmetricKeystore = nullptr; @@ -102,6 +103,9 @@ CHIP_ERROR IcdManagementAttributeAccess::Read(const ConcreteReadAttributePath & case IcdManagement::Attributes::ClientsSupportedPerFabric::Id: return ReadClientsSupportedPerFabric(aPath.mEndpointId, aEncoder); + + case IcdManagement::Attributes::MaximumCheckInBackOff::Id: + return ReadMaximumCheckInBackOff(aPath.mEndpointId, aEncoder); #endif // CHIP_CONFIG_ENABLE_ICD_CIP } @@ -221,6 +225,11 @@ CHIP_ERROR IcdManagementAttributeAccess::ReadClientsSupportedPerFabric(EndpointI return encoder.Encode(mICDConfigurationData->GetClientsSupportedPerFabric()); } +CHIP_ERROR IcdManagementAttributeAccess::ReadMaximumCheckInBackOff(EndpointId endpoint, AttributeValueEncoder & encoder) +{ + return encoder.Encode(mICDConfigurationData->GetMaximumCheckInBackoff().count()); +} + /** * @brief Function checks if the client has admin permissions to the cluster in the commandPath * diff --git a/src/app/icd/server/BUILD.gn b/src/app/icd/server/BUILD.gn index 89c39c203a7c16..f69c25015592af 100644 --- a/src/app/icd/server/BUILD.gn +++ b/src/app/icd/server/BUILD.gn @@ -66,6 +66,22 @@ source_set("notifier") { ] } +source_set("check-in-back-off") { + sources = [ "ICDCheckInBackOffStrategy.h" ] + + public_deps = [ + ":monitoring-table", + "${chip_root}/src/lib/core", + "${chip_root}/src/lib/support", + ] +} + +source_set("default-check-in-back-off") { + sources = [ "DefaultICDCheckInBackOffStrategy.h" ] + + public_deps = [ ":check-in-back-off" ] +} + # ICD Manager source-set is broken out of the main source-set to enable unit tests # All sources and configurations used by the ICDManager need to go in this source-set source_set("manager") { @@ -77,6 +93,7 @@ source_set("manager") { deps = [ ":icd-server-config" ] public_deps = [ + ":check-in-back-off", ":configuration-data", ":notifier", ":observer", diff --git a/src/app/icd/server/DefaultICDCheckInBackOffStrategy.h b/src/app/icd/server/DefaultICDCheckInBackOffStrategy.h new file mode 100644 index 00000000000000..cdf29f0f2d9f9e --- /dev/null +++ b/src/app/icd/server/DefaultICDCheckInBackOffStrategy.h @@ -0,0 +1,63 @@ +/* + * + * 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 + +namespace chip { +namespace app { + +/** + * @brief Default ICD Check-In BackOff Strategy. + * The default strategy is based on the two types of controllers + * - kPermanent : Always send a Check-In message + * - kEphemeral : Never send a Check-In message + * + * This implementation represents a no back off strategy. + */ +class DefaultICDCheckInBackOffStrategy : public ICDCheckInBackOffStrategy +{ +public: + DefaultICDCheckInBackOffStrategy() = default; + ~DefaultICDCheckInBackOffStrategy() = default; + + /** + * @brief Function checks if the entry is a permanent or ephemeral client. + * If the client is permanent, we should send a Check-In message. + * If the client is ephemeral, we should not send a Check-In message. + * + * @param entry Entry for which we are deciding whether we need to send a Check-In message or not. + * @return true If the client is permanent, return true. + * @return false If the client is not permanent, ephemeral or invalid, return false. + */ + bool ShouldSendCheckInMessage(const ICDMonitoringEntry & entry) override + { + return (entry.clientType == Clusters::IcdManagement::ClientTypeEnum::kPermanent); + } + + /** + * @brief The default Check-In BackOff fundamentally implements a no back off strategy. + * As such, we don't need to execute anything to force the maximum Check-In BackOff. + * + */ + CHIP_ERROR ForceMaximumCheckInBackoff() override { return CHIP_NO_ERROR; } +}; + +} // namespace app +} // namespace chip diff --git a/src/app/icd/server/ICDCheckInBackOffStrategy.h b/src/app/icd/server/ICDCheckInBackOffStrategy.h new file mode 100644 index 00000000000000..e0a5a317cadf5d --- /dev/null +++ b/src/app/icd/server/ICDCheckInBackOffStrategy.h @@ -0,0 +1,62 @@ +/* + * + * 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 + +namespace chip { +namespace app { + +/** + * @brief This class defines the necessary interface a ICD Check-In BackOff strategy needs to implment to be consummed by the + * ICDManager class. The strategy is injected with the init server params when initializing the device Server class. + */ +class ICDCheckInBackOffStrategy +{ +public: + virtual ~ICDCheckInBackOffStrategy() = default; + + /** + * @brief Function is used by the ICDManager to determine if a Check-In message should be sent to the given entry based on the + * Check-In BackOff strategy. + * + * There are no requirements on how the Check-In BackOff strategy should behave. + * The only specified requirement is the maximum time between to Check-In message, MaximumCheckInBackOff. + * All strategies must respect this requirement. + * + * @param entry ICDMonitoringEntry for which we are about to send a Check-In message to. + * + * @return true ICDCheckInBackOffStrategy determines that we SHOULD send a Check-In message to the given entry + * @return false ICDCheckInBackOffStrategy determines that we SHOULD NOT send a Check-In message to the given entry + */ + virtual bool ShouldSendCheckInMessage(const ICDMonitoringEntry & entry) = 0; + + /** + * @brief Function is used within the test event trigger to force the maximum BackOff state of the ICD Check-In BackOff + * strategy. This enables to validate the strategy and to certify it respects the MaximumCheckInBackOff interval during + * certification. + * + * Function sets the maxmimum BackOff state for all clients registered with the ICD + * + * @return CHIP_ERROR Any error returned during the forcing of the maximum BackOff state + */ + virtual CHIP_ERROR ForceMaximumCheckInBackoff() = 0; +}; + +} // namespace app +} // namespace chip diff --git a/src/app/icd/server/ICDConfigurationData.h b/src/app/icd/server/ICDConfigurationData.h index 9358f37fd9ecc6..937b08b99e0e45 100644 --- a/src/app/icd/server/ICDConfigurationData.h +++ b/src/app/icd/server/ICDConfigurationData.h @@ -74,6 +74,8 @@ class ICDConfigurationData System::Clock::Milliseconds16 GetMinLitActiveModeThreshold() { return kMinLitActiveModeThreshold; } + System::Clock::Seconds32 GetMaximumCheckInBackoff() { return mMaximumCheckInBackOff; } + /** * If ICD_ENFORCE_SIT_SLOW_POLL_LIMIT is set to 0, function will always return the configured Slow Polling interval * (CHIP_DEVICE_CONFIG_ICD_SLOW_POLL_INTERVAL). @@ -150,6 +152,12 @@ class ICDConfigurationData "Spec requires the minimum of supported clients per fabric be equal or greater to 1."); uint16_t mFabricClientsSupported = CHIP_CONFIG_ICD_CLIENTS_SUPPORTED_PER_FABRIC; + static_assert((CHIP_CONFIG_ICD_MAXIMUM_CHECK_IN_BACKOFF_SEC) <= kMaxIdleModeDuration.count(), + "Spec requires the MaximumCheckInBackOff to be equal or inferior to 64800s"); + static_assert((CHIP_CONFIG_ICD_IDLE_MODE_DURATION_SEC) <= (CHIP_CONFIG_ICD_MAXIMUM_CHECK_IN_BACKOFF_SEC), + "Spec requires the MaximumCheckInBackOff to be equal or superior to the IdleModeDuration"); + System::Clock::Seconds32 mMaximumCheckInBackOff = System::Clock::Seconds32(CHIP_CONFIG_ICD_MAXIMUM_CHECK_IN_BACKOFF_SEC); + // SIT ICDs should have a SlowPollingThreshold shorter than or equal to 15s (spec 9.16.1.5) static constexpr System::Clock::Milliseconds32 kSITPollingThreshold = System::Clock::Milliseconds32(15000); System::Clock::Milliseconds32 mSlowPollingInterval = CHIP_DEVICE_CONFIG_ICD_SLOW_POLL_INTERVAL; diff --git a/src/app/icd/server/ICDManager.cpp b/src/app/icd/server/ICDManager.cpp index ee55b0b0f9b23b..2ba08990aef4b6 100644 --- a/src/app/icd/server/ICDManager.cpp +++ b/src/app/icd/server/ICDManager.cpp @@ -31,10 +31,11 @@ namespace { enum class ICDTestEventTriggerEvent : uint64_t { - kAddActiveModeReq = 0x0046'0000'00000001, - kRemoveActiveModeReq = 0x0046'0000'00000002, - kInvalidateHalfCounterValues = 0x0046'0000'00000003, - kInvalidateAllCounterValues = 0x0046'0000'00000004, + kAddActiveModeReq = 0x0046'0000'00000001, + kRemoveActiveModeReq = 0x0046'0000'00000002, + kInvalidateHalfCounterValues = 0x0046'0000'00000003, + kInvalidateAllCounterValues = 0x0046'0000'00000004, + kForceMaximumCheckInBackOffState = 0x0046'0000'00000005, }; } // namespace @@ -51,15 +52,19 @@ using chip::Protocols::InteractionModel::Status; static_assert(UINT8_MAX >= CHIP_CONFIG_MAX_EXCHANGE_CONTEXTS, "ICDManager::mOpenExchangeContextCount cannot hold count for the max exchange count"); -void ICDManager::Init(PersistentStorageDelegate * storage, FabricTable * fabricTable, Crypto::SymmetricKeystore * symmetricKeystore, - Messaging::ExchangeManager * exchangeManager, SubscriptionsInfoProvider * subInfoProvider) +void ICDManager::Init() { #if CHIP_CONFIG_ENABLE_ICD_CIP - VerifyOrDie(storage != nullptr); - VerifyOrDie(fabricTable != nullptr); - VerifyOrDie(symmetricKeystore != nullptr); - VerifyOrDie(exchangeManager != nullptr); - VerifyOrDie(subInfoProvider != nullptr); + VerifyOrDie(mStorage != nullptr); + VerifyOrDie(mFabricTable != nullptr); + VerifyOrDie(mSymmetricKeystore != nullptr); + VerifyOrDie(mExchangeManager != nullptr); + VerifyOrDie(mSubInfoProvider != nullptr); + VerifyOrDie(mICDCheckInBackOffStrategy != nullptr); + + VerifyOrDie(ICDConfigurationData::GetInstance().GetICDCounter().Init(mStorage, DefaultStorageKeyAllocator::ICDCheckInCounter(), + ICDConfigurationData::kICDCounterPersistenceIncrement) == + CHIP_NO_ERROR); #endif // CHIP_CONFIG_ENABLE_ICD_CIP #if CHIP_CONFIG_ENABLE_ICD_LIT @@ -81,18 +86,6 @@ void ICDManager::Init(PersistentStorageDelegate * storage, FabricTable * fabricT VerifyOrDie(ICDNotifier::GetInstance().Subscribe(this) == CHIP_NO_ERROR); -#if CHIP_CONFIG_ENABLE_ICD_CIP - mStorage = storage; - mFabricTable = fabricTable; - mSymmetricKeystore = symmetricKeystore; - mExchangeManager = exchangeManager; - mSubInfoProvider = subInfoProvider; - - VerifyOrDie(ICDConfigurationData::GetInstance().GetICDCounter().Init(mStorage, DefaultStorageKeyAllocator::ICDCheckInCounter(), - ICDConfigurationData::kICDCounterPersistenceIncrement) == - CHIP_NO_ERROR); -#endif // CHIP_CONFIG_ENABLE_ICD_CIP - UpdateICDMode(); UpdateOperationState(OperationalState::IdleMode); } @@ -188,15 +181,14 @@ void ICDManager::SendCheckInMsgs() continue; } - if (entry.clientType == ClientTypeEnum::kEphemeral) + if (!ShouldCheckInMsgsBeSentAtActiveModeFunction(entry.fabricIndex, entry.monitoredSubject)) { - // If the registered client is ephemeral, do not send a Check-In message - // continue to next entry continue; } - if (!ShouldCheckInMsgsBeSentAtActiveModeFunction(entry.fabricIndex, entry.monitoredSubject)) + if (!mICDCheckInBackOffStrategy->ShouldSendCheckInMessage(entry)) { + // continue to next entry continue; } @@ -689,6 +681,9 @@ CHIP_ERROR ICDManager::HandleEventTrigger(uint64_t eventTrigger) case ICDTestEventTriggerEvent::kInvalidateAllCounterValues: err = ICDConfigurationData::GetInstance().GetICDCounter().InvalidateAllCheckInCounterValues(); break; + case ICDTestEventTriggerEvent::kForceMaximumCheckInBackOffState: + err = mICDCheckInBackOffStrategy->ForceMaximumCheckInBackoff(); + break; #endif // CHIP_CONFIG_ENABLE_ICD_CIP default: err = CHIP_ERROR_INVALID_ARGUMENT; diff --git a/src/app/icd/server/ICDManager.h b/src/app/icd/server/ICDManager.h index 4b996e6dba5258..4ec1dfe65231d3 100644 --- a/src/app/icd/server/ICDManager.h +++ b/src/app/icd/server/ICDManager.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -114,8 +115,52 @@ class ICDManager : public ICDListener, public TestEventTriggerHandler ICDManager() = default; ~ICDManager() = default; - void Init(PersistentStorageDelegate * storage, FabricTable * fabricTable, Crypto::SymmetricKeystore * symmetricKeyStore, - Messaging::ExchangeManager * exchangeManager, SubscriptionsInfoProvider * subInfoProvider); + /* + Builder function to set all necessary members for the ICDManager class + */ + +#if CHIP_CONFIG_ENABLE_ICD_CIP + ICDManager & SetPersistentStorageDelegate(PersistentStorageDelegate * storage) + { + mStorage = storage; + return *this; + }; + + ICDManager & SetFabricTable(FabricTable * fabricTable) + { + mFabricTable = fabricTable; + return *this; + }; + + ICDManager & SetSymmetricKeyStore(Crypto::SymmetricKeystore * symmetricKeystore) + { + mSymmetricKeystore = symmetricKeystore; + return *this; + }; + + ICDManager & SetExchangeManager(Messaging::ExchangeManager * exchangeManager) + { + mExchangeManager = exchangeManager; + return *this; + }; + + ICDManager & SetSubscriptionsInfoProvider(SubscriptionsInfoProvider * subInfoProvider) + { + mSubInfoProvider = subInfoProvider; + return *this; + }; + + ICDManager & SetICDCheckInBackOffStrategy(ICDCheckInBackOffStrategy * strategy) + { + mICDCheckInBackOffStrategy = strategy; + return *this; + }; +#endif // CHIP_CONFIG_ENABLE_ICD_CIP + + /** + * @brief Validates that the ICDManager has all the necessary members to function and initializes the class + */ + void Init(); void Shutdown(); /** @@ -318,11 +363,12 @@ class ICDManager : public ICDListener, public TestEventTriggerHandler bool mIsBootUpResumeSubscriptionExecuted = false; #endif // !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS - PersistentStorageDelegate * mStorage = nullptr; - FabricTable * mFabricTable = nullptr; - Messaging::ExchangeManager * mExchangeManager = nullptr; - Crypto::SymmetricKeystore * mSymmetricKeystore = nullptr; - SubscriptionsInfoProvider * mSubInfoProvider = nullptr; + PersistentStorageDelegate * mStorage = nullptr; + FabricTable * mFabricTable = nullptr; + Messaging::ExchangeManager * mExchangeManager = nullptr; + Crypto::SymmetricKeystore * mSymmetricKeystore = nullptr; + SubscriptionsInfoProvider * mSubInfoProvider = nullptr; + ICDCheckInBackOffStrategy * mICDCheckInBackOffStrategy = nullptr; ObjectPool mICDSenderPool; #endif // CHIP_CONFIG_ENABLE_ICD_CIP diff --git a/src/app/icd/server/tests/BUILD.gn b/src/app/icd/server/tests/BUILD.gn index 95ece1d28ae4df..9b08c9e17893ca 100644 --- a/src/app/icd/server/tests/BUILD.gn +++ b/src/app/icd/server/tests/BUILD.gn @@ -22,6 +22,7 @@ chip_test_suite("tests") { output_name = "libICDServerTests" test_sources = [ + "TestDefaultICDCheckInBackOffStrategy.cpp", "TestICDManager.cpp", "TestICDMonitoringTable.cpp", ] @@ -29,6 +30,7 @@ chip_test_suite("tests") { sources = [ "ICDConfigurationDataTestAccess.h" ] public_deps = [ + "${chip_root}/src/app/icd/server:default-check-in-back-off", "${chip_root}/src/app/icd/server:manager", "${chip_root}/src/app/icd/server:monitoring-table", "${chip_root}/src/lib/core:string-builder-adapters", diff --git a/src/app/icd/server/tests/TestDefaultICDCheckInBackOffStrategy.cpp b/src/app/icd/server/tests/TestDefaultICDCheckInBackOffStrategy.cpp new file mode 100644 index 00000000000000..b873379826d468 --- /dev/null +++ b/src/app/icd/server/tests/TestDefaultICDCheckInBackOffStrategy.cpp @@ -0,0 +1,57 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters::IcdManagement; + +using TestSessionKeystoreImpl = Crypto::DefaultSessionKeystore; + +namespace { + +TEST(TestDefaultICDCheckInBackOffStrategy, TestShouldSendCheckInMessagePermanentClient) +{ + TestSessionKeystoreImpl keystore; + ICDMonitoringEntry entry(&keystore); + + entry.clientType = ClientTypeEnum::kPermanent; + + DefaultICDCheckInBackOffStrategy strategy; + EXPECT_TRUE(strategy.ShouldSendCheckInMessage(entry)); +} + +TEST(TestDefaultICDCheckInBackOffStrategy, TestShouldSendCheckInMessageEphemeralClient) +{ + TestSessionKeystoreImpl keystore; + ICDMonitoringEntry entry(&keystore); + + entry.clientType = ClientTypeEnum::kEphemeral; + + DefaultICDCheckInBackOffStrategy strategy; + EXPECT_FALSE(strategy.ShouldSendCheckInMessage(entry)); +} + +} // namespace diff --git a/src/app/icd/server/tests/TestICDManager.cpp b/src/app/icd/server/tests/TestICDManager.cpp index 3672955bdf4b26..df5c2e4970c579 100644 --- a/src/app/icd/server/tests/TestICDManager.cpp +++ b/src/app/icd/server/tests/TestICDManager.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -197,7 +198,16 @@ class TestICDManager : public Test::LoopbackMessagingContext mICDStateObserver.ResetAll(); mICDManager.RegisterObserver(&mICDStateObserver); - mICDManager.Init(&testStorage, &GetFabricTable(), &mKeystore, &GetExchangeManager(), &mSubInfoProvider); + +#if CHIP_CONFIG_ENABLE_ICD_CIP + mICDManager.SetPersistentStorageDelegate(&testStorage) + .SetFabricTable(&GetFabricTable()) + .SetSymmetricKeyStore(&mKeystore) + .SetExchangeManager(&GetExchangeManager()) + .SetSubscriptionsInfoProvider(&mSubInfoProvider) + .SetICDCheckInBackOffStrategy(&mStrategy); +#endif // CHIP_CONFIG_ENABLE_ICD_CIP + mICDManager.Init(); } // Performs teardown for each individual test in the test suite @@ -212,6 +222,7 @@ class TestICDManager : public Test::LoopbackMessagingContext TestSubscriptionsInfoProvider mSubInfoProvider; TestPersistentStorageDelegate testStorage; TestICDStateObserver mICDStateObserver; + DefaultICDCheckInBackOffStrategy mStrategy; }; TEST_F(TestICDManager, TestICDModeDurations) @@ -568,7 +579,16 @@ TEST_F(TestICDManager, TestICDCounter) // Shut down and reinit ICDManager to increment counter mICDManager.Shutdown(); - mICDManager.Init(&(testStorage), &GetFabricTable(), &(mKeystore), &GetExchangeManager(), &(mSubInfoProvider)); +#if CHIP_CONFIG_ENABLE_ICD_CIP + mICDManager.SetPersistentStorageDelegate(&testStorage) + .SetFabricTable(&GetFabricTable()) + .SetSymmetricKeyStore(&mKeystore) + .SetExchangeManager(&GetExchangeManager()) + .SetSubscriptionsInfoProvider(&mSubInfoProvider) + .SetICDCheckInBackOffStrategy(&mStrategy); +#endif // CHIP_CONFIG_ENABLE_ICD_CIP + mICDManager.Init(); + mICDManager.RegisterObserver(&(mICDStateObserver)); EXPECT_EQ(counter + ICDConfigurationData::kICDCounterPersistenceIncrement, @@ -973,7 +993,15 @@ TEST_F(TestICDManager, TestICDStateObserverOnICDModeChangeOnInit) // Shut down and reinit ICDManager - We should go to LIT mode since we have a registration mICDManager.Shutdown(); mICDManager.RegisterObserver(&(mICDStateObserver)); - mICDManager.Init(&testStorage, &GetFabricTable(), &mKeystore, &GetExchangeManager(), &mSubInfoProvider); +#if CHIP_CONFIG_ENABLE_ICD_CIP + mICDManager.SetPersistentStorageDelegate(&testStorage) + .SetFabricTable(&GetFabricTable()) + .SetSymmetricKeyStore(&mKeystore) + .SetExchangeManager(&GetExchangeManager()) + .SetSubscriptionsInfoProvider(&mSubInfoProvider) + .SetICDCheckInBackOffStrategy(&mStrategy); +#endif // CHIP_CONFIG_ENABLE_ICD_CIP + mICDManager.Init(); // We have a registration, transition to LIT mode EXPECT_TRUE(mICDStateObserver.mOnICDModeChangeCalled); diff --git a/src/app/server/BUILD.gn b/src/app/server/BUILD.gn index 51a259c86d2552..401356d7b753a4 100644 --- a/src/app/server/BUILD.gn +++ b/src/app/server/BUILD.gn @@ -53,6 +53,7 @@ static_library("server") { public_deps = [ "${chip_root}/src/app", "${chip_root}/src/app:test-event-trigger", + "${chip_root}/src/app/icd/server:check-in-back-off", "${chip_root}/src/app/icd/server:icd-server-config", "${chip_root}/src/app/icd/server:observer", "${chip_root}/src/lib/address_resolve", @@ -72,5 +73,10 @@ static_library("server") { if (chip_enable_icd_server) { public_deps += [ "${chip_root}/src/app/icd/server:notifier" ] + + if (chip_enable_icd_checkin) { + public_deps += + [ "${chip_root}/src/app/icd/server:default-check-in-back-off" ] + } } } diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp index 5be815f0864c95..33f657f7ec1030 100644 --- a/src/app/server/Server.cpp +++ b/src/app/server/Server.cpp @@ -359,8 +359,16 @@ CHIP_ERROR Server::Init(const ServerInitParams & initParams) mICDManager.RegisterObserver(mReportScheduler); mICDManager.RegisterObserver(&app::DnssdServer::Instance()); - mICDManager.Init(mDeviceStorage, &GetFabricTable(), mSessionKeystore, &mExchangeMgr, - chip::app::InteractionModelEngine::GetInstance()); +#if CHIP_CONFIG_ENABLE_ICD_CIP + mICDManager.SetPersistentStorageDelegate(mDeviceStorage) + .SetFabricTable(&GetFabricTable()) + .SetSymmetricKeyStore(mSessionKeystore) + .SetExchangeManager(&mExchangeMgr) + .SetSubscriptionsInfoProvider(chip::app::InteractionModelEngine::GetInstance()) + .SetICDCheckInBackOffStrategy(initParams.icdCheckInBackOffStrategy); + +#endif // CHIP_CONFIG_ENABLE_ICD_CIP + mICDManager.Init(); // Register Test Event Trigger Handler if (mTestEventTriggerDelegate != nullptr) @@ -769,5 +777,8 @@ app::SimpleSubscriptionResumptionStorage CommonCaseDeviceServerInitParams::sSubs #endif app::DefaultAclStorage CommonCaseDeviceServerInitParams::sAclStorage; Crypto::DefaultSessionKeystore CommonCaseDeviceServerInitParams::sSessionKeystore; +#if CHIP_CONFIG_ENABLE_ICD_CIP +app::DefaultICDCheckInBackOffStrategy CommonCaseDeviceServerInitParams::sDefaultICDCheckInBackOffStrategy; +#endif } // namespace chip diff --git a/src/app/server/Server.h b/src/app/server/Server.h index d649e0fc923896..9e0216877fea61 100644 --- a/src/app/server/Server.h +++ b/src/app/server/Server.h @@ -69,8 +69,13 @@ #include #include +#include #if CHIP_CONFIG_ENABLE_ICD_SERVER #include // nogncheck + +#if CHIP_CONFIG_ENABLE_ICD_CIP +#include // nogncheck +#endif #endif namespace chip { @@ -164,6 +169,9 @@ struct ServerInitParams Credentials::OperationalCertificateStore * opCertStore = nullptr; // Required, if not provided, the Server::Init() WILL fail. app::reporting::ReportScheduler * reportScheduler = nullptr; + // Optional. Support for the ICD Check-In BackOff strategy. Must be initialized before being provided. + // If the ICD Check-In protocol use-case is supported and no strategy is provided, server will use the default strategy. + app::ICDCheckInBackOffStrategy * icdCheckInBackOffStrategy = nullptr; }; /** @@ -278,6 +286,13 @@ struct CommonCaseDeviceServerInitParams : public ServerInitParams ChipLogProgress(AppServer, "Subscription persistence not supported"); #endif +#if CHIP_CONFIG_ENABLE_ICD_CIP + if (this->icdCheckInBackOffStrategy == nullptr) + { + this->icdCheckInBackOffStrategy = &sDefaultICDCheckInBackOffStrategy; + } +#endif + return CHIP_NO_ERROR; } @@ -297,6 +312,9 @@ struct CommonCaseDeviceServerInitParams : public ServerInitParams #endif static app::DefaultAclStorage sAclStorage; static Crypto::DefaultSessionKeystore sSessionKeystore; +#if CHIP_CONFIG_ENABLE_ICD_CIP + static app::DefaultICDCheckInBackOffStrategy sDefaultICDCheckInBackOffStrategy; +#endif }; /** diff --git a/src/app/zap-templates/zcl/zcl-with-test-extensions.json b/src/app/zap-templates/zcl/zcl-with-test-extensions.json index b06fc9131f3eee..6e74457062e946 100644 --- a/src/app/zap-templates/zcl/zcl-with-test-extensions.json +++ b/src/app/zap-templates/zcl/zcl-with-test-extensions.json @@ -279,7 +279,8 @@ "ActiveModeThreshold", "RegisteredClients", "ICDCounter", - "ClientsSupportedPerFabric" + "ClientsSupportedPerFabric", + "MaximumCheckInBackOff" ], "Occupancy Sensing": ["HoldTimeLimits"], "Operational Credentials": [ diff --git a/src/app/zap-templates/zcl/zcl.json b/src/app/zap-templates/zcl/zcl.json index 921b2022f40f8a..9e6efec76b686d 100644 --- a/src/app/zap-templates/zcl/zcl.json +++ b/src/app/zap-templates/zcl/zcl.json @@ -277,7 +277,8 @@ "ActiveModeThreshold", "RegisteredClients", "ICDCounter", - "ClientsSupportedPerFabric" + "ClientsSupportedPerFabric", + "MaximumCheckInBackOff" ], "Occupancy Sensing": ["HoldTimeLimits"], "Operational Credentials": [ diff --git a/src/lib/core/CHIPConfig.h b/src/lib/core/CHIPConfig.h index 32e49ab2e3d45f..e9c317dfc79d5c 100644 --- a/src/lib/core/CHIPConfig.h +++ b/src/lib/core/CHIPConfig.h @@ -1624,6 +1624,15 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; #define CHIP_CONFIG_ICD_CLIENTS_SUPPORTED_PER_FABRIC 2 #endif +/** + * @def CHIP_CONFIG_ICD_MAXIMUM_CHECK_IN_BACKOFF + * + * @brief Default value for the ICD Management cluster MaximumCheckInBackoff attribute, in seconds + */ +#ifndef CHIP_CONFIG_ICD_MAXIMUM_CHECK_IN_BACKOFF_SEC +#define CHIP_CONFIG_ICD_MAXIMUM_CHECK_IN_BACKOFF_SEC CHIP_CONFIG_ICD_IDLE_MODE_DURATION_SEC +#endif + /** * @name Configuation for resuming subscriptions that timed out * diff --git a/src/python_testing/TC_ICDManagementCluster.py b/src/python_testing/TC_ICDManagementCluster.py index 6030830cacded2..9f54e9b7dc227a 100644 --- a/src/python_testing/TC_ICDManagementCluster.py +++ b/src/python_testing/TC_ICDManagementCluster.py @@ -46,6 +46,7 @@ class ICDTestEventTriggerOperations(IntEnum): kRemoveActiveModeReq = 0x0046000000000002 kInvalidateHalfCounterValues = 0x0046000000000003 kInvalidateAllCounterValues = 0x0046000000000004 + kForceMaximumCheckInBackOffState = 0x0046000000000005 class TestICDManagementCluster(MatterBaseTest): @@ -113,6 +114,15 @@ async def test_active_mode_test_event_trigger(self): ) ) + asserts.assert_is_none( + await dev_ctrl.SendCommand( + self.dut_node_id, + endpoint=kRootEndpointId, + payload=Clusters.GeneralDiagnostics.Commands.TestEventTrigger(enableKey=kTestEventTriggerKey, + eventTrigger=ICDTestEventTriggerOperations.kForceMaximumCheckInBackOffState) + ) + ) + if __name__ == "__main__": default_matter_test_main() diff --git a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp index 4f4d62a3e67bd6..d68e2fc7ddaffc 100644 --- a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp +++ b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp @@ -9397,52 +9397,6 @@ Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, chip::app::Cl } // namespace OperatingMode -namespace MaximumCheckInBackOff { - -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint32_t * value) -{ - using Traits = NumericAttributeTraits; - Traits::StorageType temp; - uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); - Protocols::InteractionModel::Status status = - emberAfReadAttribute(endpoint, Clusters::IcdManagement::Id, Id, readable, sizeof(temp)); - VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); - if (!Traits::CanRepresentValue(/* isNullable = */ false, temp)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - *value = Traits::StorageToWorking(temp); - return status; -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint32_t value, MarkAttributeDirty markDirty) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::IcdManagement::Id, Id, writable, ZCL_INT32U_ATTRIBUTE_TYPE, markDirty); -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint32_t value) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::IcdManagement::Id, Id, writable, ZCL_INT32U_ATTRIBUTE_TYPE); -} - -} // namespace MaximumCheckInBackOff - namespace FeatureMap { Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint32_t * value) diff --git a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h index da42291b70eb2a..6ce01a1ac232d3 100644 --- a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h +++ b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h @@ -1506,12 +1506,6 @@ Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, chip::app::Cl MarkAttributeDirty markDirty); } // namespace OperatingMode -namespace MaximumCheckInBackOff { -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint32_t * value); // int32u -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint32_t value); -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint32_t value, MarkAttributeDirty markDirty); -} // namespace MaximumCheckInBackOff - namespace FeatureMap { Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint32_t * value); // bitmap32 Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint32_t value); From f4d91881809084046eda7560ee94519d7ccc4f15 Mon Sep 17 00:00:00 2001 From: C Freeman Date: Fri, 26 Jul 2024 20:46:09 -0400 Subject: [PATCH 46/49] python test framework: PICS 2.0 (#34384) * [WIP] python test framework PICS 2.0 * typo * use ms int for test duration * Add documentation to decorators and helpers * Remove unused parameters * Restyled by autopep8 * Restyled by isort * linter * fix merge conflict * formatting of imports * address review comments --------- Co-authored-by: Restyled.io --- .github/workflows/tests.yaml | 2 + .../matter_yamltests/hooks.py | 6 + src/python_testing/TC_TIMESYNC_2_1.py | 55 ++- src/python_testing/matter_testing_support.py | 188 +++++++++- .../test_testing/MockTestRunner.py | 16 +- .../test_testing/TestDecorators.py | 336 ++++++++++++++++++ 6 files changed, 559 insertions(+), 44 deletions(-) create mode 100644 src/python_testing/test_testing/TestDecorators.py diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 2ff4d548c1484a..d6ff5d341cd070 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -601,6 +601,8 @@ jobs: scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/TestConformanceSupport.py' scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/test_IDM_10_4.py' scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/test_TC_SC_7_1.py' + scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/TestDecorators.py' + - name: Uploading core files uses: actions/upload-artifact@v4 diff --git a/scripts/py_matter_yamltests/matter_yamltests/hooks.py b/scripts/py_matter_yamltests/matter_yamltests/hooks.py index 3d25cfff9c06c1..78905826f55757 100644 --- a/scripts/py_matter_yamltests/matter_yamltests/hooks.py +++ b/scripts/py_matter_yamltests/matter_yamltests/hooks.py @@ -227,6 +227,12 @@ def show_prompt(self, """ pass + def test_skipped(self, filename: str, name: str): + """ + This method is called when the test script determines that the test is not applicable for the DUT. + """ + pass + class WebSocketRunnerHooks(): def connecting(self, url: str): diff --git a/src/python_testing/TC_TIMESYNC_2_1.py b/src/python_testing/TC_TIMESYNC_2_1.py index cba8ad9570ad2d..1cfb22e17c7fc8 100644 --- a/src/python_testing/TC_TIMESYNC_2_1.py +++ b/src/python_testing/TC_TIMESYNC_2_1.py @@ -32,53 +32,46 @@ import chip.clusters as Clusters from chip.clusters.Types import NullValue -from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main, utc_time_in_matter_epoch +from matter_testing_support import (MatterBaseTest, default_matter_test_main, has_attribute, has_cluster, per_endpoint_test, + utc_time_in_matter_epoch) from mobly import asserts class TC_TIMESYNC_2_1(MatterBaseTest): - async def read_ts_attribute_expect_success(self, endpoint, attribute): + async def read_ts_attribute_expect_success(self, attribute): cluster = Clusters.Objects.TimeSynchronization - return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + return await self.read_single_attribute_check_success(endpoint=None, cluster=cluster, attribute=attribute) - def pics_TC_TIMESYNC_2_1(self) -> list[str]: - return ["TIMESYNC.S"] - - @async_test_body + @per_endpoint_test(has_cluster(Clusters.TimeSynchronization) and has_attribute(Clusters.TimeSynchronization.Attributes.TimeSource)) async def test_TC_TIMESYNC_2_1(self): - endpoint = 0 - - features = await self.read_single_attribute(dev_ctrl=self.default_controller, node_id=self.dut_node_id, - endpoint=endpoint, attribute=Clusters.TimeSynchronization.Attributes.FeatureMap) + attributes = Clusters.TimeSynchronization.Attributes + features = await self.read_ts_attribute_expect_success(attribute=attributes.FeatureMap) self.supports_time_zone = bool(features & Clusters.TimeSynchronization.Bitmaps.Feature.kTimeZone) self.supports_ntpc = bool(features & Clusters.TimeSynchronization.Bitmaps.Feature.kNTPClient) self.supports_ntps = bool(features & Clusters.TimeSynchronization.Bitmaps.Feature.kNTPServer) self.supports_trusted_time_source = bool(features & Clusters.TimeSynchronization.Bitmaps.Feature.kTimeSyncClient) - time_cluster = Clusters.TimeSynchronization - timesync_attr_list = time_cluster.Attributes.AttributeList - attribute_list = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=time_cluster, attribute=timesync_attr_list) - timesource_attr_id = time_cluster.Attributes.TimeSource.attribute_id + timesync_attr_list = attributes.AttributeList + attribute_list = await self.read_ts_attribute_expect_success(attribute=timesync_attr_list) + timesource_attr_id = attributes.TimeSource.attribute_id self.print_step(1, "Commissioning, already done") - attributes = Clusters.TimeSynchronization.Attributes self.print_step(2, "Read Granularity attribute") - granularity_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.Granularity) + granularity_dut = await self.read_ts_attribute_expect_success(attribute=attributes.Granularity) asserts.assert_less(granularity_dut, Clusters.TimeSynchronization.Enums.GranularityEnum.kUnknownEnumValue, "Granularity is not in valid range") self.print_step(3, "Read TimeSource") if timesource_attr_id in attribute_list: - time_source = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.TimeSource) + time_source = await self.read_ts_attribute_expect_success(attribute=attributes.TimeSource) asserts.assert_less(time_source, Clusters.TimeSynchronization.Enums.TimeSourceEnum.kUnknownEnumValue, "TimeSource is not in valid range") self.print_step(4, "Read TrustedTimeSource") if self.supports_trusted_time_source: - trusted_time_source = await self.read_ts_attribute_expect_success(endpoint=endpoint, - attribute=attributes.TrustedTimeSource) + trusted_time_source = await self.read_ts_attribute_expect_success(attribute=attributes.TrustedTimeSource) if trusted_time_source is not NullValue: asserts.assert_less_equal(trusted_time_source.fabricIndex, 0xFE, "FabricIndex for the TrustedTimeSource is out of range") @@ -87,7 +80,7 @@ async def test_TC_TIMESYNC_2_1(self): self.print_step(5, "Read DefaultNTP") if self.supports_ntpc: - default_ntp = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.DefaultNTP) + default_ntp = await self.read_ts_attribute_expect_success(attribute=attributes.DefaultNTP) if default_ntp is not NullValue: asserts.assert_less_equal(len(default_ntp), 128, "DefaultNTP length must be less than 128") # Assume this is a valid web address if it has at least one . in the name @@ -102,7 +95,7 @@ async def test_TC_TIMESYNC_2_1(self): self.print_step(6, "Read TimeZone") if self.supports_time_zone: - tz_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.TimeZone) + tz_dut = await self.read_ts_attribute_expect_success(attribute=attributes.TimeZone) asserts.assert_greater_equal(len(tz_dut), 1, "TimeZone must have at least one entry in the list") asserts.assert_less_equal(len(tz_dut), 2, "TimeZone may have a maximum of two entries in the list") for entry in tz_dut: @@ -117,7 +110,7 @@ async def test_TC_TIMESYNC_2_1(self): self.print_step(7, "Read DSTOffset") if self.supports_time_zone: - dst_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.DSTOffset) + dst_dut = await self.read_ts_attribute_expect_success(attribute=attributes.DSTOffset) last_valid_until = -1 last_valid_starting = -1 for dst in dst_dut: @@ -131,7 +124,7 @@ async def test_TC_TIMESYNC_2_1(self): asserts.assert_equal(dst, dst_dut[-1], "DSTOffset list must have Null ValidUntil at the end") self.print_step(8, "Read UTCTime") - utc_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.UTCTime) + utc_dut = await self.read_ts_attribute_expect_success(attribute=attributes.UTCTime) if utc_dut is NullValue: asserts.assert_equal(granularity_dut, Clusters.TimeSynchronization.Enums.GranularityEnum.kNoTimeGranularity) else: @@ -146,8 +139,8 @@ async def test_TC_TIMESYNC_2_1(self): self.print_step(9, "Read LocalTime") if self.supports_time_zone: - utc_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.UTCTime) - local_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.LocalTime) + utc_dut = await self.read_ts_attribute_expect_success(attribute=attributes.UTCTime) + local_dut = await self.read_ts_attribute_expect_success(attribute=attributes.LocalTime) if utc_dut is NullValue: asserts.assert_true(local_dut is NullValue, "LocalTime must be Null if UTC time is Null") elif len(dst_dut) == 0: @@ -161,30 +154,30 @@ async def test_TC_TIMESYNC_2_1(self): self.print_step(10, "Read TimeZoneDatabase") if self.supports_time_zone: - tz_db_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.TimeZoneDatabase) + tz_db_dut = await self.read_ts_attribute_expect_success(attribute=attributes.TimeZoneDatabase) asserts.assert_less(tz_db_dut, Clusters.TimeSynchronization.Enums.TimeZoneDatabaseEnum.kUnknownEnumValue, "TimeZoneDatabase is not in valid range") self.print_step(11, "Read NTPServerAvailable") if self.supports_ntps: # bool typechecking happens in the test read functions, so all we need to do here is do the read - await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.NTPServerAvailable) + await self.read_ts_attribute_expect_success(attribute=attributes.NTPServerAvailable) self.print_step(12, "Read TimeZoneListMaxSize") if self.supports_time_zone: - size = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.TimeZoneListMaxSize) + size = await self.read_ts_attribute_expect_success(attribute=attributes.TimeZoneListMaxSize) asserts.assert_greater_equal(size, 1, "TimeZoneListMaxSize must be at least 1") asserts.assert_less_equal(size, 2, "TimeZoneListMaxSize must be max 2") self.print_step(13, "Read DSTOffsetListMaxSize") if self.supports_time_zone: - size = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.DSTOffsetListMaxSize) + size = await self.read_ts_attribute_expect_success(attribute=attributes.DSTOffsetListMaxSize) asserts.assert_greater_equal(size, 1, "DSTOffsetListMaxSize must be at least 1") self.print_step(14, "Read SupportsDNSResolve") # bool typechecking happens in the test read functions, so all we need to do here is do the read if self.supports_ntpc: - await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.SupportsDNSResolve) + await self.read_ts_attribute_expect_success(attribute=attributes.SupportsDNSResolve) if __name__ == "__main__": diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index 28385b22400d27..3d235e49873a8a 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -34,6 +34,7 @@ from dataclasses import dataclass, field from datetime import datetime, timedelta, timezone from enum import Enum +from functools import partial from typing import Any, List, Optional, Tuple from chip.tlv import float32, uint @@ -408,6 +409,9 @@ def show_prompt(self, default_value: Optional[str] = None) -> None: pass + def test_skipped(self, filename: str, name: str): + logging.info(f"Skipping test from {filename}: {name}") + @dataclass class MatterTestConfig: @@ -837,8 +841,10 @@ def setup_class(self): def setup_test(self): self.current_step_index = 0 + self.test_start_time = datetime.now(timezone.utc) self.step_start_time = datetime.now(timezone.utc) self.step_skipped = False + self.failed = False if self.runner_hook and not self.is_commissioning: test_name = self.current_test_info.name steps = self.get_defined_test_steps(test_name) @@ -1017,12 +1023,11 @@ def on_fail(self, record): record is of type TestResultRecord ''' + self.failed = True if self.runner_hook and not self.is_commissioning: exception = record.termination_signal.exception step_duration = (datetime.now(timezone.utc) - self.step_start_time) / timedelta(microseconds=1) - # This isn't QUITE the test duration because the commissioning is handled separately, but it's clsoe enough for now - # This is already given in milliseconds - test_duration = record.end_time - record.begin_time + test_duration = (datetime.now(timezone.utc) - self.test_start_time) / timedelta(microseconds=1) # TODO: I have no idea what logger, logs, request or received are. Hope None works because I have nothing to give self.runner_hook.step_failure(logger=None, logs=None, duration=step_duration, request=None, received=None) self.runner_hook.test_stop(exception=exception, duration=test_duration) @@ -1036,7 +1041,7 @@ def on_pass(self, record): # What is request? This seems like an implementation detail for the runner # TODO: As with failure, I have no idea what logger, logs or request are meant to be step_duration = (datetime.now(timezone.utc) - self.step_start_time) / timedelta(microseconds=1) - test_duration = record.end_time - record.begin_time + test_duration = (datetime.now(timezone.utc) - self.test_start_time) / timedelta(microseconds=1) self.runner_hook.step_success(logger=None, logs=None, duration=step_duration, request=None) # TODO: this check could easily be annoying when doing dev. flag it somehow? Ditto with the in-order check @@ -1054,6 +1059,18 @@ def on_pass(self, record): if self.runner_hook and not self.is_commissioning: self.runner_hook.test_stop(exception=None, duration=test_duration) + def on_skip(self, record): + ''' Called by Mobly on test skip + + record is of type TestResultRecord + ''' + if self.runner_hook and not self.is_commissioning: + test_duration = (datetime.now(timezone.utc) - self.test_start_time) / timedelta(microseconds=1) + test_name = self.current_test_info.name + filename = inspect.getfile(self.__class__) + self.runner_hook.test_skipped(filename, test_name) + self.runner_hook.test_stop(exception=None, duration=test_duration) + def pics_guard(self, pics_condition: bool): """Checks a condition and if False marks the test step as skipped and returns False, otherwise returns True. @@ -1612,6 +1629,12 @@ def parse_matter_test_args(argv: Optional[List[str]] = None) -> MatterTestConfig return convert_args_to_matter_config(parser.parse_known_args(argv)[0]) +def _async_runner(body, self: MatterBaseTest, *args, **kwargs): + timeout = self.matter_test_config.timeout if self.matter_test_config.timeout is not None else self.default_timeout + runner_with_timeout = asyncio.wait_for(body(self, *args, **kwargs), timeout=timeout) + return asyncio.run(runner_with_timeout) + + def async_test_body(body): """Decorator required to be applied whenever a `test_*` method is `async def`. @@ -1621,13 +1644,164 @@ def async_test_body(body): """ def async_runner(self: MatterBaseTest, *args, **kwargs): - timeout = self.matter_test_config.timeout if self.matter_test_config.timeout is not None else self.default_timeout - runner_with_timeout = asyncio.wait_for(body(self, *args, **kwargs), timeout=timeout) - return asyncio.run(runner_with_timeout) + return _async_runner(body, self, *args, **kwargs) return async_runner +def per_node_test(body): + """ Decorator to be used for PICS-free tests that apply to the entire node. + + Use this decorator when your script needs to be run once to validate the whole node. + To use this decorator, the test must NOT have an associated pics_ method. + """ + + def whole_node_runner(self: MatterBaseTest, *args, **kwargs): + asserts.assert_false(self.get_test_pics(self.current_test_info.name), "pics_ method supplied for per_node_test.") + return _async_runner(body, self, *args, **kwargs) + + return whole_node_runner + + +EndpointCheckFunction = typing.Callable[[Clusters.Attribute.AsyncReadTransaction.ReadResponse, int], bool] + + +def _has_cluster(wildcard, endpoint, cluster: ClusterObjects.Cluster) -> bool: + try: + return cluster in wildcard.attributes[endpoint] + except KeyError: + return False + + +def has_cluster(cluster: ClusterObjects.ClusterObjectDescriptor) -> EndpointCheckFunction: + """ EndpointCheckFunction that can be passed as a parameter to the per_endpoint_test decorator. + + Use this function with the per_endpoint_test decorator to run this test on all endpoints with + the specified cluster. For example, given a device with the following conformance + + EP0: cluster A, B, C + EP1: cluster D, E + EP2, cluster D + EP3, cluster E + + And the following test specification: + @per_endpoint_test(has_cluster(Clusters.D)) + test_mytest(self): + ... + + The test would be run on endpoint 1 and on endpoint 2. + + If the cluster is not found on any endpoint the decorator will call the on_skip function to + notify the test harness that the test is not applicable to this node and the test will not be run. + """ + return partial(_has_cluster, cluster=cluster) + + +def _has_attribute(wildcard, endpoint, attribute: ClusterObjects.ClusterAttributeDescriptor) -> bool: + cluster = getattr(Clusters, attribute.__qualname__.split('.')[-3]) + try: + attr_list = wildcard.attributes[endpoint][cluster][cluster.Attributes.AttributeList] + return attribute.attribute_id in attr_list + except KeyError: + return False + + +def has_attribute(attribute: ClusterObjects.ClusterAttributeDescriptor) -> EndpointCheckFunction: + """ EndpointCheckFunction that can be passed as a parameter to the per_endpoint_test decorator. + + Use this function with the per_endpoint_test decorator to run this test on all endpoints with + the specified attribute. For example, given a device with the following conformance + + EP0: cluster A, B, C + EP1: cluster D with attribute d, E + EP2, cluster D with attribute d + EP3, cluster D without attribute d + + And the following test specification: + @per_endpoint_test(has_attribute(Clusters.D.Attributes.d)) + test_mytest(self): + ... + + The test would be run on endpoint 1 and on endpoint 2. + + If the cluster is not found on any endpoint the decorator will call the on_skip function to + notify the test harness that the test is not applicable to this node and the test will not be run. + """ + return partial(_has_attribute, attribute=attribute) + + +async def get_accepted_endpoints_for_test(self: MatterBaseTest, accept_function: EndpointCheckFunction) -> list[uint]: + """ Helper function for the per_endpoint_test decorator. + + Returns a list of endpoints on which the test should be run given the accept_function for the test. + """ + wildcard = await self.default_controller.Read(self.dut_node_id, [()]) + return [e for e in wildcard.attributes.keys() if accept_function(wildcard, e)] + + +def per_endpoint_test(accept_function: EndpointCheckFunction): + """ Test decorator for a test that needs to be run once per endpoint that meets the accept_function criteria. + + Place this decorator above the test_ method to have the test framework run this test once per endpoint. + This decorator takes an EndpointCheckFunction to assess whether a test needs to be run on a particular + endpoint. + + For example, given the following device conformance: + + EP0: cluster A, B, C + EP1: cluster D, E + EP2, cluster D + EP3, cluster E + + And the following test specification: + @per_endpoint_test(has_cluster(Clusters.D)) + test_mytest(self): + ... + + The test would be run on endpoint 1 and on endpoint 2. + + If the cluster is not found on any endpoint the decorator will call the on_skip function to + notify the test harness that the test is not applicable to this node and the test will not be run. + + The decorator works by setting the self.matter_test_config.endpoint value and running the test function. + Therefore, tests that make use of this decorator should call controller functions against that endpoint. + Support functions in this file default to this endpoint. + + Tests that use this decorator cannot use a pics_ method for test selection and should not reference any + PICS values internally. + """ + def per_endpoint_test_internal(body): + def per_endpoint_runner(self: MatterBaseTest, *args, **kwargs): + asserts.assert_false(self.get_test_pics(self.current_test_info.name), "pics_ method supplied for per_endpoint_test.") + runner_with_timeout = asyncio.wait_for(get_accepted_endpoints_for_test(self, accept_function), timeout=5) + endpoints = asyncio.run(runner_with_timeout) + if not endpoints: + logging.info("No matching endpoints found - skipping test") + asserts.skip('No endpoints match requirements') + return + logging.info(f"Running test on the following endpoints: {endpoints}") + # setup_class is meant to be called once, but setup_test is expected to be run before + # each iteration. Mobly will run it for us the first time, but since we're running this + # more than one time, we want to make sure we reset everything as expected. + # Ditto for teardown - we want to tear down after each iteration, and we want to notify the hook that + # the test iteration is stopped. test_stop is called by on_pass or on_fail during the last iteration or + # on failure. + original_ep = self.matter_test_config.endpoint + for e in endpoints: + logging.info(f'Running test on endpoint {e}') + if e != endpoints[0]: + self.setup_test() + self.matter_test_config.endpoint = e + _async_runner(body, self, *args, **kwargs) + if e != endpoints[-1] and not self.failed: + self.teardown_test() + test_duration = (datetime.now(timezone.utc) - self.test_start_time) / timedelta(microseconds=1) + self.runner_hook.test_stop(exception=None, duration=test_duration) + self.matter_test_config.endpoint = original_ep + return per_endpoint_runner + return per_endpoint_test_internal + + class CommissionDeviceTest(MatterBaseTest): """Test class auto-injected at the start of test list to commission a device when requested""" diff --git a/src/python_testing/test_testing/MockTestRunner.py b/src/python_testing/test_testing/MockTestRunner.py index ae8d1730b8fda9..c8febc93381fdc 100644 --- a/src/python_testing/test_testing/MockTestRunner.py +++ b/src/python_testing/test_testing/MockTestRunner.py @@ -37,22 +37,26 @@ async def __call__(self, *args, **kwargs): class MockTestRunner(): - def __init__(self, filename: str, classname: str, test: str, endpoint: int, pics: dict[str, bool] = None): + + def __init__(self, filename: str, classname: str, test: str, endpoint: int = 0, pics: dict[str, bool] = None): self.test = test self.endpoint = endpoint self.pics = pics - self.set_test_config(MatterTestConfig()) - + self.set_test(filename, classname, test) self.stack = MatterStackState(self.config) self.default_controller = self.stack.certificate_authorities[0].adminList[0].NewController( nodeId=self.config.controller_node_id, paaTrustStorePath=str(self.config.paa_trust_store_path), catTags=self.config.controller_cat_tags ) + + def set_test(self, filename: str, classname: str, test: str): + self.test = test + self.set_test_config() module = importlib.import_module(Path(os.path.basename(filename)).stem) self.test_class = getattr(module, classname) - def set_test_config(self, test_config: MatterTestConfig): + def set_test_config(self, test_config: MatterTestConfig = MatterTestConfig()): self.config = test_config self.config.tests = [self.test] self.config.endpoint = self.endpoint @@ -64,8 +68,8 @@ def set_test_config(self, test_config: MatterTestConfig): def Shutdown(self): self.stack.Shutdown() - def run_test_with_mock_read(self, read_cache: Attribute.AsyncReadTransaction.ReadResponse): + def run_test_with_mock_read(self, read_cache: Attribute.AsyncReadTransaction.ReadResponse, hooks=None): self.default_controller.Read = AsyncMock(return_value=read_cache) # This doesn't need to do anything since we are overriding the read anyway self.default_controller.FindOrEstablishPASESession = AsyncMock(return_value=None) - return run_tests_no_exit(self.test_class, self.config, None, self.default_controller, self.stack) + return run_tests_no_exit(self.test_class, self.config, hooks, self.default_controller, self.stack) diff --git a/src/python_testing/test_testing/TestDecorators.py b/src/python_testing/test_testing/TestDecorators.py new file mode 100644 index 00000000000000..60a75bfca466ef --- /dev/null +++ b/src/python_testing/test_testing/TestDecorators.py @@ -0,0 +1,336 @@ +# +# 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. +# + + +# Hooks: +# If this is a per-endpoint test +# - If test is run, hook object will get one test_start and one test_stop call per endpoint on which the test is run +# - If the test is skipped, hook object will get test_start, test_skipped, test_stop +# If this is a whole-node test: +# - test will always be run, so hook object will get test_start, test_stop +# +# You will get step_* calls as appropriate in between the test_start and test_stop calls if the test is not skipped. + +import os +import sys + +import chip.clusters as Clusters +from chip.clusters import Attribute +from chip.clusters import ClusterObjects as ClusterObjects + +try: + from matter_testing_support import (MatterBaseTest, async_test_body, get_accepted_endpoints_for_test, has_attribute, + has_cluster, per_endpoint_test, per_node_test) +except ImportError: + sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + from matter_testing_support import (MatterBaseTest, async_test_body, get_accepted_endpoints_for_test, has_attribute, + has_cluster, per_endpoint_test, per_node_test) + +from typing import Optional + +from mobly import asserts +from MockTestRunner import MockTestRunner + + +def get_clusters(endpoints: list[int]) -> Attribute.AsyncReadTransaction.ReadResponse: + c = Clusters.OnOff + attr = c.Attributes + # We're JUST populating the globals here because that's all that matters for this particular test + feature_map = c.Bitmaps.Feature.kLighting + # Only supported attributes - globals and OnOff. This isn't a compliant device. Doesn't matter for this test. + attribute_list = [attr.FeatureMap.attribute_id, attr.AttributeList.attribute_id, + attr.AcceptedCommandList.attribute_id, attr.GeneratedCommandList.attribute_id, attr.OnOff.attribute_id] + accepted_commands = [c.Commands.Off, c.Commands.On] + resp = Attribute.AsyncReadTransaction.ReadResponse({}, [], {}) + for e in endpoints: + resp.attributes[e] = {c: {attr.FeatureMap: feature_map, + attr.AttributeList: attribute_list, attr.AcceptedCommandList: accepted_commands}} + return resp + + +class DecoratorTestRunnerHooks: + def __init__(self): + self.started = [] + self.skipped = [] + self.stopped = 0 + + def start(self, count: int): + pass + + def stop(self, duration: int): + pass + + def test_start(self, filename: str, name: str, count: int, steps: list[str] = []): + self.started.append(name) + + def test_skipped(self, filename: str, name: str): + self.skipped.append(name) + + def test_stop(self, exception: Exception, duration: int): + self.stopped += 1 + + def step_skipped(self, name: str, expression: str): + pass + + def step_start(self, name: str): + pass + + def step_success(self, logger, logs, duration: int, request): + pass + + def step_failure(self, logger, logs, duration: int, request, received): + pass + + def step_unknown(self): + pass + + def show_prompt(self, + msg: str, + placeholder: Optional[str] = None, + default_value: Optional[str] = None) -> None: + pass + + +class TestDecorators(MatterBaseTest): + def test_checkers(self): + has_onoff = has_cluster(Clusters.OnOff) + has_onoff_onoff = has_attribute(Clusters.OnOff.Attributes.OnOff) + has_onoff_ontime = has_attribute(Clusters.OnOff.Attributes.OnTime) + has_timesync = has_cluster(Clusters.TimeSynchronization) + has_timesync_utc = has_attribute(Clusters.TimeSynchronization.Attributes.UTCTime) + + wildcard = get_clusters([0, 1]) + + def check_endpoints(f, expect_true, expectation: str): + asserts.assert_equal(f(wildcard, 0), expect_true, f"Expected {expectation} == {expect_true} on EP0") + asserts.assert_equal(f(wildcard, 1), expect_true, f"Expected {expectation} == {expect_true} on EP1") + asserts.assert_false(f(wildcard, 2), f"Expected {expectation} == False on EP2") + + check_endpoints(has_onoff, True, "OnOff Cluster") + check_endpoints(has_onoff_onoff, True, "OnOff attribute") + check_endpoints(has_onoff_ontime, False, "OnTime attribute") + check_endpoints(has_timesync, False, "TimeSynchronization Cluster") + check_endpoints(has_timesync_utc, False, "UTC attribute") + + @async_test_body + async def test_endpoints(self): + has_onoff = has_cluster(Clusters.OnOff) + has_onoff_onoff = has_attribute(Clusters.OnOff.Attributes.OnOff) + has_onoff_ontime = has_attribute(Clusters.OnOff.Attributes.OnTime) + has_timesync = has_cluster(Clusters.TimeSynchronization) + has_timesync_utc = has_attribute(Clusters.TimeSynchronization.Attributes.UTCTime) + + all_endpoints = await self.default_controller.Read(self.dut_node_id, [()]) + all_endpoints = list(all_endpoints.attributes.keys()) + + msg = "Unexpected endpoint list returned" + + endpoints = await get_accepted_endpoints_for_test(self, has_onoff) + asserts.assert_equal(endpoints, all_endpoints, msg) + + endpoints = await get_accepted_endpoints_for_test(self, has_onoff_onoff) + asserts.assert_equal(endpoints, all_endpoints, msg) + + endpoints = await get_accepted_endpoints_for_test(self, has_onoff_ontime) + asserts.assert_equal(endpoints, [], msg) + + endpoints = await get_accepted_endpoints_for_test(self, has_timesync) + asserts.assert_equal(endpoints, [], msg) + + endpoints = await get_accepted_endpoints_for_test(self, has_timesync_utc) + asserts.assert_equal(endpoints, [], msg) + + # This test should cause an assertion because it has pics_ method + @per_node_test + async def test_whole_node_with_pics(self): + pass + + # This method returns the top level pics for test_whole_node_with_pics + # It is used to test that test_whole_node_with_pics will fail since you can't have a whole node test gated on a PICS. + def pics_whole_node_with_pics(self): + return ['EXAMPLE.S'] + + # This test should cause an assertion because it has a pics_ method + @per_endpoint_test(has_cluster(Clusters.OnOff)) + async def test_per_endpoint_with_pics(self): + pass + + # This method returns the top level pics for test_per_endpoint_with_pics + # It is used to test that test_per_endpoint_with_pics will fail since you can't have a per endpoint test gated on a PICS. + def pics_per_endpoint_with_pics(self): + return ['EXAMPLE.S'] + + # This test should be run once + @per_node_test + async def test_whole_node_ok(self): + pass + + # This test should be run once per endpoint + @per_endpoint_test(has_cluster(Clusters.OnOff)) + async def test_endpoint_cluster_yes(self): + pass + + # This test should be skipped since this cluster isn't on any endpoint + @per_endpoint_test(has_cluster(Clusters.TimeSynchronization)) + async def test_endpoint_cluster_no(self): + pass + + # This test should be run once per endpoint + @per_endpoint_test(has_attribute(Clusters.OnOff.Attributes.OnOff)) + async def test_endpoint_attribute_yes(self): + pass + + # This test should be skipped since this attribute isn't on the supported cluster + @per_endpoint_test(has_attribute(Clusters.OnOff.Attributes.OffWaitTime)) + async def test_endpoint_attribute_supported_cluster_no(self): + pass + + # This test should be skipped since this attribute is part of an unsupported cluster + @per_endpoint_test(has_attribute(Clusters.TimeSynchronization.Attributes.Granularity)) + async def test_endpoint_attribute_unsupported_cluster_no(self): + pass + + # This test should be run since both are present + @per_endpoint_test(has_attribute(Clusters.OnOff.Attributes.OnOff) and has_cluster(Clusters.OnOff)) + async def test_endpoint_boolean_yes(self): + pass + + # This test should be skipped since we have an OnOff cluster, but no Time sync + @per_endpoint_test(has_cluster(Clusters.OnOff) and has_cluster(Clusters.TimeSynchronization)) + async def test_endpoint_boolean_no(self): + pass + + @per_endpoint_test(has_cluster(Clusters.OnOff)) + async def test_fail_on_ep0(self): + if self.matter_test_config.endpoint == 0: + asserts.fail("Expected failure") + + @per_endpoint_test(has_cluster(Clusters.OnOff)) + async def test_fail_on_ep1(self): + if self.matter_test_config.endpoint == 1: + asserts.fail("Expected failure") + + @per_node_test + async def test_fail_on_whole_node(self): + asserts.fail("Expected failure") + + +def main(): + failures = [] + hooks = DecoratorTestRunnerHooks() + test_runner = MockTestRunner('TestDecorators.py', 'TestDecorators', 'test_checkers') + read_resp = get_clusters([0, 1]) + ok = test_runner.run_test_with_mock_read(read_resp, hooks) + if not ok: + failures.append("Test case failure: test_checkers") + + test_runner.set_test('TestDecorators.py', 'TestDecorators', 'test_endpoints') + read_resp = get_clusters([0, 1]) + ok = test_runner.run_test_with_mock_read(read_resp, hooks) + if not ok: + failures.append("Test case failure: test_endpoints") + + read_resp = get_clusters([0]) + ok = test_runner.run_test_with_mock_read(read_resp, hooks) + if not ok: + failures.append("Test case failure: test_endpoints") + + test_name = 'test_whole_node_with_pics' + test_runner.set_test('TestDecorators.py', 'TestDecorators', test_name) + ok = test_runner.run_test_with_mock_read(read_resp, hooks) + if ok: + failures.append(f"Did not get expected test assertion on {test_name}") + + test_name = 'test_per_endpoint_with_pics' + test_runner.set_test('TestDecorators.py', 'TestDecorators', test_name) + ok = test_runner.run_test_with_mock_read(read_resp, hooks) + if ok: + failures.append(f"Did not get expected test assertion on {test_name}") + + # Test should run once for the whole node, regardless of the number of endpoints + def run_check(test_name: str, read_response: Attribute.AsyncReadTransaction.ReadResponse, expected_runs: int, expect_skip: bool) -> None: + nonlocal failures + test_runner.set_test('TestDecorators.py', 'TestDecorators', test_name) + hooks = DecoratorTestRunnerHooks() + ok = test_runner.run_test_with_mock_read(read_response, hooks) + started_ok = len(hooks.started) == expected_runs + skipped_ok = (hooks.skipped != []) == expect_skip + stopped_ok = hooks.stopped == expected_runs + if not ok or not started_ok or not skipped_ok or not stopped_ok: + failures.append( + f'Expected {expected_runs} run of {test_name}, skips expected: {expect_skip}. Runs: {hooks.started}, skips: {hooks.skipped} stops: {hooks.stopped}') + + def check_once_per_node(test_name: str): + run_check(test_name, get_clusters([0]), 1, False) + run_check(test_name, get_clusters([0, 1]), 1, False) + + def check_once_per_endpoint(test_name: str): + run_check(test_name, get_clusters([0]), 1, False) + run_check(test_name, get_clusters([0, 1]), 2, False) + + def check_skipped(test_name: str): + run_check(test_name, get_clusters([0]), 1, True) + run_check(test_name, get_clusters([0, 1]), 1, True) + + check_once_per_node('test_whole_node_ok') + check_once_per_endpoint('test_endpoint_cluster_yes') + check_skipped('test_endpoint_cluster_no') + check_once_per_endpoint('test_endpoint_attribute_yes') + check_skipped('test_endpoint_attribute_supported_cluster_no') + check_skipped('test_endpoint_attribute_unsupported_cluster_no') + check_once_per_endpoint('test_endpoint_boolean_yes') + check_skipped('test_endpoint_boolean_no') + + test_name = 'test_fail_on_ep0' + test_runner.set_test('TestDecorators.py', 'TestDecorators', test_name) + read_resp = get_clusters([0, 1]) + ok = test_runner.run_test_with_mock_read(read_resp, hooks) + if ok: + failures.append(f"Did not get expected test assertion on {test_name}") + + test_name = 'test_fail_on_ep1' + test_runner.set_test('TestDecorators.py', 'TestDecorators', test_name) + read_resp = get_clusters([0, 1]) + ok = test_runner.run_test_with_mock_read(read_resp, hooks) + if ok: + failures.append(f"Did not get expected test assertion on {test_name}") + + test_name = 'test_fail_on_ep1' + test_runner.set_test('TestDecorators.py', 'TestDecorators', test_name) + read_resp = get_clusters([0]) + ok = test_runner.run_test_with_mock_read(read_resp, hooks) + if not ok: + failures.append(f"Unexpected failure on {test_name}") + + test_name = 'test_fail_on_whole_node' + test_runner.set_test('TestDecorators.py', 'TestDecorators', test_name) + read_resp = get_clusters([0, 1]) + ok = test_runner.run_test_with_mock_read(read_resp, hooks) + if ok: + failures.append(f"Did not get expected test assertion on {test_name}") + + test_runner.Shutdown() + print( + f"Test of Decorators: test response incorrect: {len(failures)}") + for f in failures: + print(f) + + return 1 if failures else 0 + + +if __name__ == "__main__": + sys.exit(main()) From a1690f54523802145b8fd6cb64528a675be1676e Mon Sep 17 00:00:00 2001 From: jamesharrow <93921463+jamesharrow@users.noreply.github.com> Date: Sat, 27 Jul 2024 14:11:31 +0100 Subject: [PATCH 47/49] 2nd attempt to fix random EEM and EPM CI failures (#34476) * Revert "Attempt to reduce CI failures by increasing timeout in checking fake readings have changed." This reverts commit 42d36e974367fafb5ba898b3714787b538d22e62. * Added fixed random seed to try to avoid CI failures when looking for different values between two readings. There is a high probability that 2 energy readings could be the same, so using a fixed seed should mean that tests are at least reproducible. * Restyled by whitespace * Improved comment * Moved the change previously made in EVSManufacturerImpl to FakeReadings.cpp after updating to master. * Restyled by whitespace --------- Co-authored-by: Restyled.io --- .../energy-management-common/src/FakeReadings.cpp | 6 ++++++ src/python_testing/TC_EEM_2_2.py | 4 ++-- src/python_testing/TC_EEM_2_3.py | 4 ++-- src/python_testing/TC_EEM_2_4.py | 4 ++-- src/python_testing/TC_EEM_2_5.py | 4 ++-- src/python_testing/TC_EPM_2_2.py | 6 +++--- 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/examples/energy-management-app/energy-management-common/src/FakeReadings.cpp b/examples/energy-management-app/energy-management-common/src/FakeReadings.cpp index 151e82be8436e9..c887357a5f7b58 100644 --- a/examples/energy-management-app/energy-management-common/src/FakeReadings.cpp +++ b/examples/energy-management-app/energy-management-common/src/FakeReadings.cpp @@ -93,6 +93,12 @@ void FakeReadings::StartFakeReadings(EndpointId aEndpointId, int64_t aPower_mW, if (bReset) { + // Use a fixed random seed to try to avoid random CI test failures + // which are caused when the test is checking for 2 different numbers. + // This is statistically more likely when the test runs for a long time + // or if the seed is not set + srand(1); + mTotalEnergyImported = 0; mTotalEnergyExported = 0; } diff --git a/src/python_testing/TC_EEM_2_2.py b/src/python_testing/TC_EEM_2_2.py index e8094a0d834ca3..fd2d5c828f64c9 100644 --- a/src/python_testing/TC_EEM_2_2.py +++ b/src/python_testing/TC_EEM_2_2.py @@ -53,7 +53,7 @@ def steps_TC_EEM_2_2(self) -> list[TestStep]: TestStep("4", "Wait 3 seconds"), TestStep("4a", "TH reads from the DUT the CumulativeEnergyImported attribute", "Verify the read is successful and note the value read."), - TestStep("5", "Wait 5 seconds"), + TestStep("5", "Wait 3 seconds"), TestStep("5a", "TH reads from the DUT the CumulativeEnergyImported attribute", "Verify the read is successful and that the value is greater than the value measured in step 4a."), TestStep("6", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EEM.TEST_EVENT_TRIGGER for Stop Fake Readings Test Event."), @@ -80,7 +80,7 @@ async def test_TC_EEM_2_2(self): cumulative_energy_imported = await self.read_eem_attribute_expect_success("CumulativeEnergyImported") self.step("5") - time.sleep(5) + time.sleep(3) self.step("5a") cumulative_energy_imported_2 = await self.read_eem_attribute_expect_success("CumulativeEnergyImported") diff --git a/src/python_testing/TC_EEM_2_3.py b/src/python_testing/TC_EEM_2_3.py index 2364f0d012530c..927ec9aea2efd5 100644 --- a/src/python_testing/TC_EEM_2_3.py +++ b/src/python_testing/TC_EEM_2_3.py @@ -53,7 +53,7 @@ def steps_TC_EEM_2_3(self) -> list[TestStep]: TestStep("4", "Wait 6 seconds"), TestStep("4a", "TH reads from the DUT the CumulativeEnergyExported attribute", "Verify the read is successful and note the value read."), - TestStep("5", "Wait 11 seconds"), + TestStep("5", "Wait 6 seconds"), TestStep("5a", "TH reads from the DUT the CumulativeEnergyExported attribute", "Verify the read is successful and that the value is greater than the value measured in step 4a."), TestStep("6", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EEM.TEST_EVENT_TRIGGER for Stop Fake Readings Test Event."), @@ -80,7 +80,7 @@ async def test_TC_EEM_2_3(self): cumulative_energy_exported = await self.read_eem_attribute_expect_success("CumulativeEnergyExported") self.step("5") - time.sleep(11) + time.sleep(6) self.step("5a") cumulative_energy_exported_2 = await self.read_eem_attribute_expect_success("CumulativeEnergyExported") diff --git a/src/python_testing/TC_EEM_2_4.py b/src/python_testing/TC_EEM_2_4.py index b3f052bc968411..dbc89f1934b1ab 100644 --- a/src/python_testing/TC_EEM_2_4.py +++ b/src/python_testing/TC_EEM_2_4.py @@ -53,7 +53,7 @@ def steps_TC_EEM_2_4(self) -> list[TestStep]: TestStep("4", "Wait 3 seconds"), TestStep("4a", "TH reads from the DUT the PeriodicEnergyImported attribute", "Verify the read is successful and note the value read."), - TestStep("5", "Wait 5 seconds"), + TestStep("5", "Wait 3 seconds"), TestStep("5a", "TH reads from the DUT the PeriodicEnergyImported attribute", "Verify the read is successful and that the value read has to be different from value measure in step 4a."), TestStep("6", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EEM.TEST_EVENT_TRIGGER for Stop Fake Readings Test Event."), @@ -80,7 +80,7 @@ async def test_TC_EEM_2_4(self): periodic_energy_imported = await self.read_eem_attribute_expect_success("PeriodicEnergyImported") self.step("5") - time.sleep(5) + time.sleep(3) self.step("5a") periodic_energy_imported_2 = await self.read_eem_attribute_expect_success("PeriodicEnergyImported") diff --git a/src/python_testing/TC_EEM_2_5.py b/src/python_testing/TC_EEM_2_5.py index f7fd34a0d05eca..95f01ebc15f2d7 100644 --- a/src/python_testing/TC_EEM_2_5.py +++ b/src/python_testing/TC_EEM_2_5.py @@ -53,7 +53,7 @@ def steps_TC_EEM_2_5(self) -> list[TestStep]: TestStep("4", "Wait 6 seconds"), TestStep("4a", "TH reads from the DUT the PeriodicEnergyExported attribute", "Verify the read is successful and note the value read."), - TestStep("5", "Wait 11 seconds"), + TestStep("5", "Wait 6 seconds"), TestStep("5a", "TH reads from the DUT the PeriodicEnergyExported attribute", "Verify the read is successful and that the value read has to be different from value measure in step 4a."), TestStep("6", "TH sends TestEventTrigger command to General Diagnostics Cluster on Endpoint 0 with EnableKey field set to PIXIT.EEM.TEST_EVENT_TRIGGER_KEY and EventTrigger field set to PIXIT.EEM.TEST_EVENT_TRIGGER for Stop Fake Readings Test Event."), @@ -80,7 +80,7 @@ async def test_TC_EEM_2_5(self): periodic_energy_exported = await self.read_eem_attribute_expect_success("PeriodicEnergyExported") self.step("5") - time.sleep(11) + time.sleep(6) self.step("5a") periodic_energy_exported_2 = await self.read_eem_attribute_expect_success("PeriodicEnergyExported") diff --git a/src/python_testing/TC_EPM_2_2.py b/src/python_testing/TC_EPM_2_2.py index 89c49928b3baa1..1ca497f4fabf4f 100644 --- a/src/python_testing/TC_EPM_2_2.py +++ b/src/python_testing/TC_EPM_2_2.py @@ -61,7 +61,7 @@ def steps_TC_EPM_2_2(self) -> list[TestStep]: "Verify the read is successful and that the value is between 3'848 and 4'848 mA. Note the value read."), TestStep("4c", "TH reads from the DUT the Voltage attribute", "Verify the read is successful and that the value is between 229'000 and 231'000 mV. Note the value read."), - TestStep("5", "Wait 5 seconds"), + TestStep("5", "Wait 3 seconds"), TestStep("5a", "TH reads from the DUT the ActivePower attribute", "Verify the read is successful, that the value is between '980'000 and 1'020'000 mW, and the value is different from the value read in step 4a."), TestStep("5b", "TH reads from the DUT the ActiveCurrent attribute", @@ -105,8 +105,8 @@ async def test_TC_EPM_2_2(self): voltage = await self.check_epm_attribute_in_range("Voltage", 229000, 231000) self.step("5") - # After 5 seconds... - time.sleep(5) + # After 3 seconds... + time.sleep(3) self.step("5a") # Active power is Mandatory From 44c725d2b89a5d1d1e6e88fa0262f312d57dc77e Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Sat, 27 Jul 2024 15:02:44 -0400 Subject: [PATCH 48/49] Update ZAP to improve support for global data types. (#34557) --- scripts/setup/zap.json | 4 ++-- scripts/setup/zap.version | 2 +- scripts/tools/zap/zap_execution.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/setup/zap.json b/scripts/setup/zap.json index 636da03eeb2e8f..c80affb2587550 100644 --- a/scripts/setup/zap.json +++ b/scripts/setup/zap.json @@ -8,13 +8,13 @@ "mac-amd64", "windows-amd64" ], - "tags": ["version:2@v2024.07.10-nightly.1"] + "tags": ["version:2@v2024.07.26-nightly.1"] }, { "_comment": "Always get the amd64 version on mac until usable arm64 zap build is available", "path": "fuchsia/third_party/zap/mac-amd64", "platforms": ["mac-arm64"], - "tags": ["version:2@v2024.07.10-nightly.1"] + "tags": ["version:2@v2024.07.26-nightly.1"] } ] } diff --git a/scripts/setup/zap.version b/scripts/setup/zap.version index 4414b06f961e8b..853cddc8622b96 100644 --- a/scripts/setup/zap.version +++ b/scripts/setup/zap.version @@ -1 +1 @@ -v2024.07.10-nightly +v2024.07.26-nightly diff --git a/scripts/tools/zap/zap_execution.py b/scripts/tools/zap/zap_execution.py index d027079ac70070..5d035fd22843ce 100644 --- a/scripts/tools/zap/zap_execution.py +++ b/scripts/tools/zap/zap_execution.py @@ -23,7 +23,7 @@ # Use scripts/tools/zap/version_update.py to manage ZAP versioning as many # files may need updating for versions # -MIN_ZAP_VERSION = '2024.7.10' +MIN_ZAP_VERSION = '2024.7.26' class ZapTool: From 13fe13ae3b61a5b9adb3b1ab8248fa3db11dddf2 Mon Sep 17 00:00:00 2001 From: Amine Alami <43780877+Alami-Amine@users.noreply.github.com> Date: Sun, 28 Jul 2024 20:09:51 +0200 Subject: [PATCH 49/49] VS Code Python Formatting (#34475) * add python formatter arguments to vscode .settings * add python formatters to recommended Extensions --- .vscode/extensions.json | 4 +++- .vscode/settings.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index d5969bc3ae58f2..9cf1f09cc666bc 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -21,7 +21,9 @@ "vadimcn.vscode-lldb", "xaver.clang-format", "yuichinukiyama.vscode-preview-server", - "yzhang.markdown-all-in-one" + "yzhang.markdown-all-in-one", + "ms-python.autopep8", + "ms-python.isort" ], // List of extensions recommended by VS Code that should not be recommended for users of this workspace. "unwantedRecommendations": [] diff --git a/.vscode/settings.json b/.vscode/settings.json index 397e6230087325..a8b3dd631afe4f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -192,5 +192,5 @@ "[python]": { "editor.defaultFormatter": "ms-python.autopep8" }, - "python.formatting.provider": "none" + "autopep8.args": ["--max-line-length", "132"] }