From 6f3357bc50ea0e1469980277503d128c9bb37f3b Mon Sep 17 00:00:00 2001 From: cecille Date: Fri, 6 Sep 2024 15:20:47 -0400 Subject: [PATCH 01/31] Valve: disco ball - Attribute getters --- src/app/chip_data_model.gni | 9 + ...onfiguration-and-control-cluster-logic.cpp | 123 +++++++ ...-configuration-and-control-cluster-logic.h | 121 +++++++ ...configuration-and-control-matter-context.h | 47 +++ src/app/tests/BUILD.gn | 16 + .../TestValveConfigurationAndControl.cpp | 316 ++++++++++++++++++ src/lib/core/DataModelTypes.h | 2 + 7 files changed, 634 insertions(+) create mode 100644 src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp create mode 100644 src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h create mode 100644 src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h create mode 100644 src/app/tests/TestValveConfigurationAndControl.cpp diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index c8907ac98b3d79..9005eaa92142e3 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -443,6 +443,15 @@ template("chip_data_model") { "${_app_root}/clusters/${cluster}/ArlEncoder.cpp", "${_app_root}/clusters/${cluster}/ArlEncoder.h", ] + } else if (cluster == "valve-configuration-and-control-server") { + sources += [ + "${_app_root}/clusters/${cluster}/${cluster}.cpp", + "${_app_root}/clusters/${cluster}/valve-configuration-and-control-cluster-logic.cpp", + "${_app_root}/clusters/${cluster}/valve-configuration-and-control-cluster-logic.h", + "${_app_root}/clusters/${cluster}/valve-configuration-and-control-delegate.h", + "${_app_root}/clusters/${cluster}/valve-configuration-and-control-matter-context.h", + ] + cflags += [ "-Wno-unused-private-field" ] } else { sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp" ] } diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp new file mode 100644 index 00000000000000..364ae1e2811056 --- /dev/null +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp @@ -0,0 +1,123 @@ +/** + * + * 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. + */ +/** + * @file Cross-platform API to handle cluster-specific logic for the valve configuration and control cluster on a single endpoint. + */ + +#include "valve-configuration-and-control-cluster-logic.h" +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace ValveConfigurationAndControl { + +CHIP_ERROR ClusterLogic::Init(const ClusterConformance & conformance, const ClusterState & initialState) +{ + if (!conformance.Valid()) + { + return CHIP_ERROR_INVALID_DEVICE_DESCRIPTOR; + } + mConformance = conformance; + mState = initialState; + mInitialized = true; + return CHIP_NO_ERROR; +} + +// All Get functions: +// Return CHIP_ERROR_INVALID_STATE if the class has not been initialized. +// Return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE if the attribute is not supported by the conformance. +// Return CHIP_NO_ERROR and set the parameter value otherwise +CHIP_ERROR ClusterLogic::GetOpenDuration(DataModel::Nullable & openDuration) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); + openDuration = mState.openDuration; + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterLogic::GetDefaultOpenDuration(DataModel::Nullable & defaultOpenDuration) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); + defaultOpenDuration = mState.defaultOpenDuration; + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterLogic::GetAutoCloseTime(DataModel::Nullable & autoCloseTime) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mConformance.HasFeature(Feature::kTimeSync), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + autoCloseTime = mState.autoCloseTime; + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterLogic::GetRemainingDuration(DataModel::Nullable & remainingDuration) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); + remainingDuration = mState.remainingDuration; + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterLogic::GetCurrentState(DataModel::Nullable & currentState) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); + currentState = mState.currentState; + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterLogic::GetTargetState(DataModel::Nullable & targetState) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); + targetState = mState.targetState; + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterLogic::GetCurrentLevel(DataModel::Nullable & currentLevel) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mConformance.HasFeature(Feature::kLevel), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + currentLevel = mState.currentLevel; + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterLogic::GetTargetLevel(DataModel::Nullable & targetLevel) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mConformance.HasFeature(Feature::kLevel), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + targetLevel = mState.targetLevel; + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterLogic::GetDefaultOpenLevel(uint8_t & defaultOpenLevel) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mConformance.HasFeature(Feature::kLevel), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + VerifyOrReturnError(mConformance.supportsDefaultOpenLevel, CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + defaultOpenLevel = mState.defaultOpenLevel; + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterLogic::GetValveFault(BitMask & valveFault) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mConformance.supportsValveFault, CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + valveFault = mState.valveFault; + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterLogic::GetLevelStep(uint8_t & levelStep) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mConformance.HasFeature(Feature::kLevel), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + VerifyOrReturnError(mConformance.supportsLevelStep, CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + levelStep = mState.levelStep; + return CHIP_NO_ERROR; +} + +} // namespace ValveConfigurationAndControl +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h new file mode 100644 index 00000000000000..280e70e7c2535a --- /dev/null +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h @@ -0,0 +1,121 @@ +/** + * + * 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. + */ +/** + * @file Cross-platform API to handle cluster-specific logic for the valve configuration and control cluster on a single endpoint. + */ + +#pragma once + +#include "valve-configuration-and-control-delegate.h" +#include "valve-configuration-and-control-matter-context.h" +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace ValveConfigurationAndControl { + +struct ClusterConformance +{ + inline bool HasFeature(Feature feature) const { return featureMap & to_underlying(feature); } + uint32_t featureMap; + bool supportsDefaultOpenLevel; + bool supportsValveFault; + bool supportsLevelStep; + bool Valid() const + { + bool supportsLvl = HasFeature(Feature::kLevel); + if (supportsDefaultOpenLevel & !supportsLvl) + { + ChipLogError(Zcl, + "Invalid Valve configuration and control conformance - DefaultOpenLevel is not supported without LVL"); + return false; + } + if (supportsLevelStep & !supportsLvl) + { + ChipLogError(Zcl, "Invalid Valve configuration and control conformance - LevelStep is not supported without LVL"); + return false; + } + return true; + } +}; + +struct ClusterState +{ + DataModel::Nullable openDuration = DataModel::NullNullable; + DataModel::Nullable defaultOpenDuration = DataModel::NullNullable; + DataModel::Nullable autoCloseTime = DataModel::NullNullable; + DataModel::Nullable remainingDuration = DataModel::NullNullable; + DataModel::Nullable currentState = DataModel::NullNullable; + DataModel::Nullable targetState = DataModel::NullNullable; + DataModel::Nullable currentLevel = DataModel::NullNullable; + DataModel::Nullable targetLevel = DataModel::NullNullable; + Percent defaultOpenLevel = 100u; + BitMask valveFault = 0u; + uint8_t levelStep = 1u; +}; + +class ClusterLogic +{ +public: + // Instantiates a ClusterLogic class. The caller maintains ownership of the driver and the context, but provides them for use by + // the ClusterLogic class. + ClusterLogic(Delegate & clusterDriver, MatterContext & matterContext) : + mClusterDriver(clusterDriver), mMatterContext(matterContext) + { + // TODO: remove these once the fields are used properly + (void) mClusterDriver; + (void) mMatterContext; + } + + // Validates the conformance and performs initialization. + // Returns CHIP_ERROR_INCORRECT_STATE if the cluster has already been initialized. + // Returns CHIP_ERROR_INVALID_DEVICE_DESCRIPTOR if the conformance is incorrect. + CHIP_ERROR Init(const ClusterConformance & conformance, const ClusterState & initialState = ClusterState()); + // CHIP_ERROR HandleOpen(); + // CHIP_ERROR HandleClose(); + + // All Get functions: + // Return CHIP_ERROR_INVALID_STATE if the class has not been initialized. + // Return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE if the attribute is not supported by the conformance. + // Otherwise return CHIP_NO_ERROR and set the input parameter value to the current cluster state value + CHIP_ERROR GetOpenDuration(DataModel::Nullable & openDuration); + CHIP_ERROR GetDefaultOpenDuration(DataModel::Nullable & defaultOpenDuration); + CHIP_ERROR GetAutoCloseTime(DataModel::Nullable & autoCloseTime); + CHIP_ERROR GetRemainingDuration(DataModel::Nullable & remainingDuration); + CHIP_ERROR GetCurrentState(DataModel::Nullable & currentState); + CHIP_ERROR GetTargetState(DataModel::Nullable & targetState); + CHIP_ERROR GetCurrentLevel(DataModel::Nullable & currentLevel); + CHIP_ERROR GetTargetLevel(DataModel::Nullable & targetLevel); + CHIP_ERROR GetDefaultOpenLevel(Percent & defaultOpenLevel); + CHIP_ERROR GetValveFault(BitMask & valveFault); + CHIP_ERROR GetLevelStep(uint8_t & levelStep); + +private: + bool mInitialized = false; + + Delegate & mClusterDriver; + MatterContext & mMatterContext; + + ClusterConformance mConformance; + ClusterState mState; +}; + +} // namespace ValveConfigurationAndControl +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h new file mode 100644 index 00000000000000..b59a11cfc2f7c0 --- /dev/null +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h @@ -0,0 +1,47 @@ +/* + * + * 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 ValveConfigurationAndControl { + +/** @brief + * Interface to allow interaction with interaction model and ember layers. Can be faked for unit testing. + */ +class MatterContext +{ +public: + // TODO: remove touch on mEndpoint once this is used. I am apparently unable to locate the proper place to turn this off in the + // build file, so whatever, compiler, you win. I've touched it. You happy now? + MatterContext(EndpointId endpoint) : mEndpoint(endpoint) { (void) mEndpoint; } + +private: + EndpointId mEndpoint; +}; + +} // namespace ValveConfigurationAndControl +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index 104a57a2fc019a..7706c8c4822b35 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -154,6 +154,20 @@ source_set("thread-border-router-management-test-srcs") { ] } +source_set("valve-configuration-and-control-test-srcs") { + sources = [ + "${chip_root}/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp", + "${chip_root}/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h", + "${chip_root}/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-delegate.h", + "${chip_root}/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h", + ] + public_deps = [ + "${chip_root}/src/app/common:cluster-objects", + "${chip_root}/src/lib/core", + "${chip_root}/src/lib/support", + ] +} + source_set("app-test-stubs") { sources = [ "test-ember-api.cpp", @@ -220,6 +234,7 @@ chip_test_suite("tests") { "TestTestEventTriggerDelegate.cpp", "TestTimeSyncDataProvider.cpp", "TestTimedHandler.cpp", + "TestValveConfigurationAndControl.cpp", "TestWriteInteraction.cpp", ] @@ -233,6 +248,7 @@ chip_test_suite("tests") { ":power-cluster-test-srcs", ":thread-network-directory-test-srcs", ":time-sync-data-provider-test-srcs", + ":valve-configuration-and-control-test-srcs", "${chip_root}/src/app", "${chip_root}/src/app/codegen-data-model-provider:instance-header", "${chip_root}/src/app/common:cluster-objects", diff --git a/src/app/tests/TestValveConfigurationAndControl.cpp b/src/app/tests/TestValveConfigurationAndControl.cpp new file mode 100644 index 00000000000000..abafd2a9cb8783 --- /dev/null +++ b/src/app/tests/TestValveConfigurationAndControl.cpp @@ -0,0 +1,316 @@ +/* + * + * 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 "lib/support/CHIPMem.h" +#include +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace ValveConfigurationAndControl { + +class TestValveConfigurationAndControlClusterLogic : public ::testing::Test +{ +public: + static void SetUpTestSuite() { ASSERT_EQ(Platform::MemoryInit(), CHIP_NO_ERROR); } + static void TearDownTestSuite() { Platform::MemoryShutdown(); } +}; + +class TestDelegate : public Delegate +{ +public: + TestDelegate() {} + DataModel::Nullable HandleOpenValve(DataModel::Nullable level) override + { + return DataModel::NullNullable; + } + CHIP_ERROR HandleCloseValve() override { return CHIP_NO_ERROR; } + void HandleRemainingDurationTick(uint32_t duration) override {} +}; + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestConformanceValid) +{ + ClusterConformance conformance; + // Nothing on, should be valid + conformance = { .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = false, .supportsLevelStep = false }; + EXPECT_TRUE(conformance.Valid()); + + // LVL on, no optional stuff, should be valid + conformance = { .featureMap = to_underlying(Feature::kLevel), + .supportsDefaultOpenLevel = false, + .supportsValveFault = false, + .supportsLevelStep = false }; + EXPECT_TRUE(conformance.Valid()); + + // LVL on, one optional level thing + conformance = { .featureMap = to_underlying(Feature::kLevel), + .supportsDefaultOpenLevel = true, + .supportsValveFault = false, + .supportsLevelStep = false }; + EXPECT_TRUE(conformance.Valid()); + + // LVL on, one optional level thing + conformance = { .featureMap = to_underlying(Feature::kLevel), + .supportsDefaultOpenLevel = false, + .supportsValveFault = false, + .supportsLevelStep = true }; + EXPECT_TRUE(conformance.Valid()); + + // LVL on, two optional level things + conformance = { .featureMap = to_underlying(Feature::kLevel), + .supportsDefaultOpenLevel = true, + .supportsValveFault = false, + .supportsLevelStep = true }; + EXPECT_TRUE(conformance.Valid()); + + // Fully optional thing on + conformance = { .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = true, .supportsLevelStep = false }; + EXPECT_TRUE(conformance.Valid()); + + // TS on + conformance = { .featureMap = to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = false, + .supportsValveFault = false, + .supportsLevelStep = false }; + EXPECT_TRUE(conformance.Valid()); + + // Both features on + conformance = { .featureMap = to_underlying(Feature::kTimeSync) | to_underlying(Feature::kLevel), + .supportsDefaultOpenLevel = false, + .supportsValveFault = false, + .supportsLevelStep = false }; + EXPECT_TRUE(conformance.Valid()); + + // LVL off, one optional level thing on + conformance = { .featureMap = 0, .supportsDefaultOpenLevel = true, .supportsValveFault = false, .supportsLevelStep = false }; + EXPECT_FALSE(conformance.Valid()); + + // LVL on, other optional level thing on + conformance = { .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = false, .supportsLevelStep = true }; + EXPECT_FALSE(conformance.Valid()); + + // Bad feature map + conformance = { .featureMap = 0x04, .supportsDefaultOpenLevel = false, .supportsValveFault = false, .supportsLevelStep = true }; + EXPECT_FALSE(conformance.Valid()); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesAllFeatures) +{ + TestDelegate delegate; + MatterContext context = MatterContext(0); + ClusterLogic logic = ClusterLogic(delegate, context); + + // Everything on, all should return values + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + DataModel::Nullable valElapsedSNullable; + DataModel::Nullable valEpochUsNullable; + DataModel::Nullable valEnumNullable; + DataModel::Nullable valPercentNullable; + Percent valPercent; + uint8_t val8; + BitMask valBitmap; + + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetDefaultOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetAutoCloseTime(valEpochUsNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEpochUsNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetCurrentState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetTargetState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetCurrentLevel(valPercentNullable), CHIP_NO_ERROR); + EXPECT_EQ(valPercentNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetTargetLevel(valPercentNullable), CHIP_NO_ERROR); + EXPECT_EQ(valPercentNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetDefaultOpenLevel(valPercent), CHIP_NO_ERROR); + EXPECT_EQ(valPercent, 100); + + EXPECT_EQ(logic.GetValveFault(valBitmap), CHIP_NO_ERROR); + EXPECT_EQ(valBitmap.Raw(), 0); + + EXPECT_EQ(logic.GetLevelStep(val8), CHIP_NO_ERROR); + EXPECT_EQ(val8, 1); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesNoFeatures) +{ + TestDelegate delegate; + MatterContext context = MatterContext(0); + ClusterLogic logic = ClusterLogic(delegate, context); + + // Everything on, all should return values + ClusterConformance conformance = { + .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = false, .supportsLevelStep = false + }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + DataModel::Nullable valElapsedSNullable; + DataModel::Nullable valEpochUsNullable; + DataModel::Nullable valEnumNullable; + DataModel::Nullable valPercentNullable; + Percent valPercent; + uint8_t val8; + BitMask valBitmap; + + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetDefaultOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetAutoCloseTime(valEpochUsNullable), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + + EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetCurrentState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetTargetState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetCurrentLevel(valPercentNullable), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + + EXPECT_EQ(logic.GetTargetLevel(valPercentNullable), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + + EXPECT_EQ(logic.GetDefaultOpenLevel(valPercent), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + + EXPECT_EQ(logic.GetValveFault(valBitmap), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + + EXPECT_EQ(logic.GetLevelStep(val8), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesStartingState) +{ + TestDelegate delegate; + MatterContext context = MatterContext(0); + ClusterLogic logic = ClusterLogic(delegate, context); + + // Everything on, all should return values + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + ClusterState state = { + .openDuration = DataModel::MakeNullable(static_cast(12u)), + .defaultOpenDuration = DataModel::MakeNullable(static_cast(10u)), + .autoCloseTime = DataModel::MakeNullable(static_cast(2500u)), + .remainingDuration = DataModel::MakeNullable(static_cast(1u)), + .currentState = DataModel::MakeNullable(ValveStateEnum::kTransitioning), + .targetState = DataModel::MakeNullable(ValveStateEnum::kOpen), + .currentLevel = DataModel::MakeNullable(static_cast(50u)), + .targetLevel = DataModel::MakeNullable(static_cast(75u)), + .defaultOpenLevel = 90u, + .valveFault = BitMask(ValveFaultBitmap::kLeaking), + .levelStep = 2u, + }; + EXPECT_EQ(logic.Init(conformance, state), CHIP_NO_ERROR); + + DataModel::Nullable valElapsedSNullable; + DataModel::Nullable valEpochUsNullable; + DataModel::Nullable valEnumNullable; + DataModel::Nullable valPercentNullable; + Percent valPercent; + uint8_t val8; + BitMask valBitmap; + + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, state.openDuration); + + EXPECT_EQ(logic.GetDefaultOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, state.defaultOpenDuration); + + EXPECT_EQ(logic.GetAutoCloseTime(valEpochUsNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEpochUsNullable, state.autoCloseTime); + + EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, state.remainingDuration); + + EXPECT_EQ(logic.GetCurrentState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, state.currentState); + + EXPECT_EQ(logic.GetTargetState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, state.targetState); + + EXPECT_EQ(logic.GetCurrentLevel(valPercentNullable), CHIP_NO_ERROR); + EXPECT_EQ(valPercentNullable, state.currentLevel); + + EXPECT_EQ(logic.GetTargetLevel(valPercentNullable), CHIP_NO_ERROR); + EXPECT_EQ(valPercentNullable, state.targetLevel); + + EXPECT_EQ(logic.GetDefaultOpenLevel(valPercent), CHIP_NO_ERROR); + EXPECT_EQ(valPercent, state.defaultOpenLevel); + + EXPECT_EQ(logic.GetValveFault(valBitmap), CHIP_NO_ERROR); + EXPECT_EQ(valBitmap, state.valveFault); + + EXPECT_EQ(logic.GetLevelStep(val8), CHIP_NO_ERROR); + EXPECT_EQ(val8, state.levelStep); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesUninitialized) +{ + TestDelegate delegate; + MatterContext context = MatterContext(0); + ClusterLogic logic = ClusterLogic(delegate, context); + + DataModel::Nullable valElapsedSNullable; + DataModel::Nullable valEpochUsNullable; + DataModel::Nullable valEnumNullable; + DataModel::Nullable valPercentNullable; + Percent valPercent; + uint8_t val8; + BitMask valBitmap; + + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_ERROR_INCORRECT_STATE); + EXPECT_EQ(logic.GetDefaultOpenDuration(valElapsedSNullable), CHIP_ERROR_INCORRECT_STATE); + EXPECT_EQ(logic.GetAutoCloseTime(valEpochUsNullable), CHIP_ERROR_INCORRECT_STATE); + EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_ERROR_INCORRECT_STATE); + EXPECT_EQ(logic.GetCurrentState(valEnumNullable), CHIP_ERROR_INCORRECT_STATE); + EXPECT_EQ(logic.GetTargetState(valEnumNullable), CHIP_ERROR_INCORRECT_STATE); + EXPECT_EQ(logic.GetCurrentLevel(valPercentNullable), CHIP_ERROR_INCORRECT_STATE); + EXPECT_EQ(logic.GetTargetLevel(valPercentNullable), CHIP_ERROR_INCORRECT_STATE); + EXPECT_EQ(logic.GetDefaultOpenLevel(valPercent), CHIP_ERROR_INCORRECT_STATE); + EXPECT_EQ(logic.GetValveFault(valBitmap), CHIP_ERROR_INCORRECT_STATE); + EXPECT_EQ(logic.GetLevelStep(val8), CHIP_ERROR_INCORRECT_STATE); +} + +} // namespace ValveConfigurationAndControl +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/lib/core/DataModelTypes.h b/src/lib/core/DataModelTypes.h index e0cd1d2fa1d773..2e6fe4b6d5afa2 100644 --- a/src/lib/core/DataModelTypes.h +++ b/src/lib/core/DataModelTypes.h @@ -36,7 +36,9 @@ typedef uint16_t CommandRef; typedef uint64_t CompressedFabricId; typedef uint32_t DataVersion; typedef uint32_t DeviceTypeId; +typedef uint32_t ElapsedS; typedef uint16_t EndpointId; +typedef uint64_t EpochUs; typedef uint32_t EventId; typedef uint64_t EventNumber; typedef uint64_t FabricId; From 1ab57bbdf788e4cfc810958ce26af74f4907c074 Mon Sep 17 00:00:00 2001 From: Cecille Freeman Date: Sat, 7 Sep 2024 21:27:26 -0400 Subject: [PATCH 02/31] Valve: Disco ball - Attribute Setters for client --- src/app/chip_data_model.gni | 1 + ...onfiguration-and-control-cluster-logic.cpp | 57 ++++ ...-configuration-and-control-cluster-logic.h | 30 +- ...nfiguration-and-control-matter-context.cpp | 65 +++++ ...configuration-and-control-matter-context.h | 22 +- src/app/tests/BUILD.gn | 1 + .../TestValveConfigurationAndControl.cpp | 259 +++++++++++++++++- src/lib/support/DefaultStorageKeyAllocator.h | 7 + 8 files changed, 427 insertions(+), 15 deletions(-) create mode 100644 src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.cpp diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index 9005eaa92142e3..a260fed3842fc6 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -449,6 +449,7 @@ template("chip_data_model") { "${_app_root}/clusters/${cluster}/valve-configuration-and-control-cluster-logic.cpp", "${_app_root}/clusters/${cluster}/valve-configuration-and-control-cluster-logic.h", "${_app_root}/clusters/${cluster}/valve-configuration-and-control-delegate.h", + "${_app_root}/clusters/${cluster}/valve-configuration-and-control-matter-context.cpp", "${_app_root}/clusters/${cluster}/valve-configuration-and-control-matter-context.h", ] cflags += [ "-Wno-unused-private-field" ] diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp index 364ae1e2811056..fcdb686bc520e9 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp @@ -34,6 +34,15 @@ CHIP_ERROR ClusterLogic::Init(const ClusterConformance & conformance, const Clus } mConformance = conformance; mState = initialState; + + // Overwrite default initial state with stored persisted values if set. + uint32_t defaultOpenDuration; + if (mMatterContext.GetDefaultOpenDuration(defaultOpenDuration) == CHIP_NO_ERROR) + { + mState.defaultOpenDuration.SetNonNull(defaultOpenDuration); + } + mMatterContext.GetDefaultOpenLevel(mState.defaultOpenLevel); + mInitialized = true; return CHIP_NO_ERROR; } @@ -117,6 +126,54 @@ CHIP_ERROR ClusterLogic::GetLevelStep(uint8_t & levelStep) return CHIP_NO_ERROR; } +CHIP_ERROR ClusterLogic::SetDefaultOpenDuration(const DataModel::Nullable & defaultOpenDuration) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); + if (!defaultOpenDuration.IsNull() && defaultOpenDuration.Value() < 1) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + ReturnErrorOnFailure(mMatterContext.StoreDefaultOpenDuration(defaultOpenDuration)); + mState.defaultOpenDuration = defaultOpenDuration; + return CHIP_NO_ERROR; +} + +bool ClusterLogic::ValueCompliesWithLevelStep(const uint8_t value) +{ + if (mConformance.supportsLevelStep) + { + if ((value != 100u) && ((value % mState.levelStep) != 0)) + { + return false; + } + } + return true; +} + +CHIP_ERROR ClusterLogic::SetDefaultOpenLevel(const uint8_t defaultOpenLevel) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mConformance.supportsDefaultOpenLevel, CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + if (defaultOpenLevel < 1 || defaultOpenLevel > 100) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + VerifyOrReturnError(ValueCompliesWithLevelStep(defaultOpenLevel), CHIP_ERROR_INVALID_ARGUMENT); + + ReturnErrorOnFailure(mMatterContext.StoreDefaultOpenLevel(defaultOpenLevel)); + mState.defaultOpenLevel = defaultOpenLevel; + return CHIP_NO_ERROR; +} + +CHIP_ERROR ClusterLogic::SetValveFault(const ValveFaultBitmap valveFault) +{ + return CHIP_ERROR_NOT_IMPLEMENTED; +} +CHIP_ERROR ClusterLogic::ClearValveFault(const ValveFaultBitmap valveFault) +{ + return CHIP_ERROR_NOT_IMPLEMENTED; +} + } // namespace ValveConfigurationAndControl } // namespace Clusters } // namespace app diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h index 280e70e7c2535a..4c213ceec12eec 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h @@ -22,6 +22,7 @@ #include "valve-configuration-and-control-delegate.h" #include "valve-configuration-and-control-matter-context.h" +#include #include namespace chip { @@ -79,7 +80,6 @@ class ClusterLogic { // TODO: remove these once the fields are used properly (void) mClusterDriver; - (void) mMatterContext; } // Validates the conformance and performs initialization. @@ -105,7 +105,35 @@ class ClusterLogic CHIP_ERROR GetValveFault(BitMask & valveFault); CHIP_ERROR GetLevelStep(uint8_t & levelStep); + // All Set functions + // Return CHIP_ERROR_INCORRECT_STATE if the class has not been initialized. + // Return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE if the attribute is not supported by the conformance. + // Return CHIP_ERROR_INVALID_ARGUMENT if the input value is out of range. + // Returns CHIP_ERROR_PERSISTED_STORAGE_FAILED if the value could not not be stored in persistent storage. + // Otherwise return CHIP_NO_ERROR and set the parameter value in the cluster state + // Set functions are supplied for any values that can be set either internally by the device or externally + // through a direct attribute write. Changes to attributes that happen as a side effect of cluster commands + // are handled by the cluster command handlers. + + // DefaultOpenDuration can be set by the client using attribute write + CHIP_ERROR SetDefaultOpenDuration(const DataModel::Nullable & defaultOpenDuration); + + // DefaultOpenLevel can be set by the client using attribute write + CHIP_ERROR SetDefaultOpenLevel(const uint8_t defaultOpenLevel); + + // ValveFault can be set internally by the device. + // Use the Set function to add a specific valve fault and the clear function to clear it. + // Q: Should we push these through the delegate? + CHIP_ERROR SetValveFault(const ValveFaultBitmap valveFault); + CHIP_ERROR ClearValveFault(const ValveFaultBitmap valveFault); + + // Other ones that are set internally? + // Current state + // Current level + private: + // Determines if the level value is allowed per the level step. + bool ValueCompliesWithLevelStep(const uint8_t value); bool mInitialized = false; Delegate & mClusterDriver; diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.cpp new file mode 100644 index 00000000000000..fed1f25d9c1046 --- /dev/null +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.cpp @@ -0,0 +1,65 @@ +/* + * + * 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 "valve-configuration-and-control-matter-context.h" + +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace ValveConfigurationAndControl { + +CHIP_ERROR MatterContext::StoreDefaultOpenDuration(const DataModel::Nullable & defaultOpenDuration) +{ + + if (defaultOpenDuration.IsNull()) + { + return mPersistentStorageDelegate.SyncDeleteKeyValue( + DefaultStorageKeyAllocator::VCCDefaultOpenDuration(mEndpoint).KeyName()); + } + uint32_t val = defaultOpenDuration.Value(); + return mPersistentStorageDelegate.SyncSetKeyValue(DefaultStorageKeyAllocator::VCCDefaultOpenDuration(mEndpoint).KeyName(), &val, + sizeof(val)); +} + +CHIP_ERROR MatterContext::GetDefaultOpenDuration(uint32_t & returnVal) +{ + uint16_t size = static_cast(sizeof(returnVal)); + return mPersistentStorageDelegate.SyncGetKeyValue(DefaultStorageKeyAllocator::VCCDefaultOpenDuration(mEndpoint).KeyName(), + &returnVal, size); +} + +CHIP_ERROR MatterContext::StoreDefaultOpenLevel(const uint8_t defaultOpenLevel) +{ + + return mPersistentStorageDelegate.SyncSetKeyValue(DefaultStorageKeyAllocator::VCCDefaultOpenLevel(mEndpoint).KeyName(), + &defaultOpenLevel, sizeof(defaultOpenLevel)); +} + +CHIP_ERROR MatterContext::GetDefaultOpenLevel(uint8_t & returnVal) +{ + uint16_t size = static_cast(sizeof(returnVal)); + return mPersistentStorageDelegate.SyncGetKeyValue(DefaultStorageKeyAllocator::VCCDefaultOpenLevel(mEndpoint).KeyName(), + &returnVal, size); +} + +} // namespace ValveConfigurationAndControl +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h index b59a11cfc2f7c0..a3f3d90ceb6a91 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h @@ -18,9 +18,10 @@ #pragma once +#include #include -// #include -#include +#include +#include namespace chip { namespace app { @@ -35,10 +36,25 @@ class MatterContext public: // TODO: remove touch on mEndpoint once this is used. I am apparently unable to locate the proper place to turn this off in the // build file, so whatever, compiler, you win. I've touched it. You happy now? - MatterContext(EndpointId endpoint) : mEndpoint(endpoint) { (void) mEndpoint; } + MatterContext(EndpointId endpoint, PersistentStorageDelegate & persistentStorageDelegate) : + mEndpoint(endpoint), mPersistentStorageDelegate(persistentStorageDelegate) + {} + + // All Set functions: + // Return CHIP_ERROR_PERSISTED_STORAGE_FAILED if the value could not be stored. + // Return CHIP_NO_ERROR if the value was successfully stored. Clear the storage on a NullNullable. + CHIP_ERROR StoreDefaultOpenDuration(const DataModel::Nullable & val); + CHIP_ERROR StoreDefaultOpenLevel(const uint8_t val); + + // All Get functions + // Return CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND if there is no value in storage. + // Return CHIP_NO_ERROR and fill return value if the value is found. + CHIP_ERROR GetDefaultOpenDuration(uint32_t & returnVal); + CHIP_ERROR GetDefaultOpenLevel(uint8_t & returnVal); private: EndpointId mEndpoint; + PersistentStorageDelegate & mPersistentStorageDelegate; }; } // namespace ValveConfigurationAndControl diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index 7706c8c4822b35..2a6807cae974ef 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -159,6 +159,7 @@ source_set("valve-configuration-and-control-test-srcs") { "${chip_root}/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp", "${chip_root}/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h", "${chip_root}/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-delegate.h", + "${chip_root}/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.cpp", "${chip_root}/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h", ] public_deps = [ diff --git a/src/app/tests/TestValveConfigurationAndControl.cpp b/src/app/tests/TestValveConfigurationAndControl.cpp index abafd2a9cb8783..0b6f08cf74f76b 100644 --- a/src/app/tests/TestValveConfigurationAndControl.cpp +++ b/src/app/tests/TestValveConfigurationAndControl.cpp @@ -15,11 +15,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "lib/support/CHIPMem.h" #include #include #include #include +#include +#include +#include #include namespace chip { @@ -48,9 +50,10 @@ class TestDelegate : public Delegate TEST_F(TestValveConfigurationAndControlClusterLogic, TestConformanceValid) { - ClusterConformance conformance; // Nothing on, should be valid - conformance = { .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = false, .supportsLevelStep = false }; + ClusterConformance conformance = { + .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = false, .supportsLevelStep = false + }; EXPECT_TRUE(conformance.Valid()); // LVL on, no optional stuff, should be valid @@ -115,8 +118,9 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestConformanceValid) TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesAllFeatures) { TestDelegate delegate; - MatterContext context = MatterContext(0); - ClusterLogic logic = ClusterLogic(delegate, context); + TestPersistentStorageDelegate storageDelegate; + MatterContext context(0, storageDelegate); + ClusterLogic logic(delegate, context); // Everything on, all should return values ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), @@ -170,8 +174,9 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesAllFeature TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesNoFeatures) { TestDelegate delegate; - MatterContext context = MatterContext(0); - ClusterLogic logic = ClusterLogic(delegate, context); + TestPersistentStorageDelegate storageDelegate; + MatterContext context(0, storageDelegate); + ClusterLogic logic(delegate, context); // Everything on, all should return values ClusterConformance conformance = { @@ -218,8 +223,9 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesNoFeatures TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesStartingState) { TestDelegate delegate; - MatterContext context = MatterContext(0); - ClusterLogic logic = ClusterLogic(delegate, context); + TestPersistentStorageDelegate storageDelegate; + MatterContext context(0, storageDelegate); + ClusterLogic logic(delegate, context); // Everything on, all should return values ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), @@ -286,8 +292,9 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesStartingSt TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesUninitialized) { TestDelegate delegate; - MatterContext context = MatterContext(0); - ClusterLogic logic = ClusterLogic(delegate, context); + TestPersistentStorageDelegate storageDelegate; + MatterContext context(0, storageDelegate); + ClusterLogic logic(delegate, context); DataModel::Nullable valElapsedSNullable; DataModel::Nullable valEpochUsNullable; @@ -310,6 +317,236 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesUninitiali EXPECT_EQ(logic.GetLevelStep(val8), CHIP_ERROR_INCORRECT_STATE); } +TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenDuration) +{ + TestDelegate delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + DataModel::Nullable valElapsedSNullable; + + // Setting this value before initialization should fail + auto testVal = DataModel::MakeNullable(static_cast(5u)); + EXPECT_EQ(logic.SetDefaultOpenDuration(testVal), CHIP_ERROR_INCORRECT_STATE); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + EXPECT_EQ(logic.GetDefaultOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + // Lowest possible value + testVal = DataModel::MakeNullable(static_cast(1u)); + EXPECT_EQ(logic.SetDefaultOpenDuration(testVal), CHIP_NO_ERROR); + + // Ensure the value is accessible via the API + EXPECT_EQ(logic.GetDefaultOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, testVal); + + // Ensure the value is persisted in the test storage + StorageKeyName keyName = DefaultStorageKeyAllocator::VCCDefaultOpenDuration(endpoint); + uint32_t persistedValue; + uint16_t size = static_cast(sizeof(persistedValue)); + EXPECT_TRUE(storageDelegate.HasKey(keyName.KeyName())); + EXPECT_EQ(storageDelegate.SyncGetKeyValue(keyName.KeyName(), &persistedValue, size), CHIP_NO_ERROR); + EXPECT_EQ(persistedValue, testVal.Value()); + // Test that this doesn't exist for other endpoints. Check 1. + EXPECT_FALSE(storageDelegate.HasKey(DefaultStorageKeyAllocator::VCCDefaultOpenDuration(1).KeyName())); + + testVal = DataModel::MakeNullable(static_cast(12u)); + EXPECT_EQ(logic.SetDefaultOpenDuration(testVal), CHIP_NO_ERROR); + + EXPECT_EQ(logic.GetDefaultOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, testVal); + + EXPECT_TRUE(storageDelegate.HasKey(keyName.KeyName())); + EXPECT_EQ(storageDelegate.SyncGetKeyValue(keyName.KeyName(), &persistedValue, size), CHIP_NO_ERROR); + EXPECT_EQ(persistedValue, testVal.Value()); + + auto outOfRangeVal = DataModel::MakeNullable(static_cast(0u)); + EXPECT_EQ(logic.SetDefaultOpenDuration(outOfRangeVal), CHIP_ERROR_INVALID_ARGUMENT); + + // Ensure the value wasn't changed + EXPECT_EQ(logic.GetDefaultOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, testVal); + + EXPECT_TRUE(storageDelegate.HasKey(keyName.KeyName())); + EXPECT_EQ(storageDelegate.SyncGetKeyValue(keyName.KeyName(), &persistedValue, size), CHIP_NO_ERROR); + EXPECT_EQ(persistedValue, testVal.Value()); + + // Test that firing up a new logic cluster on the same endpoint loads the value from persisted storage + ClusterLogic logic_same_endpoint = ClusterLogic(delegate, context); + EXPECT_EQ(logic_same_endpoint.Init(conformance), CHIP_NO_ERROR); + EXPECT_EQ(logic_same_endpoint.GetDefaultOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, testVal); + + // Test that a new logic cluster on a different endpoint does not load the value + MatterContext context_ep1 = MatterContext(1, storageDelegate); + ClusterLogic logic_different_endpoint = ClusterLogic(delegate, context_ep1); + EXPECT_EQ(logic_different_endpoint.Init(conformance), CHIP_NO_ERROR); + EXPECT_EQ(logic_different_endpoint.GetDefaultOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + // Test setting back to null, this should clear the persisted value + testVal = DataModel::NullNullable; + EXPECT_EQ(logic.SetDefaultOpenDuration(testVal), CHIP_NO_ERROR); + EXPECT_FALSE(storageDelegate.HasKey(keyName.KeyName())); + + // Check that the value is not loaded when a new logic cluster is created + ClusterLogic logic_same_endpoint_again = ClusterLogic(delegate, context); + EXPECT_EQ(logic_same_endpoint_again.Init(conformance), CHIP_NO_ERROR); + EXPECT_EQ(logic_same_endpoint_again.GetDefaultOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + // Test that the calling function fails when write fails are on + storageDelegate.SetRejectWrites(true); + testVal.SetNonNull(12u); + EXPECT_EQ(logic.SetDefaultOpenDuration(testVal), CHIP_ERROR_PERSISTED_STORAGE_FAILED); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenLevel) +{ + TestDelegate delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + uint8_t val8; + + // Setting this value before initialization should fail + uint8_t testVal = 5u; + EXPECT_EQ(logic.SetDefaultOpenLevel(testVal), CHIP_ERROR_INCORRECT_STATE); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + EXPECT_EQ(logic.GetDefaultOpenLevel(val8), CHIP_NO_ERROR); + EXPECT_EQ(val8, 100u); + + // Lowest possible value + testVal = 1u; + EXPECT_EQ(logic.SetDefaultOpenLevel(testVal), CHIP_NO_ERROR); + + // Ensure the value is accessible via the API + EXPECT_EQ(logic.GetDefaultOpenLevel(val8), CHIP_NO_ERROR); + EXPECT_EQ(val8, testVal); + + // Ensure the value is persisted in the test storage + StorageKeyName keyName = DefaultStorageKeyAllocator::VCCDefaultOpenLevel(endpoint); + uint8_t persistedValue; + uint16_t size = static_cast(sizeof(persistedValue)); + EXPECT_TRUE(storageDelegate.HasKey(keyName.KeyName())); + EXPECT_EQ(storageDelegate.SyncGetKeyValue(keyName.KeyName(), &persistedValue, size), CHIP_NO_ERROR); + EXPECT_EQ(persistedValue, testVal); + // Test that this doesn't exist for other endpoints. Check 1. + EXPECT_FALSE(storageDelegate.HasKey(DefaultStorageKeyAllocator::VCCDefaultOpenLevel(1).KeyName())); + + // Highest possible value + testVal = 100u; + EXPECT_EQ(logic.SetDefaultOpenLevel(testVal), CHIP_NO_ERROR); + EXPECT_EQ(logic.GetDefaultOpenLevel(val8), CHIP_NO_ERROR); + EXPECT_EQ(val8, testVal); + + EXPECT_TRUE(storageDelegate.HasKey(keyName.KeyName())); + EXPECT_EQ(storageDelegate.SyncGetKeyValue(keyName.KeyName(), &persistedValue, size), CHIP_NO_ERROR); + EXPECT_EQ(persistedValue, testVal); + + uint8_t outOfRangeVal = 0u; + EXPECT_EQ(logic.SetDefaultOpenLevel(outOfRangeVal), CHIP_ERROR_INVALID_ARGUMENT); + outOfRangeVal = 101u; + EXPECT_EQ(logic.SetDefaultOpenLevel(outOfRangeVal), CHIP_ERROR_INVALID_ARGUMENT); + + // Ensure the value wasn't changed + EXPECT_EQ(logic.GetDefaultOpenLevel(val8), CHIP_NO_ERROR); + EXPECT_EQ(val8, testVal); + + EXPECT_TRUE(storageDelegate.HasKey(keyName.KeyName())); + EXPECT_EQ(storageDelegate.SyncGetKeyValue(keyName.KeyName(), &persistedValue, size), CHIP_NO_ERROR); + EXPECT_EQ(persistedValue, testVal); + + // Set Non-default value + testVal = 12u; + EXPECT_EQ(logic.SetDefaultOpenLevel(testVal), CHIP_NO_ERROR); + // Test that firing up a new logic cluster on the same endpoint loads the value from persisted storage + ClusterLogic logic_same_endpoint = ClusterLogic(delegate, context); + EXPECT_EQ(logic_same_endpoint.Init(conformance), CHIP_NO_ERROR); + EXPECT_EQ(logic_same_endpoint.GetDefaultOpenLevel(val8), CHIP_NO_ERROR); + EXPECT_EQ(val8, testVal); + + // Test that a new logic cluster on a different endpoint does not load the value + MatterContext context_ep1 = MatterContext(1, storageDelegate); + ClusterLogic logic_different_endpoint = ClusterLogic(delegate, context_ep1); + EXPECT_EQ(logic_different_endpoint.Init(conformance), CHIP_NO_ERROR); + EXPECT_EQ(logic_different_endpoint.GetDefaultOpenLevel(val8), CHIP_NO_ERROR); + EXPECT_EQ(val8, 100u); + + // Test that the calling function fails when write fails are on + storageDelegate.SetRejectWrites(true); + testVal = 15u; + EXPECT_EQ(logic.SetDefaultOpenLevel(testVal), CHIP_ERROR_PERSISTED_STORAGE_FAILED); + storageDelegate.SetRejectWrites(false); + + // Test that we get an error if this attribute is not supported + ClusterLogic logic_no_level = ClusterLogic(delegate, context); + ClusterConformance conformance_no_level = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = false, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic_no_level.Init(conformance_no_level), CHIP_NO_ERROR); + EXPECT_EQ(logic_no_level.SetDefaultOpenLevel(testVal), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenLevelWithLevelStep) +{ + TestDelegate delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + uint8_t val8; + uint8_t testVal; + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + ClusterState state = ClusterState(); + state.levelStep = 45; + EXPECT_EQ(logic.Init(conformance, state), CHIP_NO_ERROR); + + // Default is 100 + // 45, 90 and 100 should work, others should fail. + testVal = 45u; + EXPECT_EQ(logic.SetDefaultOpenLevel(testVal), CHIP_NO_ERROR); + EXPECT_EQ(logic.GetDefaultOpenLevel(val8), CHIP_NO_ERROR); + EXPECT_EQ(val8, testVal); + + testVal = 90u; + EXPECT_EQ(logic.SetDefaultOpenLevel(testVal), CHIP_NO_ERROR); + EXPECT_EQ(logic.GetDefaultOpenLevel(val8), CHIP_NO_ERROR); + EXPECT_EQ(val8, testVal); + + testVal = 100u; + EXPECT_EQ(logic.SetDefaultOpenLevel(testVal), CHIP_NO_ERROR); + EXPECT_EQ(logic.GetDefaultOpenLevel(val8), CHIP_NO_ERROR); + EXPECT_EQ(val8, testVal); + + // Test a value that's not in the level step and ensure the value is not changed. + EXPECT_EQ(logic.SetDefaultOpenLevel(33u), CHIP_ERROR_INVALID_ARGUMENT); + EXPECT_EQ(logic.GetDefaultOpenLevel(val8), CHIP_NO_ERROR); + EXPECT_EQ(val8, testVal); +} + } // namespace ValveConfigurationAndControl } // namespace Clusters } // namespace app diff --git a/src/lib/support/DefaultStorageKeyAllocator.h b/src/lib/support/DefaultStorageKeyAllocator.h index 9ed8a2f56cfd77..383e96fce30f76 100644 --- a/src/lib/support/DefaultStorageKeyAllocator.h +++ b/src/lib/support/DefaultStorageKeyAllocator.h @@ -256,6 +256,13 @@ class DefaultStorageKeyAllocator // when new fabric is created, this list needs to be updated, // when client init DefaultICDClientStorage, this table needs to be loaded. static StorageKeyName ICDFabricList() { return StorageKeyName::FromConst("g/icdfl"); } + + // Valve configuration and control + static StorageKeyName VCCDefaultOpenDuration(EndpointId endpoint) + { + return StorageKeyName::Formatted("g/vcc/dod/%x", endpoint); + } + static StorageKeyName VCCDefaultOpenLevel(EndpointId endpoint) { return StorageKeyName::Formatted("g/vcc/dol/%x", endpoint); } }; } // namespace chip From 6d4c3667b71780533d534f3fd3e9357820b48a92 Mon Sep 17 00:00:00 2001 From: Cecille Freeman Date: Sun, 8 Sep 2024 12:51:04 -0400 Subject: [PATCH 03/31] Valve: Disco ball - Open command Sepearate delegates for level control and non-level control valves, add unit tests and handlers. --- ...onfiguration-and-control-cluster-logic.cpp | 132 +++++ ...-configuration-and-control-cluster-logic.h | 17 +- ...valve-configuration-and-control-delegate.h | 92 +++ .../TestValveConfigurationAndControl.cpp | 532 +++++++++++++++++- 4 files changed, 759 insertions(+), 14 deletions(-) diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp index fcdb686bc520e9..e47a6caf710e1d 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp @@ -174,6 +174,138 @@ CHIP_ERROR ClusterLogic::ClearValveFault(const ValveFaultBitmap valveFault) return CHIP_ERROR_NOT_IMPLEMENTED; } +CHIP_ERROR ClusterLogic::GetRealTargetLevel(const std::optional & targetLevel, Percent & realTargetLevel) +{ + if (!targetLevel.has_value()) + { + if (mConformance.supportsDefaultOpenLevel) + { + realTargetLevel = mState.defaultOpenLevel; + return CHIP_NO_ERROR; + } + realTargetLevel = 100u; + return CHIP_NO_ERROR; + } + // targetLevel has a value + VerifyOrReturnError(ValueCompliesWithLevelStep(targetLevel.value()), CHIP_ERROR_INVALID_ARGUMENT); + realTargetLevel = targetLevel.value(); + return CHIP_NO_ERROR; +} + +CHIP_ERROR ClusterLogic::HandleOpenLevel(const std::optional & targetLevel) +{ + // This function should only be called for devices that support the level feature. + VerifyOrReturnError(mConformance.HasFeature(Feature::kLevel), CHIP_ERROR_INTERNAL); + + Percent realTargetLevel; + Percent returnedCurrentLevel = 0; + BitMask returnedValveFault = 0; + ReturnErrorOnFailure(GetRealTargetLevel(targetLevel, realTargetLevel)); + + CHIP_ERROR err = mClusterDriver.HandleOpenValve(realTargetLevel, returnedCurrentLevel, returnedValveFault); + + if (mConformance.supportsValveFault) + { + mState.valveFault = returnedValveFault; + } + if (err != CHIP_NO_ERROR) + { + return err; + } + if (returnedCurrentLevel == realTargetLevel) + { + mState.targetLevel = DataModel::NullNullable; + mState.currentLevel = realTargetLevel; + mState.targetState = DataModel::NullNullable; + mState.currentState = ValveStateEnum::kOpen; + } + else + { + mState.targetLevel = realTargetLevel; + mState.currentLevel = returnedCurrentLevel; + mState.targetState.SetNonNull(ValveStateEnum::kOpen); + mState.currentState.SetNonNull(ValveStateEnum::kTransitioning); + // TODO: Need to start a timer to continue querying the device for updates + } + return CHIP_NO_ERROR; +} + +CHIP_ERROR ClusterLogic::HandleOpenNoLevel() +{ + // This function should only be called for devices that do not support the level feature. + VerifyOrReturnError(!mConformance.HasFeature(Feature::kLevel), CHIP_ERROR_INTERNAL); + + ValveStateEnum returnedState = ValveStateEnum::kUnknownEnumValue; + BitMask returnedValveFault = 0; + + CHIP_ERROR err = mClusterDriver.HandleOpenValve(returnedState, returnedValveFault); + if (mConformance.supportsValveFault) + { + mState.valveFault = returnedValveFault; + } + if (err != CHIP_NO_ERROR) + { + return err; + } + if (returnedState == ValveStateEnum::kOpen) + { + mState.targetLevel.SetNull(); + mState.currentState.SetNonNull(ValveStateEnum::kOpen); + } + else + { + mState.targetState.SetNonNull(ValveStateEnum::kOpen); + mState.currentState.SetNonNull(ValveStateEnum::kTransitioning); + // TODO: Need to start a timer to continue querying the device for updates + } + return CHIP_NO_ERROR; +} + +CHIP_ERROR ClusterLogic::HandleOpenCommand(std::optional> openDuration, + std::optional targetLevel) +{ + // openDuration + // - if this is omitted, fall back to defaultOpenDuration + // - if this is NULL, remaining duration is NULL + // - if this is a value, use that value + // - if remaining duration is not null and TS is supported, set the autoCloseTime as appropriate + // targetLevel + // - if LVL is not supported + // - if this is omitted, that's correct + // - if this is supplied return error + // - if LVL is supported + // - if this value is not supplied, use defaultOpenLevel if supported, otherwise 100 + // - if this value is supplied, check against levelStep, error if not OK, otherwise set targetLevel + VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); + + DataModel::Nullable realOpenDuration; + if (openDuration.has_value()) + { + realOpenDuration = openDuration.value(); + } + else + { + realOpenDuration = mState.defaultOpenDuration; + } + + if (!mConformance.HasFeature(Feature::kLevel) && targetLevel.has_value()) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + if (mConformance.HasFeature(Feature::kLevel)) + { + ReturnErrorOnFailure(HandleOpenLevel(targetLevel)); + } + else + { + ReturnErrorOnFailure(HandleOpenNoLevel()); + } + + mState.openDuration = realOpenDuration; + return CHIP_NO_ERROR; +} + } // namespace ValveConfigurationAndControl } // namespace Clusters } // namespace app diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h index 4c213ceec12eec..b44e485d9ee16f 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h @@ -75,7 +75,7 @@ class ClusterLogic public: // Instantiates a ClusterLogic class. The caller maintains ownership of the driver and the context, but provides them for use by // the ClusterLogic class. - ClusterLogic(Delegate & clusterDriver, MatterContext & matterContext) : + ClusterLogic(DelegateBase & clusterDriver, MatterContext & matterContext) : mClusterDriver(clusterDriver), mMatterContext(matterContext) { // TODO: remove these once the fields are used properly @@ -131,12 +131,25 @@ class ClusterLogic // Current state // Current level + // Return CHIP_ERROR_INCORRECT_STATE if the class has not been initialized. + // Return CHIP_ERROR_INVALID_ARGUMENT if the input values are out is out of range or the targetLevel is supplied when LVL is not + // supported. + // Calls delegate HandleOpen function after validating the parameters + CHIP_ERROR HandleOpenCommand(std::optional> openDuration, std::optional targetLevel); + private: // Determines if the level value is allowed per the level step. bool ValueCompliesWithLevelStep(const uint8_t value); + // Returns the target level to send to the delegate based on the targetLevel command field, the device conformance and the + // defaults. Returns error if the supplied target level is invalid. + CHIP_ERROR GetRealTargetLevel(const std::optional & targetLevel, Percent & realTargetLevel); + // Internal function call to handle open commands for devices that support the LVL feature. + CHIP_ERROR HandleOpenLevel(const std::optional & targetLevel); + // Internal function call to handle open commands for devices that do not support the LVL feature. + CHIP_ERROR HandleOpenNoLevel(); bool mInitialized = false; - Delegate & mClusterDriver; + DelegateBase & mClusterDriver; MatterContext & mMatterContext; ClusterConformance mConformance; diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-delegate.h b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-delegate.h index b0d3e5fd14a90d..ed29e3bae19f18 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-delegate.h +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-delegate.h @@ -31,6 +31,15 @@ namespace ValveConfigurationAndControl { /** @brief * Defines methods for implementing application-specific logic for the Valve Configuration and Control Cluster. */ + +// TODO: Change this to a level delegate and a non-level delegate. +enum DelegateType +{ + kBase, + kLevel, + kNonLevel, +}; + class Delegate { public: @@ -44,6 +53,89 @@ class Delegate virtual ~Delegate() = default; }; +class DelegateBase +{ +public: + DelegateBase(){}; + virtual ~DelegateBase() = default; + + // TODO: Remove these + virtual DataModel::Nullable HandleOpenValve(DataModel::Nullable level) + { + return DataModel::NullNullable; + } + virtual void HandleRemainingDurationTick(uint32_t duration) {} + + // This delegate function will be called only for valve implementations that support the LVL feature. + // Delegates for valves that do not support the LVL feature return CHIP_ERROR_NOT_IMPLEMENTED. + // When this function is called, the delegate should set the valve to the target level, or begin the async process of opening + // the valve to the desired level. + // If the valve is able to be opened (success) + // - the delegate should set currentLevel + // - If the valve is fully open to the target level, currentLevel should be set to the targetLevel + // - If the valve is not fully opened, the delegate should set the currentLevel to the current valve level and the caller + // will continue to query the valve level until the target level is reached or the valve is closed. + // - A valve fault may be returned even if the Open command is successful, if the fault did not prevent the valve from safely + // opening + // - return CHIP_NO_ERROR + // If the valve cannot be safely opened (failure) + // - The delegate should set the valveFault parameter to indicate the reason for the failure (if applicable) + // - The delegate should return a CHIP_ERROR_INTERNAL + virtual CHIP_ERROR HandleOpenValve(const Percent targetLevel, Percent & currentLevel, + BitMask & valveFault) = 0; + + // This delegate function will be called only for valve implementations that DO NOT support the LVL feature. + // Delegates for valves that support the LVL feature should return CHIP_ERROR_NOT_IMPLEMENTED. + // When this function is called, the delegate should open the valve, or begin the async process of opening the valve. + // If the valve is able to be opened (success) + // - If the valve is fully open, currentState should be set to kOpen + // - If the valve is not fully opened, the delegate should set the currentLevel to the current valve level and the caller + // will continue to query the valve level until the target level is reached or the valve is closed. + // - A valve fault may be returned even if the Open command is successful, if the fault did not prevent the valve from safely + // opening + // - return CHIP_NO_ERROR + // If the valve cannot be safely opened (failure) + // - The delegate should set the valveFault parameter to indicate the reason for the failure (if applicable) + // - The delegate should return a CHIP_ERROR_INTERNAL + virtual CHIP_ERROR HandleOpenValve(ValveStateEnum & currentState, BitMask & valveFault) = 0; + + // This delegate function will be called only for valve implementations that support the LVL feature. + virtual Percent GetCurrentValveLevel() = 0; + // This delegate function will be called only for valve implementations that do not support the LVL feature. + virtual ValveStateEnum GetCurrentValveState() = 0; + virtual CHIP_ERROR HandleCloseValve() = 0; + virtual DelegateType GetDelegateType() { return DelegateType::kBase; }; +}; + +class LevelControlDelegate : public DelegateBase +{ + virtual CHIP_ERROR HandleOpenValve(const Percent targetLevel, Percent & currentLevel, + BitMask & valveFault) = 0; + + virtual Percent GetCurrentValveLevel() = 0; + + // Final overrides - the driver should not implement these classes + CHIP_ERROR HandleOpenValve(ValveStateEnum & currentState, BitMask & valveFault) final + { + return CHIP_ERROR_NOT_IMPLEMENTED; + } + ValveStateEnum GetCurrentValveState() final { return ValveStateEnum::kUnknownEnumValue; } + DelegateType GetDelegateType() final { return DelegateType::kLevel; }; +}; + +class NonLevelControlDelegate : public DelegateBase +{ + virtual CHIP_ERROR HandleOpenValve(ValveStateEnum & currentState, BitMask & valveFault) = 0; + virtual ValveStateEnum GetCurrentValveState() = 0; + + CHIP_ERROR HandleOpenValve(const Percent targetLevel, Percent & currentLevel, BitMask & valveFault) final + { + return CHIP_ERROR_NOT_IMPLEMENTED; + } + Percent GetCurrentValveLevel() final { return 0; } + DelegateType GetDelegateType() final { return DelegateType::kNonLevel; }; +}; + } // namespace ValveConfigurationAndControl } // namespace Clusters } // namespace app diff --git a/src/app/tests/TestValveConfigurationAndControl.cpp b/src/app/tests/TestValveConfigurationAndControl.cpp index 0b6f08cf74f76b..65558d67ade3eb 100644 --- a/src/app/tests/TestValveConfigurationAndControl.cpp +++ b/src/app/tests/TestValveConfigurationAndControl.cpp @@ -36,16 +36,96 @@ class TestValveConfigurationAndControlClusterLogic : public ::testing::Test static void TearDownTestSuite() { Platform::MemoryShutdown(); } }; -class TestDelegate : public Delegate +class TestDelegateLevel : public LevelControlDelegate { public: - TestDelegate() {} - DataModel::Nullable HandleOpenValve(DataModel::Nullable level) override + TestDelegateLevel() {} + CHIP_ERROR HandleOpenValve(const Percent targetLevel, Percent & currentLevel, BitMask & valveFault) override { - return DataModel::NullNullable; + lastRequestedLevel = targetLevel; + ++numHandleOpenValveCalls; + if (simulateAsyncOpen) + { + currentLevel = targetLevel / 2; + } + else + { + currentLevel = targetLevel; + } + level = currentLevel; + target = targetLevel; + if (simulateFailure || simulateValveFaultNoFailure) + { + valveFault = simulatedFailureBitMask; + } + if (simulateFailure) + { + return CHIP_ERROR_INTERNAL; + } + return CHIP_NO_ERROR; } + Percent GetCurrentValveLevel() override + { + // TODO: maybe want to ramp this. + level = target; + return level; + } + + BitMask simulatedFailureBitMask = + BitMask(to_underlying(ValveFaultBitmap::kBlocked) | to_underlying(ValveFaultBitmap::kGeneralFault)); + CHIP_ERROR HandleCloseValve() override { return CHIP_NO_ERROR; } + Percent lastRequestedLevel = 0; + bool simulateFailure = false; + bool simulateValveFaultNoFailure = false; + bool simulateAsyncOpen = false; + int numHandleOpenValveCalls = 0; + Percent level = 0; + Percent target = 0; +}; + +class TestDelegateNoLevel : public NonLevelControlDelegate +{ +public: + TestDelegateNoLevel() {} + CHIP_ERROR HandleOpenValve(ValveStateEnum & currentState, BitMask & valveFault) override + { + ++numHandleOpenValveCalls; + if (simulateAsyncOpen) + { + currentState = ValveStateEnum::kTransitioning; + } + else + { + currentState = ValveStateEnum::kOpen; + } + state = currentState; + target = ValveStateEnum::kOpen; + if (simulateFailure || simulateValveFaultNoFailure) + { + valveFault = simulatedFailureBitMask; + } + if (simulateFailure) + { + return CHIP_ERROR_INTERNAL; + } + return CHIP_NO_ERROR; + } + ValveStateEnum GetCurrentValveState() override + { + // Might want to have called more than one time before hitting the target. + state = target; + return state; + } + + BitMask simulatedFailureBitMask = + BitMask(to_underlying(ValveFaultBitmap::kBlocked) | to_underlying(ValveFaultBitmap::kGeneralFault)); CHIP_ERROR HandleCloseValve() override { return CHIP_NO_ERROR; } - void HandleRemainingDurationTick(uint32_t duration) override {} + bool simulateFailure = false; + bool simulateValveFaultNoFailure = false; + bool simulateAsyncOpen = false; + int numHandleOpenValveCalls = 0; + ValveStateEnum state = ValveStateEnum::kUnknownEnumValue; + ValveStateEnum target = ValveStateEnum::kUnknownEnumValue; }; TEST_F(TestValveConfigurationAndControlClusterLogic, TestConformanceValid) @@ -117,7 +197,7 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestConformanceValid) TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesAllFeatures) { - TestDelegate delegate; + TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; MatterContext context(0, storageDelegate); ClusterLogic logic(delegate, context); @@ -173,7 +253,7 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesAllFeature TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesNoFeatures) { - TestDelegate delegate; + TestDelegateNoLevel delegate; TestPersistentStorageDelegate storageDelegate; MatterContext context(0, storageDelegate); ClusterLogic logic(delegate, context); @@ -222,7 +302,7 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesNoFeatures TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesStartingState) { - TestDelegate delegate; + TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; MatterContext context(0, storageDelegate); ClusterLogic logic(delegate, context); @@ -291,7 +371,7 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesStartingSt TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesUninitialized) { - TestDelegate delegate; + TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; MatterContext context(0, storageDelegate); ClusterLogic logic(delegate, context); @@ -319,7 +399,7 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesUninitiali TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenDuration) { - TestDelegate delegate; + TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; MatterContext context(endpoint, storageDelegate); @@ -411,7 +491,7 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenDuration) TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenLevel) { - TestDelegate delegate; + TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; MatterContext context(endpoint, storageDelegate); @@ -507,7 +587,7 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenLevel) TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenLevelWithLevelStep) { - TestDelegate delegate; + TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; MatterContext context(endpoint, storageDelegate); @@ -547,6 +627,434 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenLevelWith EXPECT_EQ(val8, testVal); } +TEST_F(TestValveConfigurationAndControlClusterLogic, TestWrongDelegates) +{ + TestDelegateLevel delegateLevel; + TestDelegateNoLevel delegateNoLevel; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MatterContext context(endpoint, storageDelegate); + ClusterLogic logicLevel(delegateLevel, context); + ClusterLogic logicNoLevel(delegateNoLevel, context); + ClusterConformance conformanceLevel = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + ClusterConformance conformanceNoLevel = { + .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = false, .supportsLevelStep = false + }; + + EXPECT_EQ(logicLevel.Init(conformanceNoLevel), CHIP_ERROR_INVALID_ARGUMENT); + EXPECT_EQ(logicNoLevel.Init(conformanceLevel), CHIP_ERROR_INVALID_ARGUMENT); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenDuration) +{ + + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + DataModel::Nullable valElapsedSNullable; + + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetDefaultOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + // Fall back to default + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + DataModel::Nullable defaultOpenDuration; + defaultOpenDuration.SetNonNull(12u); + EXPECT_EQ(logic.SetDefaultOpenDuration(defaultOpenDuration), CHIP_NO_ERROR); + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, defaultOpenDuration); + + // Set from command parameters + DataModel::Nullable openDuration; + openDuration.SetNull(); + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + openDuration.SetNonNull(12u); + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable.ValueOr(0), 12u); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenTargetLevelFeatureUnsupported) +{ + TestDelegateNoLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { + .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = false, .supportsLevelStep = false + }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(delegate.numHandleOpenValveCalls, 1); + + // Should get an error when this is called with target level set since the feature is unsupported. + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::make_optional(50u)), CHIP_ERROR_INVALID_ARGUMENT); + EXPECT_EQ(delegate.numHandleOpenValveCalls, 1); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenTargetLevelNotSuppliedNoDefaultSupported) +{ + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel), + .supportsDefaultOpenLevel = false, + .supportsValveFault = false, + .supportsLevelStep = false }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(delegate.lastRequestedLevel, 100u); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenTargetLevelNotSuppliedDefaultSupported) +{ + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel), + .supportsDefaultOpenLevel = true, + .supportsValveFault = false, + .supportsLevelStep = false }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(delegate.lastRequestedLevel, 100u); + + EXPECT_EQ(logic.SetDefaultOpenLevel(50u), CHIP_NO_ERROR); + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(delegate.lastRequestedLevel, 50u); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenTargetLevelSupplied) +{ + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel), + .supportsDefaultOpenLevel = true, + .supportsValveFault = false, + .supportsLevelStep = true }; + ClusterState state = ClusterState(); + state.levelStep = 33; + EXPECT_EQ(logic.Init(conformance, state), CHIP_NO_ERROR); + + // 33, 66, 99 and 100 should all work, nothing else should + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::make_optional(33u)), CHIP_NO_ERROR); + EXPECT_EQ(delegate.lastRequestedLevel, 33u); + + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::make_optional(66u)), CHIP_NO_ERROR); + EXPECT_EQ(delegate.lastRequestedLevel, 66u); + + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::make_optional(99u)), CHIP_NO_ERROR); + EXPECT_EQ(delegate.lastRequestedLevel, 99u); + + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::make_optional(100u)), CHIP_NO_ERROR); + EXPECT_EQ(delegate.lastRequestedLevel, 100u); + EXPECT_EQ(delegate.numHandleOpenValveCalls, 4); + + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::make_optional(32u)), CHIP_ERROR_INVALID_ARGUMENT); + // Ensure this wasn't called again. + EXPECT_EQ(delegate.lastRequestedLevel, 100u); + EXPECT_EQ(delegate.numHandleOpenValveCalls, 4); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenImmediateLevelSupported) +{ + // Testing that current level, target level, target state and current state are set correctly + // If the delegate is able to open the valve fully during the handler call + // then the current level and current state should indicate open and the appropriate level. + // TargetLevel and TargetState should be NULL. + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + delegate.simulateFailure = false; + delegate.simulateAsyncOpen = false; + + DataModel::Nullable level; + DataModel::Nullable state; + uint8_t targetLevel; + + targetLevel = 100u; + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::make_optional(targetLevel)), CHIP_NO_ERROR); + EXPECT_EQ(logic.GetCurrentLevel(level), CHIP_NO_ERROR); + EXPECT_EQ(level.ValueOr(0), targetLevel); + EXPECT_EQ(logic.GetTargetLevel(level), CHIP_NO_ERROR); + EXPECT_EQ(level, DataModel::NullNullable); + EXPECT_EQ(logic.GetCurrentState(state), CHIP_NO_ERROR); + EXPECT_EQ(state.ValueOr(ValveStateEnum::kUnknownEnumValue), ValveStateEnum::kOpen); + EXPECT_EQ(logic.GetTargetState(state), CHIP_NO_ERROR); + EXPECT_EQ(state, DataModel::NullNullable); + + targetLevel = 50u; + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::make_optional(targetLevel)), CHIP_NO_ERROR); + EXPECT_EQ(logic.GetCurrentLevel(level), CHIP_NO_ERROR); + EXPECT_EQ(level.ValueOr(0), targetLevel); + EXPECT_EQ(logic.GetTargetLevel(level), CHIP_NO_ERROR); + EXPECT_EQ(level, DataModel::NullNullable); + EXPECT_EQ(logic.GetCurrentState(state), CHIP_NO_ERROR); + EXPECT_EQ(state.ValueOr(ValveStateEnum::kUnknownEnumValue), ValveStateEnum::kOpen); + EXPECT_EQ(logic.GetTargetState(state), CHIP_NO_ERROR); + EXPECT_EQ(state, DataModel::NullNullable); + + EXPECT_EQ(delegate.numHandleOpenValveCalls, 2); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenImmediateLevelNotSupported) +{ + TestDelegateNoLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { + .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = true, .supportsLevelStep = false + }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + delegate.simulateFailure = false; + delegate.simulateAsyncOpen = false; + + DataModel::Nullable state; + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(logic.GetCurrentState(state), CHIP_NO_ERROR); + EXPECT_EQ(state.ValueOr(ValveStateEnum::kUnknownEnumValue), ValveStateEnum::kOpen); + EXPECT_EQ(logic.GetTargetState(state), CHIP_NO_ERROR); + EXPECT_EQ(state, DataModel::NullNullable); + + EXPECT_EQ(delegate.numHandleOpenValveCalls, 1); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenAsyncLevelSupported) +{ + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + delegate.simulateFailure = false; + delegate.simulateAsyncOpen = true; + + DataModel::Nullable level; + DataModel::Nullable state; + uint8_t targetLevel; + + targetLevel = 100u; + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::make_optional(targetLevel)), CHIP_NO_ERROR); + EXPECT_EQ(logic.GetCurrentLevel(level), CHIP_NO_ERROR); + EXPECT_EQ(level.ValueOr(0), targetLevel / 2); + EXPECT_EQ(logic.GetTargetLevel(level), CHIP_NO_ERROR); + EXPECT_EQ(level.ValueOr(0), targetLevel); + EXPECT_EQ(logic.GetCurrentState(state), CHIP_NO_ERROR); + EXPECT_EQ(state.ValueOr(ValveStateEnum::kUnknownEnumValue), ValveStateEnum::kTransitioning); + EXPECT_EQ(logic.GetTargetState(state), CHIP_NO_ERROR); + EXPECT_EQ(state.ValueOr(ValveStateEnum::kUnknownEnumValue), ValveStateEnum::kOpen); + + targetLevel = 50u; + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::make_optional(targetLevel)), CHIP_NO_ERROR); + EXPECT_EQ(logic.GetCurrentLevel(level), CHIP_NO_ERROR); + EXPECT_EQ(level.ValueOr(0), targetLevel / 2); + EXPECT_EQ(logic.GetTargetLevel(level), CHIP_NO_ERROR); + EXPECT_EQ(level.ValueOr(0), targetLevel); + EXPECT_EQ(logic.GetCurrentState(state), CHIP_NO_ERROR); + EXPECT_EQ(state.ValueOr(ValveStateEnum::kUnknownEnumValue), ValveStateEnum::kTransitioning); + EXPECT_EQ(logic.GetTargetState(state), CHIP_NO_ERROR); + EXPECT_EQ(state.ValueOr(ValveStateEnum::kUnknownEnumValue), ValveStateEnum::kOpen); + + EXPECT_EQ(delegate.numHandleOpenValveCalls, 2); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenAsyncLevelNotSupported) +{ + TestDelegateNoLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { + .featureMap = 0, + .supportsDefaultOpenLevel = false, + .supportsValveFault = false, + .supportsLevelStep = false, + }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + delegate.simulateFailure = false; + delegate.simulateAsyncOpen = true; + + DataModel::Nullable state; + + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(logic.GetCurrentState(state), CHIP_NO_ERROR); + EXPECT_EQ(state.ValueOr(ValveStateEnum::kUnknownEnumValue), ValveStateEnum::kTransitioning); + EXPECT_EQ(logic.GetTargetState(state), CHIP_NO_ERROR); + EXPECT_EQ(state.ValueOr(ValveStateEnum::kUnknownEnumValue), ValveStateEnum::kOpen); + + EXPECT_EQ(delegate.numHandleOpenValveCalls, 1); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturnedNoErrorLevel) +{ + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + BitMask valveFault; + + delegate.simulateValveFaultNoFailure = true; + delegate.simulatedFailureBitMask = BitMask(to_underlying(ValveFaultBitmap::kLeaking)); + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(delegate.numHandleOpenValveCalls, 1); + EXPECT_EQ(logic.GetValveFault(valveFault), CHIP_NO_ERROR); + EXPECT_EQ(valveFault, delegate.simulatedFailureBitMask); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturnedNoErrorNoLevel) +{ + TestDelegateNoLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { + .featureMap = 0, + .supportsDefaultOpenLevel = false, + .supportsValveFault = true, + .supportsLevelStep = false, + }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + BitMask valveFault; + + delegate.simulateValveFaultNoFailure = true; + delegate.simulatedFailureBitMask = BitMask(to_underlying(ValveFaultBitmap::kLeaking)); + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(delegate.numHandleOpenValveCalls, 1); + EXPECT_EQ(logic.GetValveFault(valveFault), CHIP_NO_ERROR); + EXPECT_EQ(valveFault, delegate.simulatedFailureBitMask); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturnedErrorLevel) +{ + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + BitMask valveFault; + + delegate.simulateFailure = true; + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_ERROR_INTERNAL); + EXPECT_EQ(delegate.numHandleOpenValveCalls, 1); + EXPECT_EQ(logic.GetValveFault(valveFault), CHIP_NO_ERROR); + EXPECT_EQ(valveFault, delegate.simulatedFailureBitMask); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturnedErrorNoLevel) +{ + TestDelegateNoLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MatterContext context = MatterContext(endpoint, storageDelegate); + ClusterLogic logic = ClusterLogic(delegate, context); + + ClusterConformance conformance = { + .featureMap = 0, + .supportsDefaultOpenLevel = false, + .supportsValveFault = true, + .supportsLevelStep = false, + }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + BitMask valveFault; + + delegate.simulateFailure = true; + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_ERROR_INTERNAL); + EXPECT_EQ(delegate.numHandleOpenValveCalls, 1); + EXPECT_EQ(logic.GetValveFault(valveFault), CHIP_NO_ERROR); + EXPECT_EQ(valveFault, delegate.simulatedFailureBitMask); +} + +// Clock::ClockBase * const savedClock = &SystemClock(); +// Clock::Internal::MockClock mockClock; +// Clock::Internal::SetSystemClockForTesting(&mockClock); + +// Test that the cluster logic doesn't balk if error is returned, but valve fault isn't supported +// Test that the auto close time is set appropriately for open duration - add in prior test? +// Test setter for valve fault +// Test attribute change notifications are sent out and not sent out when the attribute is change do the same value - add in prior +// tests? Test events are generated + +// Init providing the wrong delegate type + +// TODO: Should the cluster logic query the current level and curent state from the driver at startup? + } // namespace ValveConfigurationAndControl } // namespace Clusters } // namespace app From 30e6fd14177114373956859e3ddb6d6fd67bb735 Mon Sep 17 00:00:00 2001 From: cecille Date: Wed, 11 Sep 2024 17:45:24 -0400 Subject: [PATCH 04/31] Valve: Disco ball - start of Remaining duration with Q --- .../QuieterReporting.h | 2 + ...onfiguration-and-control-cluster-logic.cpp | 293 ++++++-- ...-configuration-and-control-cluster-logic.h | 80 ++- ...nfiguration-and-control-matter-context.cpp | 5 + ...configuration-and-control-matter-context.h | 5 + src/app/tests/BUILD.gn | 2 + .../TestValveConfigurationAndControl.cpp | 647 +++++++++++++++--- 7 files changed, 896 insertions(+), 138 deletions(-) diff --git a/src/app/cluster-building-blocks/QuieterReporting.h b/src/app/cluster-building-blocks/QuieterReporting.h index e64d56cd2b1c72..1b69a853d0d192 100644 --- a/src/app/cluster-building-blocks/QuieterReporting.h +++ b/src/app/cluster-building-blocks/QuieterReporting.h @@ -205,6 +205,8 @@ class QuieterReportingAttribute return isNewlyDirty ? AttributeDirtyState::kMustReport : AttributeDirtyState::kNoReportNeeded; } + chip::System::Clock::Timeout GetLastReportTime() { return mLastDirtyTimestampMillis; } + /** * Same as the other `SetValue`, but assumes a changedPredicate that never overrides to dirty. * diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp index e47a6caf710e1d..b42aa4132353d8 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp @@ -19,21 +19,25 @@ */ #include "valve-configuration-and-control-cluster-logic.h" + +#include + +#include #include +#include +#include namespace chip { namespace app { namespace Clusters { namespace ValveConfigurationAndControl { -CHIP_ERROR ClusterLogic::Init(const ClusterConformance & conformance, const ClusterState & initialState) +void ClusterStateAttributes::Init(ClusterInitParameters state) { - if (!conformance.Valid()) - { - return CHIP_ERROR_INVALID_DEVICE_DESCRIPTOR; - } - mConformance = conformance; - mState = initialState; + mState.currentLevel = state.currentLevel; + mState.currentState = state.currentState; + mState.valveFault = state.valveFault; + mState.levelStep = state.levelStep; // Overwrite default initial state with stored persisted values if set. uint32_t defaultOpenDuration; @@ -43,6 +47,152 @@ CHIP_ERROR ClusterLogic::Init(const ClusterConformance & conformance, const Clus } mMatterContext.GetDefaultOpenLevel(mState.defaultOpenLevel); + QuieterReportingPolicyFlags & policy = mState.remainingDuration.policy(); + policy = QuieterReportingPolicyFlags(to_underlying(QuieterReportingPolicyEnum::kMarkDirtyOnChangeToFromZero) | + to_underlying(QuieterReportingPolicyEnum::kMarkDirtyOnIncrement)); +} + +CHIP_ERROR ClusterStateAttributes::SetRemainingDuration(const DataModel::Nullable & remainingDuration) +{ + System::Clock::Milliseconds64 now = System::SystemClock().GetMonotonicMilliseconds64(); + AttributeDirtyState dirtyState = mState.remainingDuration.SetValue( + remainingDuration, now, mState.remainingDuration.GetPredicateForSufficientTimeSinceLastDirty(kRemainingDurationReportRate)); + if (dirtyState == AttributeDirtyState::kMustReport) + { + mMatterContext.MarkDirty(Attributes::RemainingDuration::Id); + } + return CHIP_NO_ERROR; +} + +CHIP_ERROR ClusterStateAttributes::SetOpenDuration(const DataModel::Nullable & openDuration) +{ + bool dirty = openDuration != mState.openDuration; + mState.openDuration = openDuration; + if (dirty) + { + mMatterContext.MarkDirty(Attributes::OpenDuration::Id); + } + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterStateAttributes::SetDefaultOpenDuration(const DataModel::Nullable & defaultOpenDuration) +{ + bool dirty = defaultOpenDuration != mState.defaultOpenDuration; + ReturnErrorOnFailure(mMatterContext.StoreDefaultOpenDuration(defaultOpenDuration)); + mState.defaultOpenDuration = defaultOpenDuration; + if (dirty) + { + mMatterContext.MarkDirty(Attributes::DefaultOpenDuration::Id); + } + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterStateAttributes::SetAutoCloseTime(const DataModel::Nullable & autoCloseTime) +{ + bool dirty = autoCloseTime != mState.autoCloseTime; + mState.autoCloseTime = autoCloseTime; + if (dirty) + { + mMatterContext.MarkDirty(Attributes::AutoCloseTime::Id); + } + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterStateAttributes::SetCurrentState(const DataModel::Nullable & currentState) +{ + bool dirty = currentState != mState.currentState; + mState.currentState = currentState; + if (dirty) + { + mMatterContext.MarkDirty(Attributes::CurrentState::Id); + } + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterStateAttributes::SetTargetState(const DataModel::Nullable & targetState) +{ + bool dirty = targetState != mState.targetState; + mState.targetState = targetState; + if (dirty) + { + mMatterContext.MarkDirty(Attributes::TargetState::Id); + } + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterStateAttributes::SetCurrentLevel(const DataModel::Nullable & currentLevel) +{ + // TODO: Q quality + bool dirty = currentLevel != mState.currentLevel; + mState.currentLevel = currentLevel; + if (dirty) + { + mMatterContext.MarkDirty(Attributes::CurrentLevel::Id); + } + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterStateAttributes::SetTargetLevel(const DataModel::Nullable & targetLevel) +{ + bool dirty = targetLevel != mState.targetLevel; + mState.targetLevel = targetLevel; + if (dirty) + { + mMatterContext.MarkDirty(Attributes::TargetLevel::Id); + } + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterStateAttributes::SetDefaultOpenLevel(const Percent defaultOpenLevel) +{ + bool dirty = defaultOpenLevel != mState.defaultOpenLevel; + ReturnErrorOnFailure(mMatterContext.StoreDefaultOpenLevel(defaultOpenLevel)); + mState.defaultOpenLevel = defaultOpenLevel; + if (dirty) + { + mMatterContext.MarkDirty(Attributes::DefaultOpenLevel::Id); + } + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterStateAttributes::SetValveFault(const BitMask & valveFault) +{ + bool dirty = valveFault != mState.valveFault; + mState.valveFault = valveFault; + if (dirty) + { + mMatterContext.MarkDirty(Attributes::ValveFault::Id); + } + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterStateAttributes::SetLevelStep(const uint8_t levelStep) +{ + bool dirty = levelStep != mState.levelStep; + mState.levelStep = levelStep; + if (dirty) + { + mMatterContext.MarkDirty(Attributes::LevelStep::Id); + } + return CHIP_NO_ERROR; +} + +System::Clock::Milliseconds64 ClusterStateAttributes::GetNextReportTimeForRemainingDuration() +{ + return std::chrono::duration_cast(mState.remainingDuration.GetLastReportTime()) + + kRemainingDurationReportRate; +} + +CHIP_ERROR ClusterLogic::Init(const ClusterConformance & conformance, const ClusterInitParameters & initialState) +{ + if (!conformance.Valid()) + { + return CHIP_ERROR_INVALID_DEVICE_DESCRIPTOR; + } + if (conformance.HasFeature(Feature::kLevel) && mClusterDriver.GetDelegateType() != kLevel) + { + return CHIP_ERROR_INVALID_DEVICE_DESCRIPTOR; + } + if (!conformance.HasFeature(Feature::kLevel) && mClusterDriver.GetDelegateType() != kNonLevel) + { + return CHIP_ERROR_INVALID_DEVICE_DESCRIPTOR; + } + mConformance = conformance; + + mState.Init(initialState); + mDurationStarted = System::SystemClock().GetMonotonicMilliseconds64(); + mInitialized = true; return CHIP_NO_ERROR; } @@ -54,52 +204,53 @@ CHIP_ERROR ClusterLogic::Init(const ClusterConformance & conformance, const Clus CHIP_ERROR ClusterLogic::GetOpenDuration(DataModel::Nullable & openDuration) { VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); - openDuration = mState.openDuration; + openDuration = mState.GetState().openDuration; return CHIP_NO_ERROR; } CHIP_ERROR ClusterLogic::GetDefaultOpenDuration(DataModel::Nullable & defaultOpenDuration) { VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); - defaultOpenDuration = mState.defaultOpenDuration; + defaultOpenDuration = mState.GetState().defaultOpenDuration; return CHIP_NO_ERROR; } CHIP_ERROR ClusterLogic::GetAutoCloseTime(DataModel::Nullable & autoCloseTime) { VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mConformance.HasFeature(Feature::kTimeSync), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); - autoCloseTime = mState.autoCloseTime; + autoCloseTime = mState.GetState().autoCloseTime; return CHIP_NO_ERROR; } CHIP_ERROR ClusterLogic::GetRemainingDuration(DataModel::Nullable & remainingDuration) { VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); - remainingDuration = mState.remainingDuration; + HandleUpdateRemainingDurationInternal(); + remainingDuration = mState.GetState().remainingDuration.value(); return CHIP_NO_ERROR; } CHIP_ERROR ClusterLogic::GetCurrentState(DataModel::Nullable & currentState) { VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); - currentState = mState.currentState; + currentState = mState.GetState().currentState; return CHIP_NO_ERROR; } CHIP_ERROR ClusterLogic::GetTargetState(DataModel::Nullable & targetState) { VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); - targetState = mState.targetState; + targetState = mState.GetState().targetState; return CHIP_NO_ERROR; } CHIP_ERROR ClusterLogic::GetCurrentLevel(DataModel::Nullable & currentLevel) { VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mConformance.HasFeature(Feature::kLevel), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); - currentLevel = mState.currentLevel; + currentLevel = mState.GetState().currentLevel; return CHIP_NO_ERROR; } CHIP_ERROR ClusterLogic::GetTargetLevel(DataModel::Nullable & targetLevel) { VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mConformance.HasFeature(Feature::kLevel), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); - targetLevel = mState.targetLevel; + targetLevel = mState.GetState().targetLevel; return CHIP_NO_ERROR; } CHIP_ERROR ClusterLogic::GetDefaultOpenLevel(uint8_t & defaultOpenLevel) @@ -107,14 +258,14 @@ CHIP_ERROR ClusterLogic::GetDefaultOpenLevel(uint8_t & defaultOpenLevel) VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mConformance.HasFeature(Feature::kLevel), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); VerifyOrReturnError(mConformance.supportsDefaultOpenLevel, CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); - defaultOpenLevel = mState.defaultOpenLevel; + defaultOpenLevel = mState.GetState().defaultOpenLevel; return CHIP_NO_ERROR; } CHIP_ERROR ClusterLogic::GetValveFault(BitMask & valveFault) { VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mConformance.supportsValveFault, CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); - valveFault = mState.valveFault; + valveFault = mState.GetState().valveFault; return CHIP_NO_ERROR; } CHIP_ERROR ClusterLogic::GetLevelStep(uint8_t & levelStep) @@ -122,7 +273,7 @@ CHIP_ERROR ClusterLogic::GetLevelStep(uint8_t & levelStep) VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mConformance.HasFeature(Feature::kLevel), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); VerifyOrReturnError(mConformance.supportsLevelStep, CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); - levelStep = mState.levelStep; + levelStep = mState.GetState().levelStep; return CHIP_NO_ERROR; } @@ -133,16 +284,14 @@ CHIP_ERROR ClusterLogic::SetDefaultOpenDuration(const DataModel::Nullable & targe { if (mConformance.supportsDefaultOpenLevel) { - realTargetLevel = mState.defaultOpenLevel; + realTargetLevel = mState.GetState().defaultOpenLevel; return CHIP_NO_ERROR; } realTargetLevel = 100u; @@ -206,7 +352,7 @@ CHIP_ERROR ClusterLogic::HandleOpenLevel(const std::optional & targetLe if (mConformance.supportsValveFault) { - mState.valveFault = returnedValveFault; + mState.SetValveFault(returnedValveFault); } if (err != CHIP_NO_ERROR) { @@ -214,18 +360,18 @@ CHIP_ERROR ClusterLogic::HandleOpenLevel(const std::optional & targetLe } if (returnedCurrentLevel == realTargetLevel) { - mState.targetLevel = DataModel::NullNullable; - mState.currentLevel = realTargetLevel; - mState.targetState = DataModel::NullNullable; - mState.currentState = ValveStateEnum::kOpen; + mState.SetTargetLevel(DataModel::NullNullable); + mState.SetCurrentLevel(realTargetLevel); + mState.SetTargetState(DataModel::NullNullable); + mState.SetCurrentState(ValveStateEnum::kOpen); } else { - mState.targetLevel = realTargetLevel; - mState.currentLevel = returnedCurrentLevel; - mState.targetState.SetNonNull(ValveStateEnum::kOpen); - mState.currentState.SetNonNull(ValveStateEnum::kTransitioning); - // TODO: Need to start a timer to continue querying the device for updates + mState.SetTargetLevel(realTargetLevel); + mState.SetCurrentLevel(returnedCurrentLevel); + mState.SetTargetState(DataModel::Nullable(ValveStateEnum::kOpen)); + mState.SetCurrentState(DataModel::Nullable(ValveStateEnum::kTransitioning)); + // TODO: Need to start a timer to continue querying the device for updates. Or just let the delegate handle this? } return CHIP_NO_ERROR; } @@ -241,7 +387,7 @@ CHIP_ERROR ClusterLogic::HandleOpenNoLevel() CHIP_ERROR err = mClusterDriver.HandleOpenValve(returnedState, returnedValveFault); if (mConformance.supportsValveFault) { - mState.valveFault = returnedValveFault; + mState.SetValveFault(returnedValveFault); } if (err != CHIP_NO_ERROR) { @@ -249,14 +395,14 @@ CHIP_ERROR ClusterLogic::HandleOpenNoLevel() } if (returnedState == ValveStateEnum::kOpen) { - mState.targetLevel.SetNull(); - mState.currentState.SetNonNull(ValveStateEnum::kOpen); + mState.SetTargetLevel(DataModel::NullNullable); + mState.SetCurrentState(DataModel::Nullable(ValveStateEnum::kOpen)); } else { - mState.targetState.SetNonNull(ValveStateEnum::kOpen); - mState.currentState.SetNonNull(ValveStateEnum::kTransitioning); - // TODO: Need to start a timer to continue querying the device for updates + mState.SetTargetState(DataModel::Nullable(ValveStateEnum::kOpen)); + mState.SetCurrentState(DataModel::Nullable(ValveStateEnum::kTransitioning)); + // TODO: Need to start a timer to continue querying the device for updates. Or just let the delegate handle this? } return CHIP_NO_ERROR; } @@ -285,7 +431,7 @@ CHIP_ERROR ClusterLogic::HandleOpenCommand(std::optional(0)); + mState.SetTargetLevel(DataModel::NullNullable); + mState.SetCurrentState(DataModel::Nullable(ValveStateEnum::kClosed)); + mState.SetTargetState(DataModel::NullNullable); + mState.SetAutoCloseTime(DataModel::NullNullable); +} + +void ClusterLogic::HandleUpdateRemainingDuration(System::Layer * systemLayer, void * context) +{ + auto * logic = static_cast(context); + logic->HandleUpdateRemainingDurationInternal(); +} + +void ClusterLogic::HandleUpdateRemainingDurationInternal() +{ + // Start by cancelling the timer in case this was called from a command handler + // We will start a new timer if required. + DeviceLayer::SystemLayer().CancelTimer(HandleUpdateRemainingDuration, this); + + if (mState.GetState().openDuration.IsNull()) + { + // I think this might be an error state - if openDuration is NULL, this timer shouldn't be on. + mState.SetRemainingDuration(DataModel::NullNullable); + return; + } + + // Setup a new timer to either send the next report or handle the close operation + System::Clock::Milliseconds64 now = System::SystemClock().GetMonotonicMilliseconds64(); + System::Clock::Seconds64 openDurationS = System::Clock::Seconds64(mState.GetState().openDuration.ValueOr(0)); + System::Clock::Milliseconds64 closeTimeMs = + mDurationStarted + std::chrono::duration_cast(openDurationS); + if (now >= closeTimeMs) + { + // Time's up, close the valve. Close handles setting the open and remaining duration + HandleCloseInternal(); + return; + } + System::Clock::Milliseconds64 remainingMs = closeTimeMs - now; + System::Clock::Milliseconds64 nextReportTimer = mState.GetNextReportTimeForRemainingDuration() - now; + + System::Clock::Milliseconds64 nextTimerTime = std::min(nextReportTimer, remainingMs); + DeviceLayer::SystemLayer().StartTimer(std::chrono::duration_cast(nextTimerTime), + HandleUpdateRemainingDuration, this); + + auto remainingS = std::chrono::round(remainingMs); + mState.SetRemainingDuration(DataModel::Nullable(remainingS.count())); +} + } // namespace ValveConfigurationAndControl } // namespace Clusters } // namespace app diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h index b44e485d9ee16f..247abf9a563c67 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h @@ -22,8 +22,10 @@ #include "valve-configuration-and-control-delegate.h" #include "valve-configuration-and-control-matter-context.h" +#include #include #include +#include namespace chip { namespace app { @@ -55,19 +57,56 @@ struct ClusterConformance } }; +struct ClusterInitParameters +{ + DataModel::Nullable currentState = DataModel::NullNullable; + DataModel::Nullable currentLevel = DataModel::NullNullable; + BitMask valveFault = 0u; + uint8_t levelStep = 1u; +}; struct ClusterState { - DataModel::Nullable openDuration = DataModel::NullNullable; - DataModel::Nullable defaultOpenDuration = DataModel::NullNullable; - DataModel::Nullable autoCloseTime = DataModel::NullNullable; - DataModel::Nullable remainingDuration = DataModel::NullNullable; - DataModel::Nullable currentState = DataModel::NullNullable; - DataModel::Nullable targetState = DataModel::NullNullable; - DataModel::Nullable currentLevel = DataModel::NullNullable; - DataModel::Nullable targetLevel = DataModel::NullNullable; - Percent defaultOpenLevel = 100u; - BitMask valveFault = 0u; - uint8_t levelStep = 1u; + DataModel::Nullable openDuration = DataModel::NullNullable; + DataModel::Nullable defaultOpenDuration = DataModel::NullNullable; + DataModel::Nullable autoCloseTime = DataModel::NullNullable; + QuieterReportingAttribute remainingDuration = QuieterReportingAttribute(); + DataModel::Nullable currentState = DataModel::NullNullable; + DataModel::Nullable targetState = DataModel::NullNullable; + DataModel::Nullable currentLevel = DataModel::NullNullable; + DataModel::Nullable targetLevel = DataModel::NullNullable; + Percent defaultOpenLevel = 100u; + BitMask valveFault = 0u; + uint8_t levelStep = 1u; +}; + +// Attribute sets are forced to go through this class so the attributes can be marked dirty appropriately. +// This cluster handles storage and marking dirty. The cluster logic should handle constraint and conformance checking. +class ClusterStateAttributes +{ +public: + explicit ClusterStateAttributes(MatterContext & matterContext) : mMatterContext(matterContext){}; + void Init(ClusterInitParameters initialState); + const ClusterState & GetState() { return mState; } + + CHIP_ERROR SetOpenDuration(const DataModel::Nullable & openDuration); + CHIP_ERROR SetDefaultOpenDuration(const DataModel::Nullable & defaultOpenDuration); + CHIP_ERROR SetAutoCloseTime(const DataModel::Nullable & autoCloseTime); + CHIP_ERROR SetRemainingDuration(const DataModel::Nullable & remainingDuration); + CHIP_ERROR SetCurrentState(const DataModel::Nullable & currentState); + CHIP_ERROR SetTargetState(const DataModel::Nullable & targetState); + CHIP_ERROR SetCurrentLevel(const DataModel::Nullable & currentLevel); + CHIP_ERROR SetTargetLevel(const DataModel::Nullable & targetLevel); + CHIP_ERROR SetDefaultOpenLevel(const Percent defaultOpenLevel); + CHIP_ERROR SetValveFault(const BitMask & valveFault); + CHIP_ERROR SetLevelStep(const uint8_t levelStep); + + System::Clock::Milliseconds64 GetNextReportTimeForRemainingDuration(); + +private: + const System::Clock::Milliseconds64 kRemainingDurationReportRate = + std::chrono::duration_cast(System::Clock::Seconds64(1)); + ClusterState mState; + MatterContext & mMatterContext; }; class ClusterLogic @@ -76,7 +115,7 @@ class ClusterLogic // Instantiates a ClusterLogic class. The caller maintains ownership of the driver and the context, but provides them for use by // the ClusterLogic class. ClusterLogic(DelegateBase & clusterDriver, MatterContext & matterContext) : - mClusterDriver(clusterDriver), mMatterContext(matterContext) + mClusterDriver(clusterDriver), mState(ClusterStateAttributes(matterContext)) { // TODO: remove these once the fields are used properly (void) mClusterDriver; @@ -85,7 +124,7 @@ class ClusterLogic // Validates the conformance and performs initialization. // Returns CHIP_ERROR_INCORRECT_STATE if the cluster has already been initialized. // Returns CHIP_ERROR_INVALID_DEVICE_DESCRIPTOR if the conformance is incorrect. - CHIP_ERROR Init(const ClusterConformance & conformance, const ClusterState & initialState = ClusterState()); + CHIP_ERROR Init(const ClusterConformance & conformance, const ClusterInitParameters & initialState = ClusterInitParameters()); // CHIP_ERROR HandleOpen(); // CHIP_ERROR HandleClose(); @@ -147,13 +186,22 @@ class ClusterLogic CHIP_ERROR HandleOpenLevel(const std::optional & targetLevel); // Internal function call to handle open commands for devices that do not support the LVL feature. CHIP_ERROR HandleOpenNoLevel(); - bool mInitialized = false; + + // Wrapper for the timer function. Private member so it can call private functions. + static void HandleUpdateRemainingDuration(System::Layer * systemLayer, void * context); + // internal function called by wrapper + void HandleUpdateRemainingDurationInternal(); + // Internal function called by HandleUpdateRemainingDuration to call the close function in the delegate and + // set all the attributes back to their closed state. + void HandleCloseInternal(); + + bool mInitialized = false; + System::Clock::Milliseconds64 mDurationStarted = System::Clock::Milliseconds64(0); DelegateBase & mClusterDriver; - MatterContext & mMatterContext; ClusterConformance mConformance; - ClusterState mState; + ClusterStateAttributes mState; }; } // namespace ValveConfigurationAndControl diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.cpp index fed1f25d9c1046..ea1bbdb9849f89 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.cpp +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.cpp @@ -59,6 +59,11 @@ CHIP_ERROR MatterContext::GetDefaultOpenLevel(uint8_t & returnVal) &returnVal, size); } +void MatterContext::MarkDirty(const AttributeId id) +{ + // Uh...how do we do this? +} + } // namespace ValveConfigurationAndControl } // namespace Clusters } // namespace app diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h index a3f3d90ceb6a91..40d8ec79f11007 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h @@ -52,6 +52,11 @@ class MatterContext CHIP_ERROR GetDefaultOpenDuration(uint32_t & returnVal); CHIP_ERROR GetDefaultOpenLevel(uint8_t & returnVal); + // MarkDirty + virtual void MarkDirty(AttributeId id); + + virtual ~MatterContext() = default; + private: EndpointId mEndpoint; PersistentStorageDelegate & mPersistentStorageDelegate; diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index 2a6807cae974ef..00e73b1f8eae4f 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -156,6 +156,7 @@ source_set("thread-border-router-management-test-srcs") { source_set("valve-configuration-and-control-test-srcs") { sources = [ + "${chip_root}/src/app/cluster-building-blocks/QuieterReporting.h", "${chip_root}/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp", "${chip_root}/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h", "${chip_root}/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-delegate.h", @@ -166,6 +167,7 @@ source_set("valve-configuration-and-control-test-srcs") { "${chip_root}/src/app/common:cluster-objects", "${chip_root}/src/lib/core", "${chip_root}/src/lib/support", + "${chip_root}/src/platform", ] } diff --git a/src/app/tests/TestValveConfigurationAndControl.cpp b/src/app/tests/TestValveConfigurationAndControl.cpp index 65558d67ade3eb..b852c3da9241ea 100644 --- a/src/app/tests/TestValveConfigurationAndControl.cpp +++ b/src/app/tests/TestValveConfigurationAndControl.cpp @@ -22,18 +22,110 @@ #include #include #include +#include #include +#include +#include +using namespace chip::System::Clock::Literals; namespace chip { + +namespace System { + +// TODO: This might be worthwhile to generalize +class TimerAndMockClock : public Clock::Internal::MockClock, public Layer +{ +public: + // System Layer overrides + CHIP_ERROR Init() override { return CHIP_NO_ERROR; } + void Shutdown() override { Clear(); } + void Clear() + { + mTimerList.Clear(); + mTimerNodes.ReleaseAll(); + } + bool IsInitialized() const override { return true; } + + CHIP_ERROR StartTimer(Clock::Timeout aDelay, TimerCompleteCallback aComplete, void * aAppState) override + { + Clock::Timestamp awakenTime = GetMonotonicMilliseconds64() + std::chrono::duration_cast(aDelay); + TimerList::Node * node = mTimerNodes.Create(*this, awakenTime, aComplete, aAppState); + mTimerList.Add(node); + return CHIP_NO_ERROR; + } + void CancelTimer(TimerCompleteCallback aComplete, void * aAppState) override + { + TimerList::Node * cancelled = mTimerList.Remove(aComplete, aAppState); + if (cancelled != nullptr) + { + mTimerNodes.Release(cancelled); + } + } + CHIP_ERROR ExtendTimerTo(Clock::Timeout aDelay, TimerCompleteCallback aComplete, void * aAppState) override + { + return CHIP_ERROR_NOT_IMPLEMENTED; + } + bool IsTimerActive(TimerCompleteCallback onComplete, void * appState) override + { + return mTimerList.GetRemainingTime(onComplete, appState) != Clock::Timeout(0); + } + Clock::Timeout GetRemainingTime(TimerCompleteCallback onComplete, void * appState) override + { + return mTimerList.GetRemainingTime(onComplete, appState); + } + CHIP_ERROR ScheduleWork(TimerCompleteCallback aComplete, void * aAppState) override { return CHIP_ERROR_NOT_IMPLEMENTED; } + + // Clock overrides + void SetMonotonic(Clock::Milliseconds64 timestamp) + { + MockClock::SetMonotonic(timestamp); + // Find all the timers that fired at this time or before and invoke the callbacks + TimerList::Node * node; + while ((node = mTimerList.Earliest()) != nullptr && node->AwakenTime() <= timestamp) + { + mTimerList.PopEarliest(); + // Invoke auto-releases + mTimerNodes.Invoke(node); + } + } + + void AdvanceMonotonic(Clock::Milliseconds64 increment) { SetMonotonic(GetMonotonicMilliseconds64() + increment); } + +private: + TimerPool<> mTimerNodes; + TimerList mTimerList; +}; + +} // namespace System namespace app { namespace Clusters { namespace ValveConfigurationAndControl { +namespace { +// These are globals because SetUpTestSuite is static and I'm not shaving that yak today. +System::TimerAndMockClock gSystemLayerAndClock = System::TimerAndMockClock(); +System::Clock::ClockBase * gSavedClock = nullptr; +} // namespace + class TestValveConfigurationAndControlClusterLogic : public ::testing::Test { public: - static void SetUpTestSuite() { ASSERT_EQ(Platform::MemoryInit(), CHIP_NO_ERROR); } - static void TearDownTestSuite() { Platform::MemoryShutdown(); } + static void SetUpTestSuite() + { + ASSERT_EQ(Platform::MemoryInit(), CHIP_NO_ERROR); + gSavedClock = &System::SystemClock(); + System::Clock::Internal::SetSystemClockForTesting(&gSystemLayerAndClock); + DeviceLayer::SetSystemLayerForTesting(&gSystemLayerAndClock); + } + static void TearDownTestSuite() + { + gSystemLayerAndClock.Shutdown(); + DeviceLayer::SetSystemLayerForTesting(nullptr); + System::Clock::Internal::SetSystemClockForTesting(gSavedClock); + Platform::MemoryShutdown(); + } + +private: }; class TestDelegateLevel : public LevelControlDelegate @@ -128,6 +220,28 @@ class TestDelegateNoLevel : public NonLevelControlDelegate ValveStateEnum target = ValveStateEnum::kUnknownEnumValue; }; +// TODO: This also might be good to generalize, by using a common matter context for each cluster with allowed overrides +class MockedMatterContext : public MatterContext +{ +public: + MockedMatterContext(EndpointId endpoint, PersistentStorageDelegate & persistentStorageDelegate) : + MatterContext(endpoint, persistentStorageDelegate) + {} + void MarkDirty(AttributeId id) override { mDirtyMarkedList.push_back(id); } + std::vector GetDirtyList() { return mDirtyMarkedList; } + void ClearDirtyList() { mDirtyMarkedList.clear(); } + ~MockedMatterContext() { gSystemLayerAndClock.Clear(); } + +private: + // Won't handle double-marking an attribute, so don't do that in tests + std::vector mDirtyMarkedList; +}; + +//========================================================================================= +// Tests for conformance +//========================================================================================= + +// This test ensures that the Init function properly errors when the conformance is valid and passes otherwise. TEST_F(TestValveConfigurationAndControlClusterLogic, TestConformanceValid) { // Nothing on, should be valid @@ -195,11 +309,40 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestConformanceValid) EXPECT_FALSE(conformance.Valid()); } +// The cluster requires different versions of the delegate depending on the supported feature set. +// This test ensures the Init function corectly errors if the supplied delegate does not match +// the given cluster conformance. +TEST_F(TestValveConfigurationAndControlClusterLogic, TestWrongDelegates) +{ + TestDelegateLevel delegateLevel; + TestDelegateNoLevel delegateNoLevel; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logicLevel(delegateLevel, context); + ClusterLogic logicNoLevel(delegateNoLevel, context); + ClusterConformance conformanceLevel = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + ClusterConformance conformanceNoLevel = { + .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = false, .supportsLevelStep = false + }; + + EXPECT_EQ(logicLevel.Init(conformanceNoLevel), CHIP_ERROR_INVALID_DEVICE_DESCRIPTOR); + EXPECT_EQ(logicNoLevel.Init(conformanceLevel), CHIP_ERROR_INVALID_DEVICE_DESCRIPTOR); +} + +//========================================================================================= +// Tests for getters +//========================================================================================= + +// This test ensures that all getters are properly implemented and return valid values after successful initialization. TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesAllFeatures) { TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; - MatterContext context(0, storageDelegate); + MockedMatterContext context(0, storageDelegate); ClusterLogic logic(delegate, context); // Everything on, all should return values @@ -251,11 +394,13 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesAllFeature EXPECT_EQ(val8, 1); } +// This test ensures that attributes that are not supported by the conformance properly return errors +// and attributes that are supported return values properly. TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesNoFeatures) { TestDelegateNoLevel delegate; TestPersistentStorageDelegate storageDelegate; - MatterContext context(0, storageDelegate); + MockedMatterContext context(0, storageDelegate); ClusterLogic logic(delegate, context); // Everything on, all should return values @@ -300,32 +445,32 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesNoFeatures EXPECT_EQ(logic.GetLevelStep(val8), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); } +// This test ensures that all attribute getters return the given starting state values before changes. TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesStartingState) { TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; - MatterContext context(0, storageDelegate); + MockedMatterContext context(0, storageDelegate); ClusterLogic logic(delegate, context); // Everything on, all should return values - ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), - .supportsDefaultOpenLevel = true, - .supportsValveFault = true, - .supportsLevelStep = true }; - ClusterState state = { - .openDuration = DataModel::MakeNullable(static_cast(12u)), - .defaultOpenDuration = DataModel::MakeNullable(static_cast(10u)), - .autoCloseTime = DataModel::MakeNullable(static_cast(2500u)), - .remainingDuration = DataModel::MakeNullable(static_cast(1u)), - .currentState = DataModel::MakeNullable(ValveStateEnum::kTransitioning), - .targetState = DataModel::MakeNullable(ValveStateEnum::kOpen), - .currentLevel = DataModel::MakeNullable(static_cast(50u)), - .targetLevel = DataModel::MakeNullable(static_cast(75u)), - .defaultOpenLevel = 90u, - .valveFault = BitMask(ValveFaultBitmap::kLeaking), - .levelStep = 2u, + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + ClusterInitParameters initialState = { + .currentState = DataModel::MakeNullable(ValveStateEnum::kTransitioning), + .currentLevel = DataModel::MakeNullable(static_cast(50u)), + .valveFault = BitMask(ValveFaultBitmap::kLeaking), + .levelStep = 2u, }; - EXPECT_EQ(logic.Init(conformance, state), CHIP_NO_ERROR); + EXPECT_EQ(logic.Init(conformance, initialState), CHIP_NO_ERROR); + + ClusterState state; + state.currentState = initialState.currentState; + state.currentLevel = initialState.currentLevel; + state.valveFault = initialState.valveFault; + state.levelStep = initialState.levelStep; DataModel::Nullable valElapsedSNullable; DataModel::Nullable valEpochUsNullable; @@ -345,7 +490,7 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesStartingSt EXPECT_EQ(valEpochUsNullable, state.autoCloseTime); EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); - EXPECT_EQ(valElapsedSNullable, state.remainingDuration); + EXPECT_EQ(valElapsedSNullable, state.remainingDuration.value()); EXPECT_EQ(logic.GetCurrentState(valEnumNullable), CHIP_NO_ERROR); EXPECT_EQ(valEnumNullable, state.currentState); @@ -369,11 +514,12 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesStartingSt EXPECT_EQ(val8, state.levelStep); } +// This test ensures that all attribute getter functions properly error on an uninitialized cluster. TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesUninitialized) { TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; - MatterContext context(0, storageDelegate); + MockedMatterContext context(0, storageDelegate); ClusterLogic logic(delegate, context); DataModel::Nullable valElapsedSNullable; @@ -397,12 +543,23 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesUninitiali EXPECT_EQ(logic.GetLevelStep(val8), CHIP_ERROR_INCORRECT_STATE); } +//========================================================================================= +// Tests for setters +//========================================================================================= + +// This test ensures that the Set function for DefaultOpenDuration sets the value properly including +// - constraints checks +// - value pushed through to persistent storage correctly +// - value is persisted on a per-endpoint basis +// - cluster logic loads saved values at startup +// - cluster logic operates only on the stored values for the correct endpoint +// - Set function correctly errors when the persistent storage errors TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenDuration) { TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; - MatterContext context(endpoint, storageDelegate); + MockedMatterContext context(endpoint, storageDelegate); ClusterLogic logic(delegate, context); DataModel::Nullable valElapsedSNullable; @@ -466,7 +623,7 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenDuration) EXPECT_EQ(valElapsedSNullable, testVal); // Test that a new logic cluster on a different endpoint does not load the value - MatterContext context_ep1 = MatterContext(1, storageDelegate); + MockedMatterContext context_ep1 = MockedMatterContext(1, storageDelegate); ClusterLogic logic_different_endpoint = ClusterLogic(delegate, context_ep1); EXPECT_EQ(logic_different_endpoint.Init(conformance), CHIP_NO_ERROR); EXPECT_EQ(logic_different_endpoint.GetDefaultOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); @@ -489,12 +646,21 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenDuration) EXPECT_EQ(logic.SetDefaultOpenDuration(testVal), CHIP_ERROR_PERSISTED_STORAGE_FAILED); } +// This test ensures that the Set function for DefaultOpenDuration sets the value properly including +// - constraints checks +// - value pushed through to persistent storage correctly +// - value is persisted on a per-endpoint basis +// - cluster logic loads saved values at startup +// - cluster logic operates only on the stored values for the correct endpoint +// - Set function correctly errors when the persistent storage errors +// - Set function errors when the OpenLevel attribute is not supported +// Conformance to the LevelStep value is tested in another test TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenLevel) { TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; - MatterContext context(endpoint, storageDelegate); + MockedMatterContext context(endpoint, storageDelegate); ClusterLogic logic(delegate, context); uint8_t val8; @@ -563,7 +729,7 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenLevel) EXPECT_EQ(val8, testVal); // Test that a new logic cluster on a different endpoint does not load the value - MatterContext context_ep1 = MatterContext(1, storageDelegate); + MockedMatterContext context_ep1 = MockedMatterContext(1, storageDelegate); ClusterLogic logic_different_endpoint = ClusterLogic(delegate, context_ep1); EXPECT_EQ(logic_different_endpoint.Init(conformance), CHIP_NO_ERROR); EXPECT_EQ(logic_different_endpoint.GetDefaultOpenLevel(val8), CHIP_NO_ERROR); @@ -585,12 +751,13 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenLevel) EXPECT_EQ(logic_no_level.SetDefaultOpenLevel(testVal), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); } +// This test ensures that the SetDefaultOpenLevel function correctly assesses the set value with respect to the LevelStep. TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenLevelWithLevelStep) { TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; - MatterContext context(endpoint, storageDelegate); + MockedMatterContext context(endpoint, storageDelegate); ClusterLogic logic(delegate, context); uint8_t val8; @@ -600,8 +767,8 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenLevelWith .supportsDefaultOpenLevel = true, .supportsValveFault = true, .supportsLevelStep = true }; - ClusterState state = ClusterState(); - state.levelStep = 45; + ClusterInitParameters state; + state.levelStep = 45; EXPECT_EQ(logic.Init(conformance, state), CHIP_NO_ERROR); // Default is 100 @@ -627,34 +794,21 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenLevelWith EXPECT_EQ(val8, testVal); } -TEST_F(TestValveConfigurationAndControlClusterLogic, TestWrongDelegates) -{ - TestDelegateLevel delegateLevel; - TestDelegateNoLevel delegateNoLevel; - TestPersistentStorageDelegate storageDelegate; - EndpointId endpoint = 0; - MatterContext context(endpoint, storageDelegate); - ClusterLogic logicLevel(delegateLevel, context); - ClusterLogic logicNoLevel(delegateNoLevel, context); - ClusterConformance conformanceLevel = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), - .supportsDefaultOpenLevel = true, - .supportsValveFault = true, - .supportsLevelStep = true }; - ClusterConformance conformanceNoLevel = { - .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = false, .supportsLevelStep = false - }; - - EXPECT_EQ(logicLevel.Init(conformanceNoLevel), CHIP_ERROR_INVALID_ARGUMENT); - EXPECT_EQ(logicNoLevel.Init(conformanceLevel), CHIP_ERROR_INVALID_ARGUMENT); -} +//========================================================================================= +// Tests for Open command parameters +//========================================================================================= +// This test ensures that the OpenDuration is set correctly when Open is called. Tests: +// - Fall back to Null when omitted and DefaultOpenDuration is null +// - Fall back to DefaultOpenDuration when omitted and DefaultOpenDuration is set +// - Null and value are both accepted when the parameter is supplied in the command field. TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenDuration) { TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; - MatterContext context(endpoint, storageDelegate); + MockedMatterContext context(endpoint, storageDelegate); ClusterLogic logic(delegate, context); ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), @@ -665,6 +819,7 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenDuration) DataModel::Nullable valElapsedSNullable; + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(DataModel::NullNullable), std::nullopt), CHIP_NO_ERROR); EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); @@ -696,12 +851,14 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenDuration) EXPECT_EQ(valElapsedSNullable.ValueOr(0), 12u); } +// This test ensures that the Open command correctly errors when the TargetLevel field is +// set for devices that do not support the LVL feature. TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenTargetLevelFeatureUnsupported) { TestDelegateNoLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; - MatterContext context(endpoint, storageDelegate); + MockedMatterContext context(endpoint, storageDelegate); ClusterLogic logic(delegate, context); ClusterConformance conformance = { @@ -717,12 +874,14 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenTargetLevelFe EXPECT_EQ(delegate.numHandleOpenValveCalls, 1); } +// This test ensures that The Open command correctly falls back to the spec-defined default when the target level +// is omitted and the DefaultOpenLevel is not supported. TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenTargetLevelNotSuppliedNoDefaultSupported) { TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; - MatterContext context(endpoint, storageDelegate); + MockedMatterContext context(endpoint, storageDelegate); ClusterLogic logic(delegate, context); ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel), @@ -734,12 +893,14 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenTargetLevelNo EXPECT_EQ(delegate.lastRequestedLevel, 100u); } +// This test ensures that the Open command correclty calls back to the DefaultOpenLevel when the target level +// is omitted and the DefaultOpenLevel is supported. TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenTargetLevelNotSuppliedDefaultSupported) { TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; - MatterContext context(endpoint, storageDelegate); + MockedMatterContext context(endpoint, storageDelegate); ClusterLogic logic(delegate, context); ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel), @@ -755,20 +916,21 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenTargetLevelNo EXPECT_EQ(delegate.lastRequestedLevel, 50u); } +// This test ensures the Open command uses the supplied target level if it is supplied. TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenTargetLevelSupplied) { TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; - MatterContext context(endpoint, storageDelegate); + MockedMatterContext context(endpoint, storageDelegate); ClusterLogic logic(delegate, context); ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel), .supportsDefaultOpenLevel = true, .supportsValveFault = false, .supportsLevelStep = true }; - ClusterState state = ClusterState(); - state.levelStep = 33; + ClusterInitParameters state; + state.levelStep = 33; EXPECT_EQ(logic.Init(conformance, state), CHIP_NO_ERROR); // 33, 66, 99 and 100 should all work, nothing else should @@ -791,6 +953,12 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenTargetLevelSu EXPECT_EQ(delegate.numHandleOpenValveCalls, 4); } +//========================================================================================= +// Tests for Open Command handlers - current and target values +//========================================================================================= + +// This test ensures the target and current values are set correcly when the delegate is +// able to open the value immediately. Cluster supports LVL feature TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenImmediateLevelSupported) { // Testing that current level, target level, target state and current state are set correctly @@ -800,7 +968,7 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenImmediateLeve TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; - MatterContext context(endpoint, storageDelegate); + MockedMatterContext context(endpoint, storageDelegate); ClusterLogic logic(delegate, context); ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel), @@ -841,12 +1009,14 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenImmediateLeve EXPECT_EQ(delegate.numHandleOpenValveCalls, 2); } +// This test ensures the target and current values are set correcly when the delegate is +// able to open the value immediately. Cluster does not support LVL feature TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenImmediateLevelNotSupported) { TestDelegateNoLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; - MatterContext context(endpoint, storageDelegate); + MockedMatterContext context(endpoint, storageDelegate); ClusterLogic logic(delegate, context); ClusterConformance conformance = { @@ -867,12 +1037,14 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenImmediateLeve EXPECT_EQ(delegate.numHandleOpenValveCalls, 1); } +// This test ensures the target and current values are set correcly when the delegate is +// not able to open the value immediately. Cluster supports LVL feature TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenAsyncLevelSupported) { TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; - MatterContext context(endpoint, storageDelegate); + MockedMatterContext context(endpoint, storageDelegate); ClusterLogic logic(delegate, context); ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel), @@ -913,12 +1085,14 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenAsyncLevelSup EXPECT_EQ(delegate.numHandleOpenValveCalls, 2); } +// This test ensures the target and current values are set correcly when the delegate is +// not able to open the value immediately. Cluster does not support LVL feature TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenAsyncLevelNotSupported) { TestDelegateNoLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; - MatterContext context(endpoint, storageDelegate); + MockedMatterContext context(endpoint, storageDelegate); ClusterLogic logic(delegate, context); ClusterConformance conformance = { @@ -943,12 +1117,17 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenAsyncLevelNot EXPECT_EQ(delegate.numHandleOpenValveCalls, 1); } +//========================================================================================= +// Tests for Open Command handlers - valve faults +//========================================================================================= + +// Test ensures valve faults are handled correctly when the valve can still be opened. LVL feature supported. TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturnedNoErrorLevel) { TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; - MatterContext context(endpoint, storageDelegate); + MockedMatterContext context(endpoint, storageDelegate); ClusterLogic logic(delegate, context); ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel), @@ -967,12 +1146,13 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturned EXPECT_EQ(valveFault, delegate.simulatedFailureBitMask); } +// Test ensures valve faults are handled correctly when the valve can still be opened. LVL feature not supported. TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturnedNoErrorNoLevel) { TestDelegateNoLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; - MatterContext context(endpoint, storageDelegate); + MockedMatterContext context(endpoint, storageDelegate); ClusterLogic logic(delegate, context); ClusterConformance conformance = { @@ -993,12 +1173,13 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturned EXPECT_EQ(valveFault, delegate.simulatedFailureBitMask); } +// Test ensures valve faults are handled correctly when the valve cannot be opened. LVL feature supported. TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturnedErrorLevel) { TestDelegateLevel delegate; TestPersistentStorageDelegate storageDelegate; EndpointId endpoint = 0; - MatterContext context(endpoint, storageDelegate); + MockedMatterContext context(endpoint, storageDelegate); ClusterLogic logic(delegate, context); ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel), @@ -1016,13 +1197,14 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturned EXPECT_EQ(valveFault, delegate.simulatedFailureBitMask); } +// Test ensures valve faults are handled correctly when the valve cannot be opened. LVL feature not supported. TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturnedErrorNoLevel) { TestDelegateNoLevel delegate; TestPersistentStorageDelegate storageDelegate; - EndpointId endpoint = 0; - MatterContext context = MatterContext(endpoint, storageDelegate); - ClusterLogic logic = ClusterLogic(delegate, context); + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); ClusterConformance conformance = { .featureMap = 0, @@ -1041,17 +1223,328 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturned EXPECT_EQ(valveFault, delegate.simulatedFailureBitMask); } -// Clock::ClockBase * const savedClock = &SystemClock(); -// Clock::Internal::MockClock mockClock; -// Clock::Internal::SetSystemClockForTesting(&mockClock); +// Test ensures returned valve faults do not cause cluster problems if returned from the delegate when the valve feature is not +// supported. +TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturnedErrorLevelFaultNotSupported) +{ + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); -// Test that the cluster logic doesn't balk if error is returned, but valve fault isn't supported -// Test that the auto close time is set appropriately for open duration - add in prior test? -// Test setter for valve fault -// Test attribute change notifications are sent out and not sent out when the attribute is change do the same value - add in prior -// tests? Test events are generated + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel), + .supportsDefaultOpenLevel = true, + .supportsValveFault = false, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + delegate.simulateFailure = true; + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_ERROR_INTERNAL); + EXPECT_EQ(delegate.numHandleOpenValveCalls, 1); + + delegate.simulateFailure = false; + delegate.simulateValveFaultNoFailure = true; + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(delegate.numHandleOpenValveCalls, 2); +} + +// Test ensures returned valve faults do not cause cluster problems if returned from the delegate when the valve feature is not +// supported. +TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturnedErrorNoLevelFaultNotSupported) +{ + TestDelegateNoLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { + .featureMap = 0, + .supportsDefaultOpenLevel = false, + .supportsValveFault = false, + .supportsLevelStep = false, + }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + delegate.simulateFailure = true; + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_ERROR_INTERNAL); + EXPECT_EQ(delegate.numHandleOpenValveCalls, 1); + + delegate.simulateFailure = false; + delegate.simulateValveFaultNoFailure = true; + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(delegate.numHandleOpenValveCalls, 2); +} + +bool HasAttributeChanges(std::vector changes, AttributeId id) +{ + return std::find(changes.begin(), changes.end(), id) != changes.end(); +} +//========================================================================================= +// Tests for timing for Attribute updates and RemainingDuration Q +//========================================================================================= +// Tests that remaining duration is updated when openLevel is set to a value or to to null +// Tests that attribute reports are sent out for attributes changed on Open call +// Tests that attribute reports are sent for RemainingDuration at Q times. +TEST_F(TestValveConfigurationAndControlClusterLogic, TestAttributeUpdates) +{ + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + DataModel::Nullable valElapsedSNullable; + DataModel::Nullable valPercentNullable; + + // When null, RemainingDuration should also be null. No updates are expected. + context.ClearDirtyList(); + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(DataModel::NullNullable), std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + // We should see the currentLevel and currentState marked as dirty, and the targets should not be + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentLevel::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentState::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetLevel::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetState::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); + + // When non-null RemainingDuration should initially be set to OpenDuration and this should trigger + // an attribute change callback. Attribute change callbacks should happen no more than once per second + // and at the end. + // This test tests attribute callbacks on an open duration that runs to the end. The OpenDuration is + // in units of seconds, so the close call will come at units of seconds. + gSystemLayerAndClock.SetMonotonic(0_ms64); + context.ClearDirtyList(); + + DataModel::Nullable openDuration; + openDuration.SetNonNull(12u); + Percent requestedLevel = 50u; + // Test also that we get an attribute update + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::make_optional(requestedLevel)), CHIP_NO_ERROR); + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable.ValueOr(0), openDuration.Value()); + EXPECT_EQ(logic.GetCurrentLevel(valPercentNullable), CHIP_NO_ERROR); + EXPECT_EQ(valPercentNullable.ValueOr(0), requestedLevel); + // We should see the following attributes marked as dirty: currentLevel, remainingDuration, openDuration + // The targetLevel and targetState should be null, and thus we should see no update. + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentLevel::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentState::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetLevel::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetState::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); + + context.ClearDirtyList(); + gSystemLayerAndClock.AdvanceMonotonic(500_ms64); + // No new reports should be happening in this time. + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); + // Even if we read the remaining duration to force an update, we shouldn't expect an attribute change report + EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); + + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentLevel::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentState::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetLevel::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetState::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + + // At 1s, the system should generate a report. + context.ClearDirtyList(); + gSystemLayerAndClock.AdvanceMonotonic(500_ms64); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); + EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable.ValueOr(0), openDuration.Value() - 1); + + // Nothing else should have changed + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentLevel::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentState::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetLevel::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetState::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + + // At 1.7s, the system should not generate a report even if we read the value to force and update. + // The new duration should be reflected (rounded to nearest s) + context.ClearDirtyList(); + gSystemLayerAndClock.AdvanceMonotonic(700_ms64); + EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable.ValueOr(0), openDuration.Value() - 2); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); + + // At 2s, we should get an attribute change report even if we don't read. + context.ClearDirtyList(); + gSystemLayerAndClock.AdvanceMonotonic(300_ms64); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); + + // Only that one attribute should be reporting a change. + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentLevel::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentState::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetLevel::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetState::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + + // Clear out all the attribute reports at 11.5 seconds. This should trigger the prior timer. + gSystemLayerAndClock.SetMonotonic(11500_ms64); + context.ClearDirtyList(); + + // Ensure we get a report at 12s and that the value goes back to Null + gSystemLayerAndClock.AdvanceMonotonic(500_ms64); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); + + // Everything else should also be reporting changes as they flip back to their defaults. + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentLevel::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentState::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + + context.ClearDirtyList(); + // Ensure we don't get a further report generated by reading + EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); -// Init providing the wrong delegate type + // Test increasing the open duration on a non-second order to see that we get an attribute change callback for the change + // up. + gSystemLayerAndClock.SetMonotonic(0_ms64); + context.ClearDirtyList(); + + openDuration.SetNonNull(12u); + requestedLevel = 50u; + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::make_optional(requestedLevel)), CHIP_NO_ERROR); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentLevel::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentState::Id)); + + context.ClearDirtyList(); + gSystemLayerAndClock.AdvanceMonotonic(500_ms64); + // At 0.5s, we wouldn't normally expect a report for duration, but if we send a new open to increase the time, we should. + openDuration.SetNonNull(15u); + requestedLevel = 50u; + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::make_optional(requestedLevel)), CHIP_NO_ERROR); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentLevel::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentState::Id)); + + // TODO: Clarify, should we get a report if we use open to set the remaining duration down? I think so. + // TODO: Add such tests here. I don't think the underlying layer handles that properly right now. + + // Just before the next report should go out, we set this to NULL. We should get a report immediately. + gSystemLayerAndClock.AdvanceMonotonic(400_ms64); + + openDuration.SetNull(); + requestedLevel = 100u; + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::make_optional(requestedLevel)), CHIP_NO_ERROR); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentLevel::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentState::Id)); + + context.ClearDirtyList(); + + // Check that we DON'T get a report from the previous Open call, which had a remaining duration timer on it. + gSystemLayerAndClock.AdvanceMonotonic(100_ms64); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); +} + +// Tests that RemainingDuration gets updated correctly even if the timer doesn't fire on the exact ms (it doesn't). +TEST_F(TestValveConfigurationAndControlClusterLogic, TestAttributeUpdatesNonExactClock) +{ + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + gSystemLayerAndClock.SetMonotonic(0_ms64); + gSystemLayerAndClock.Clear(); + DataModel::Nullable openDuration; + openDuration.SetNonNull(2u); + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::nullopt), CHIP_NO_ERROR); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + + context.ClearDirtyList(); + gSystemLayerAndClock.AdvanceMonotonic(550_ms64); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + + context.ClearDirtyList(); + gSystemLayerAndClock.AdvanceMonotonic(550_ms64); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); + EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + + context.ClearDirtyList(); + gSystemLayerAndClock.SetMonotonic(2100_ms64); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); +} + +//========================================================================================= +// Tests for automatically calling close from the cluster logic +//========================================================================================= +// ensures close is called at open duration and not before + +//========================================================================================= +// Tests for handling close commands +//========================================================================================= +// while remaining duration is null and valve is open +// while remaining duration is not null and valve is open +// while valve is closed + +//========================================================================================= +// Tests for timing for async read updates to current / target level and state +//========================================================================================= +// TODO: Should the cluster logic be responsible for reaching out to the delegate at the Q update period to get updates? +// TODO: What about for target state? Should this be drive by the cluster logic or the delegate? Maybe both? +// Pros for cluster logic: +// - delegate has fewer responsibilities, doesn't have to do things like timers for updating level +// Pros for delegate: +// - hardware might have built-in mechanisms to alert for level changes and state changes + +// Tests that GetCurrentValveLevel is called until the level hits the target +// Tests that the attribute update is not more than +// TODO: should reads in the middle of the update period go out and get the current value? Probably, but it's annoying. + +// Timing tests for async read +// Tests to ensure that we get calls to update the level and state until the target state / level is reached. +// Tests to ensure that we don't get updates on more than an X basis or at the end and the beginning + +//========================================================================================= +// Tests for attribute callbacks from delegates +//========================================================================================= +// TODO: Should the delegate call the cluster logic class direclty, or should this be piped through the delegate class so the app +// layer ONLY has to interact with the delegate? +// Test setter for valve fault Test attribute change notifications are sent out and not +// sent out when the attribute is change do the same value - add in prior + +//========================================================================================= +// Tests for attribute callbacks from delegates +//========================================================================================= +// Tests for events +// Test events are generated + +//========================================================================================= +// Tests for attribute callbacks from delegates +//========================================================================================= +// Tests for auto-close +// Test that the auto close time is set appropriately for open duration - add in prior test? +// should work both when the time is available at the time of the call and when the time is later set // TODO: Should the cluster logic query the current level and curent state from the driver at startup? From 02906e11517a1c3ea56b64021c4ba541af46ffed Mon Sep 17 00:00:00 2001 From: cecille Date: Sun, 15 Sep 2024 18:20:52 -0400 Subject: [PATCH 05/31] Valve: Disco ball - Handle automatic close --- ...onfiguration-and-control-cluster-logic.cpp | 39 +- ...valve-configuration-and-control-delegate.h | 63 ++- .../TestValveConfigurationAndControl.cpp | 498 +++++++++++++++++- 3 files changed, 557 insertions(+), 43 deletions(-) diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp index b42aa4132353d8..a31b626f066583 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp @@ -457,13 +457,44 @@ CHIP_ERROR ClusterLogic::HandleOpenCommand(std::optional faults; + if (mConformance.HasFeature(Feature::kLevel)) + { + Percent currentLevel; + err = mClusterDriver.HandleCloseValve(currentLevel, faults); + if (err == CHIP_NO_ERROR) + { + mState.SetCurrentLevel(DataModel::Nullable(currentLevel)); + if (currentLevel == 0) + { + mState.SetCurrentState(DataModel::Nullable(ValveStateEnum::kClosed)); + } + else + { + mState.SetCurrentState(DataModel::Nullable(ValveStateEnum::kTransitioning)); + } + } + } + else + { + ValveStateEnum state; + err = mClusterDriver.HandleCloseValve(state, faults); + if (err == CHIP_NO_ERROR) + { + mState.SetCurrentState(state); + } + } + // If there was an error, we know nothing about the current state + if (err != CHIP_NO_ERROR) + { + mState.SetCurrentLevel(DataModel::NullNullable); + mState.SetCurrentState(DataModel::NullNullable); + } + mState.SetValveFault(faults); mState.SetOpenDuration(DataModel::NullNullable); mState.SetRemainingDuration(DataModel::NullNullable); - mState.SetCurrentLevel(DataModel::Nullable(0)); mState.SetTargetLevel(DataModel::NullNullable); - mState.SetCurrentState(DataModel::Nullable(ValveStateEnum::kClosed)); mState.SetTargetState(DataModel::NullNullable); mState.SetAutoCloseTime(DataModel::NullNullable); } diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-delegate.h b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-delegate.h index ed29e3bae19f18..30ddc40f7cb4d2 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-delegate.h +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-delegate.h @@ -59,13 +59,6 @@ class DelegateBase DelegateBase(){}; virtual ~DelegateBase() = default; - // TODO: Remove these - virtual DataModel::Nullable HandleOpenValve(DataModel::Nullable level) - { - return DataModel::NullNullable; - } - virtual void HandleRemainingDurationTick(uint32_t duration) {} - // This delegate function will be called only for valve implementations that support the LVL feature. // Delegates for valves that do not support the LVL feature return CHIP_ERROR_NOT_IMPLEMENTED. // When this function is called, the delegate should set the valve to the target level, or begin the async process of opening @@ -89,7 +82,7 @@ class DelegateBase // When this function is called, the delegate should open the valve, or begin the async process of opening the valve. // If the valve is able to be opened (success) // - If the valve is fully open, currentState should be set to kOpen - // - If the valve is not fully opened, the delegate should set the currentLevel to the current valve level and the caller + // - If the valve is not fully opened, the delegate should set the currentState to kTransitioning and the caller // will continue to query the valve level until the target level is reached or the valve is closed. // - A valve fault may be returned even if the Open command is successful, if the fault did not prevent the valve from safely // opening @@ -103,7 +96,41 @@ class DelegateBase virtual Percent GetCurrentValveLevel() = 0; // This delegate function will be called only for valve implementations that do not support the LVL feature. virtual ValveStateEnum GetCurrentValveState() = 0; - virtual CHIP_ERROR HandleCloseValve() = 0; + + // This delegate function will be called when the valve needs to be closed either due to an explicit command or + // from the expiration of the open duration. This function will be called for valves that support the LVL feature. + // Delegates for valves that do not support the LVL feature should return CHIP_ERROR_NOT_IMPLEMENTED. + // When this function is called, the delegate should close the valve, or begin the async process of closing. + // If the valve is able to be closed (success) + // - the delegate should set currentLevel + // - If the valve is fully closed, currentLevel should be set to 0 + // - If the valve is not fully closed, the delegate should set the currentLevel to the current valve level and the caller + // will continue to query the valve level until the valve reaches 0 or a command overrides. + // - A valve fault may be returned even if the Open command is successful, if the fault did not prevent the valve from safely + // closing + // - return CHIP_NO_ERROR + // If the valve cannot be closed (failure) + // - The delegate should set the valveFault parameter to indicate the reason for the failure (if applicable) + // - The delegate should return a CHIP_ERROR_INTERNAL + virtual CHIP_ERROR HandleCloseValve(Percent & currentLevel, BitMask & valveFault) = 0; + + // This delegate function will be called when the valve needs to be closed either due to an explicit command or + // from the expiration of the open duration. + // This delegate function will be called only for valve implementations that DO NOT support the LVL feature. + // Delegates for valves that support the LVL feature should return CHIP_ERROR_NOT_IMPLEMENTED. + // When this function is called, the delegate should close the valve, or begin the async process of closing. + // If the valve is able to be closed (success) + // - If the valve is fully closed, currentState should be set to kClosed + // - If the valve is not fully opened, the delegate should set the currentLevel to the current valve level and the caller + // will continue to query the valve level until the target level is reached or the valve is closed. + // - A valve fault may be returned even if the Open command is successful, if the fault did not prevent the valve from safely + // opening + // - return CHIP_NO_ERROR + // If the valve cannot be safely opened (failure) + // - The delegate should set the valveFault parameter to indicate the reason for the failure (if applicable) + // - The delegate should return a CHIP_ERROR_INTERNAL + virtual CHIP_ERROR HandleCloseValve(ValveStateEnum & currentState, BitMask & valveFault) = 0; + virtual DelegateType GetDelegateType() { return DelegateType::kBase; }; }; @@ -112,26 +139,38 @@ class LevelControlDelegate : public DelegateBase virtual CHIP_ERROR HandleOpenValve(const Percent targetLevel, Percent & currentLevel, BitMask & valveFault) = 0; - virtual Percent GetCurrentValveLevel() = 0; + virtual Percent GetCurrentValveLevel() = 0; + virtual CHIP_ERROR HandleCloseValve(Percent & currentLevel, BitMask & valveFault) = 0; // Final overrides - the driver should not implement these classes CHIP_ERROR HandleOpenValve(ValveStateEnum & currentState, BitMask & valveFault) final { return CHIP_ERROR_NOT_IMPLEMENTED; } + CHIP_ERROR HandleCloseValve(ValveStateEnum & currentState, BitMask & valveFault) final + { + return CHIP_ERROR_NOT_IMPLEMENTED; + } + ValveStateEnum GetCurrentValveState() final { return ValveStateEnum::kUnknownEnumValue; } DelegateType GetDelegateType() final { return DelegateType::kLevel; }; }; class NonLevelControlDelegate : public DelegateBase { - virtual CHIP_ERROR HandleOpenValve(ValveStateEnum & currentState, BitMask & valveFault) = 0; - virtual ValveStateEnum GetCurrentValveState() = 0; + virtual CHIP_ERROR HandleOpenValve(ValveStateEnum & currentState, BitMask & valveFault) = 0; + virtual ValveStateEnum GetCurrentValveState() = 0; + virtual CHIP_ERROR HandleCloseValve(ValveStateEnum & currentState, BitMask & valveFault) = 0; CHIP_ERROR HandleOpenValve(const Percent targetLevel, Percent & currentLevel, BitMask & valveFault) final { return CHIP_ERROR_NOT_IMPLEMENTED; } + CHIP_ERROR HandleCloseValve(Percent & currentLevel, BitMask & valveFault) final + { + return CHIP_ERROR_NOT_IMPLEMENTED; + } + Percent GetCurrentValveLevel() final { return 0; } DelegateType GetDelegateType() final { return DelegateType::kNonLevel; }; }; diff --git a/src/app/tests/TestValveConfigurationAndControl.cpp b/src/app/tests/TestValveConfigurationAndControl.cpp index b852c3da9241ea..073be82479b807 100644 --- a/src/app/tests/TestValveConfigurationAndControl.cpp +++ b/src/app/tests/TestValveConfigurationAndControl.cpp @@ -136,7 +136,7 @@ class TestDelegateLevel : public LevelControlDelegate { lastRequestedLevel = targetLevel; ++numHandleOpenValveCalls; - if (simulateAsyncOpen) + if (simulateAsync) { currentLevel = targetLevel / 2; } @@ -146,16 +146,39 @@ class TestDelegateLevel : public LevelControlDelegate } level = currentLevel; target = targetLevel; - if (simulateFailure || simulateValveFaultNoFailure) + if (simulateOpenFailure || simulateValveFaultNoFailure) { valveFault = simulatedFailureBitMask; } - if (simulateFailure) + if (simulateOpenFailure) { return CHIP_ERROR_INTERNAL; } return CHIP_NO_ERROR; } + virtual CHIP_ERROR HandleCloseValve(Percent & currentLevel, BitMask & valveFault) override + { + ++numHandleCloseValveCalls; + if (simulateAsync) + { + currentLevel = 10; + } + else + { + currentLevel = 0; + } + level = currentLevel; + if (simulateCloseFailure || simulateValveFaultNoFailure) + { + valveFault = simulatedFailureBitMask; + } + if (simulateCloseFailure) + { + return CHIP_ERROR_INTERNAL; + } + return CHIP_NO_ERROR; + } + Percent GetCurrentValveLevel() override { // TODO: maybe want to ramp this. @@ -165,12 +188,13 @@ class TestDelegateLevel : public LevelControlDelegate BitMask simulatedFailureBitMask = BitMask(to_underlying(ValveFaultBitmap::kBlocked) | to_underlying(ValveFaultBitmap::kGeneralFault)); - CHIP_ERROR HandleCloseValve() override { return CHIP_NO_ERROR; } Percent lastRequestedLevel = 0; - bool simulateFailure = false; + bool simulateOpenFailure = false; + bool simulateCloseFailure = false; bool simulateValveFaultNoFailure = false; - bool simulateAsyncOpen = false; + bool simulateAsync = false; int numHandleOpenValveCalls = 0; + int numHandleCloseValveCalls = 0; Percent level = 0; Percent target = 0; }; @@ -182,7 +206,7 @@ class TestDelegateNoLevel : public NonLevelControlDelegate CHIP_ERROR HandleOpenValve(ValveStateEnum & currentState, BitMask & valveFault) override { ++numHandleOpenValveCalls; - if (simulateAsyncOpen) + if (simulateAsync) { currentState = ValveStateEnum::kTransitioning; } @@ -192,11 +216,34 @@ class TestDelegateNoLevel : public NonLevelControlDelegate } state = currentState; target = ValveStateEnum::kOpen; - if (simulateFailure || simulateValveFaultNoFailure) + if (simulateOpenFailure || simulateValveFaultNoFailure) { valveFault = simulatedFailureBitMask; } - if (simulateFailure) + if (simulateOpenFailure) + { + return CHIP_ERROR_INTERNAL; + } + return CHIP_NO_ERROR; + } + CHIP_ERROR HandleCloseValve(ValveStateEnum & currentState, BitMask & valveFault) override + { + ++numHandleCloseValveCalls; + if (simulateAsync) + { + currentState = ValveStateEnum::kTransitioning; + } + else + { + currentState = ValveStateEnum::kClosed; + } + state = currentState; + target = ValveStateEnum::kClosed; + if (simulateCloseFailure || simulateValveFaultNoFailure) + { + valveFault = simulatedFailureBitMask; + } + if (simulateCloseFailure) { return CHIP_ERROR_INTERNAL; } @@ -211,11 +258,12 @@ class TestDelegateNoLevel : public NonLevelControlDelegate BitMask simulatedFailureBitMask = BitMask(to_underlying(ValveFaultBitmap::kBlocked) | to_underlying(ValveFaultBitmap::kGeneralFault)); - CHIP_ERROR HandleCloseValve() override { return CHIP_NO_ERROR; } - bool simulateFailure = false; + bool simulateOpenFailure = false; + bool simulateCloseFailure = false; bool simulateValveFaultNoFailure = false; - bool simulateAsyncOpen = false; + bool simulateAsync = false; int numHandleOpenValveCalls = 0; + int numHandleCloseValveCalls = 0; ValveStateEnum state = ValveStateEnum::kUnknownEnumValue; ValveStateEnum target = ValveStateEnum::kUnknownEnumValue; }; @@ -977,8 +1025,8 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenImmediateLeve .supportsLevelStep = true }; EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); - delegate.simulateFailure = false; - delegate.simulateAsyncOpen = false; + delegate.simulateOpenFailure = false; + delegate.simulateAsync = false; DataModel::Nullable level; DataModel::Nullable state; @@ -1024,8 +1072,8 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenImmediateLeve }; EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); - delegate.simulateFailure = false; - delegate.simulateAsyncOpen = false; + delegate.simulateOpenFailure = false; + delegate.simulateAsync = false; DataModel::Nullable state; EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); @@ -1053,8 +1101,8 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenAsyncLevelSup .supportsLevelStep = true }; EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); - delegate.simulateFailure = false; - delegate.simulateAsyncOpen = true; + delegate.simulateOpenFailure = false; + delegate.simulateAsync = true; DataModel::Nullable level; DataModel::Nullable state; @@ -1103,8 +1151,8 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenAsyncLevelNot }; EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); - delegate.simulateFailure = false; - delegate.simulateAsyncOpen = true; + delegate.simulateOpenFailure = false; + delegate.simulateAsync = true; DataModel::Nullable state; @@ -1190,7 +1238,7 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturned BitMask valveFault; - delegate.simulateFailure = true; + delegate.simulateOpenFailure = true; EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_ERROR_INTERNAL); EXPECT_EQ(delegate.numHandleOpenValveCalls, 1); EXPECT_EQ(logic.GetValveFault(valveFault), CHIP_NO_ERROR); @@ -1216,7 +1264,7 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturned BitMask valveFault; - delegate.simulateFailure = true; + delegate.simulateOpenFailure = true; EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_ERROR_INTERNAL); EXPECT_EQ(delegate.numHandleOpenValveCalls, 1); EXPECT_EQ(logic.GetValveFault(valveFault), CHIP_NO_ERROR); @@ -1239,11 +1287,11 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturned .supportsLevelStep = true }; EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); - delegate.simulateFailure = true; + delegate.simulateOpenFailure = true; EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_ERROR_INTERNAL); EXPECT_EQ(delegate.numHandleOpenValveCalls, 1); - delegate.simulateFailure = false; + delegate.simulateOpenFailure = false; delegate.simulateValveFaultNoFailure = true; EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); EXPECT_EQ(delegate.numHandleOpenValveCalls, 2); @@ -1267,11 +1315,11 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenFaultReturned }; EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); - delegate.simulateFailure = true; + delegate.simulateOpenFailure = true; EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_ERROR_INTERNAL); EXPECT_EQ(delegate.numHandleOpenValveCalls, 1); - delegate.simulateFailure = false; + delegate.simulateOpenFailure = false; delegate.simulateValveFaultNoFailure = true; EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); EXPECT_EQ(delegate.numHandleOpenValveCalls, 2); @@ -1498,7 +1546,403 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestAttributeUpdatesNonExac //========================================================================================= // Tests for automatically calling close from the cluster logic //========================================================================================= -// ensures close is called at open duration and not before +TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCalledAtOpenDurationLevel) +{ + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + DataModel::Nullable valElapsedSNullable; + DataModel::Nullable valEnumNullable; + DataModel::Nullable valPercentNullable; + BitMask valBitmap; + + gSystemLayerAndClock.SetMonotonic(0_ms64); + gSystemLayerAndClock.Clear(); + DataModel::Nullable openDuration; + openDuration.SetNonNull(2u); + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::nullopt), CHIP_NO_ERROR); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 0); + + gSystemLayerAndClock.AdvanceMonotonic(2000_ms64); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); + // No valve faults, level should say 0, state should say closed, targets should be null + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetCurrentState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::Nullable(ValveStateEnum::kClosed)); + + EXPECT_EQ(logic.GetTargetState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetCurrentLevel(valPercentNullable), CHIP_NO_ERROR); + EXPECT_EQ(valPercentNullable, DataModel::Nullable(0)); + + EXPECT_EQ(logic.GetTargetLevel(valPercentNullable), CHIP_NO_ERROR); + EXPECT_EQ(valPercentNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetValveFault(valBitmap), CHIP_NO_ERROR); + EXPECT_EQ(valBitmap, BitMask(0)); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCalledAtOpenDurationNoLevel) +{ + TestDelegateNoLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { + .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = true, .supportsLevelStep = false + }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + DataModel::Nullable valElapsedSNullable; + DataModel::Nullable valEnumNullable; + BitMask valBitmap; + + gSystemLayerAndClock.SetMonotonic(0_ms64); + gSystemLayerAndClock.Clear(); + DataModel::Nullable openDuration; + openDuration.SetNonNull(2u); + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::nullopt), CHIP_NO_ERROR); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 0); + + gSystemLayerAndClock.AdvanceMonotonic(2000_ms64); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); + // No valve faults, level should say 0, state should say closed, targets should be null + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetCurrentState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::Nullable(ValveStateEnum::kClosed)); + + EXPECT_EQ(logic.GetTargetState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetValveFault(valBitmap), CHIP_NO_ERROR); + EXPECT_EQ(valBitmap, BitMask(0)); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCalledAtOpenDurationLevelAsync) +{ + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + delegate.simulateAsync = true; + + DataModel::Nullable valElapsedSNullable; + DataModel::Nullable valEnumNullable; + DataModel::Nullable valPercentNullable; + BitMask valBitmap; + + gSystemLayerAndClock.SetMonotonic(0_ms64); + gSystemLayerAndClock.Clear(); + DataModel::Nullable openDuration; + openDuration.SetNonNull(2u); + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::nullopt), CHIP_NO_ERROR); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 0); + + gSystemLayerAndClock.AdvanceMonotonic(2000_ms64); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); + // No valve faults, level should say 0, state should say closed, targets should be null + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetCurrentState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::Nullable(ValveStateEnum::kTransitioning)); + + EXPECT_EQ(logic.GetTargetState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetCurrentLevel(valPercentNullable), CHIP_NO_ERROR); + EXPECT_NE(valPercentNullable, DataModel::Nullable(0)); + + EXPECT_EQ(logic.GetTargetLevel(valPercentNullable), CHIP_NO_ERROR); + EXPECT_EQ(valPercentNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetValveFault(valBitmap), CHIP_NO_ERROR); + EXPECT_EQ(valBitmap, BitMask(0)); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCalledAtOpenDurationNoLevelAsync) +{ + TestDelegateNoLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { + .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = true, .supportsLevelStep = false + }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + delegate.simulateAsync = true; + + DataModel::Nullable valElapsedSNullable; + DataModel::Nullable valEnumNullable; + BitMask valBitmap; + + gSystemLayerAndClock.SetMonotonic(0_ms64); + gSystemLayerAndClock.Clear(); + DataModel::Nullable openDuration; + openDuration.SetNonNull(2u); + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::nullopt), CHIP_NO_ERROR); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 0); + + gSystemLayerAndClock.AdvanceMonotonic(2000_ms64); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); + + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetCurrentState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::Nullable(ValveStateEnum::kTransitioning)); + + EXPECT_EQ(logic.GetTargetState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetValveFault(valBitmap), CHIP_NO_ERROR); + EXPECT_EQ(valBitmap, BitMask(0)); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCalledAtOpenDurationLevelOkFaultReturned) +{ + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + delegate.simulatedFailureBitMask = BitMask(to_underlying(ValveFaultBitmap::kLeaking)); + delegate.simulateValveFaultNoFailure = true; + + DataModel::Nullable valElapsedSNullable; + DataModel::Nullable valEnumNullable; + DataModel::Nullable valPercentNullable; + BitMask valBitmap; + + gSystemLayerAndClock.SetMonotonic(0_ms64); + gSystemLayerAndClock.Clear(); + DataModel::Nullable openDuration; + openDuration.SetNonNull(2u); + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::nullopt), CHIP_NO_ERROR); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 0); + + gSystemLayerAndClock.AdvanceMonotonic(2000_ms64); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); + + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetCurrentState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::Nullable(ValveStateEnum::kClosed)); + + EXPECT_EQ(logic.GetTargetState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetCurrentLevel(valPercentNullable), CHIP_NO_ERROR); + EXPECT_EQ(valPercentNullable, DataModel::Nullable(0)); + + EXPECT_EQ(logic.GetTargetLevel(valPercentNullable), CHIP_NO_ERROR); + EXPECT_EQ(valPercentNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetValveFault(valBitmap), CHIP_NO_ERROR); + EXPECT_EQ(valBitmap, BitMask(delegate.simulatedFailureBitMask)); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCalledAtOpenDurationNoLevelOkFaultReturned) +{ + TestDelegateNoLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { + .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = true, .supportsLevelStep = false + }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + delegate.simulatedFailureBitMask = BitMask(to_underlying(ValveFaultBitmap::kLeaking)); + delegate.simulateValveFaultNoFailure = true; + + DataModel::Nullable valElapsedSNullable; + DataModel::Nullable valEnumNullable; + BitMask valBitmap; + + gSystemLayerAndClock.SetMonotonic(0_ms64); + gSystemLayerAndClock.Clear(); + DataModel::Nullable openDuration; + openDuration.SetNonNull(2u); + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::nullopt), CHIP_NO_ERROR); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 0); + + gSystemLayerAndClock.AdvanceMonotonic(2000_ms64); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); + + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetCurrentState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::Nullable(ValveStateEnum::kClosed)); + + EXPECT_EQ(logic.GetTargetState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetValveFault(valBitmap), CHIP_NO_ERROR); + EXPECT_EQ(valBitmap, BitMask(delegate.simulatedFailureBitMask)); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCalledAtOpenDurationLevelFailure) +{ + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + delegate.simulateCloseFailure = true; + + DataModel::Nullable valElapsedSNullable; + DataModel::Nullable valEnumNullable; + DataModel::Nullable valPercentNullable; + BitMask valBitmap; + + gSystemLayerAndClock.SetMonotonic(0_ms64); + gSystemLayerAndClock.Clear(); + DataModel::Nullable openDuration; + openDuration.SetNonNull(2u); + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::nullopt), CHIP_NO_ERROR); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 0); + + gSystemLayerAndClock.AdvanceMonotonic(2000_ms64); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); + + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetCurrentState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetTargetState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetCurrentLevel(valPercentNullable), CHIP_NO_ERROR); + EXPECT_EQ(valPercentNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetTargetLevel(valPercentNullable), CHIP_NO_ERROR); + EXPECT_EQ(valPercentNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetValveFault(valBitmap), CHIP_NO_ERROR); + EXPECT_EQ(valBitmap, BitMask(delegate.simulatedFailureBitMask)); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCalledAtOpenDurationNoLevelFailure) +{ + TestDelegateNoLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { + .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = true, .supportsLevelStep = false + }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + delegate.simulateCloseFailure = true; + + DataModel::Nullable valElapsedSNullable; + DataModel::Nullable valEnumNullable; + BitMask valBitmap; + + gSystemLayerAndClock.SetMonotonic(0_ms64); + gSystemLayerAndClock.Clear(); + DataModel::Nullable openDuration; + openDuration.SetNonNull(2u); + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::nullopt), CHIP_NO_ERROR); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 0); + + gSystemLayerAndClock.AdvanceMonotonic(2000_ms64); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); + + EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); + EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetCurrentState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetTargetState(valEnumNullable), CHIP_NO_ERROR); + EXPECT_EQ(valEnumNullable, DataModel::NullNullable); + + EXPECT_EQ(logic.GetValveFault(valBitmap), CHIP_NO_ERROR); + EXPECT_EQ(valBitmap, BitMask(delegate.simulatedFailureBitMask)); +} //========================================================================================= // Tests for handling close commands From 8bf5e5ab65b483cb1d22eb8942093b7c0fc065f9 Mon Sep 17 00:00:00 2001 From: cecille Date: Sun, 15 Sep 2024 22:09:42 -0400 Subject: [PATCH 06/31] Valve: disco ball - Handle close command --- ...onfiguration-and-control-cluster-logic.cpp | 11 +- ...-configuration-and-control-cluster-logic.h | 12 +- .../TestValveConfigurationAndControl.cpp | 225 +++++++++++++++++- 3 files changed, 235 insertions(+), 13 deletions(-) diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp index a31b626f066583..19db47e8162b69 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp @@ -454,9 +454,15 @@ CHIP_ERROR ClusterLogic::HandleOpenCommand(std::optional faults; if (mConformance.HasFeature(Feature::kLevel)) @@ -497,6 +503,7 @@ void ClusterLogic::HandleCloseInternal() mState.SetTargetLevel(DataModel::NullNullable); mState.SetTargetState(DataModel::NullNullable); mState.SetAutoCloseTime(DataModel::NullNullable); + return err; } void ClusterLogic::HandleUpdateRemainingDuration(System::Layer * systemLayer, void * context) diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h index 247abf9a563c67..2d1b61c9f86827 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h @@ -170,12 +170,16 @@ class ClusterLogic // Current state // Current level - // Return CHIP_ERROR_INCORRECT_STATE if the class has not been initialized. - // Return CHIP_ERROR_INVALID_ARGUMENT if the input values are out is out of range or the targetLevel is supplied when LVL is not - // supported. + // Returns CHIP_ERROR_INCORRECT_STATE if the class has not been initialized. + // Returns CHIP_ERROR_INVALID_ARGUMENT if the input values are out is out of range or the targetLevel is supplied when LVL is + // not supported. // Calls delegate HandleOpen function after validating the parameters CHIP_ERROR HandleOpenCommand(std::optional> openDuration, std::optional targetLevel); + // Returns CHIP_ERROR_INCORRECT_STATE if the class has not been initialized. + // Calls delegate HandleClose function after validating the parameters and stops any open duration timers. + CHIP_ERROR HandleCloseCommand(); + private: // Determines if the level value is allowed per the level step. bool ValueCompliesWithLevelStep(const uint8_t value); @@ -193,7 +197,7 @@ class ClusterLogic void HandleUpdateRemainingDurationInternal(); // Internal function called by HandleUpdateRemainingDuration to call the close function in the delegate and // set all the attributes back to their closed state. - void HandleCloseInternal(); + CHIP_ERROR HandleCloseInternal(); bool mInitialized = false; System::Clock::Milliseconds64 mDurationStarted = System::Clock::Milliseconds64(0); diff --git a/src/app/tests/TestValveConfigurationAndControl.cpp b/src/app/tests/TestValveConfigurationAndControl.cpp index 073be82479b807..5d138ba82133f4 100644 --- a/src/app/tests/TestValveConfigurationAndControl.cpp +++ b/src/app/tests/TestValveConfigurationAndControl.cpp @@ -32,7 +32,7 @@ namespace chip { namespace System { -// TODO: This might be worthwhile to generalize +// TODO: This might be worthwhile to generalize and put into the system layer, but it too will need unit tests. class TimerAndMockClock : public Clock::Internal::MockClock, public Layer { public: @@ -268,7 +268,6 @@ class TestDelegateNoLevel : public NonLevelControlDelegate ValveStateEnum target = ValveStateEnum::kUnknownEnumValue; }; -// TODO: This also might be good to generalize, by using a common matter context for each cluster with allowed overrides class MockedMatterContext : public MatterContext { public: @@ -1947,9 +1946,222 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCalledAtOpenDurati //========================================================================================= // Tests for handling close commands //========================================================================================= -// while remaining duration is null and valve is open // while remaining duration is not null and valve is open + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandOpenValveDurationLevel) +{ + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + gSystemLayerAndClock.SetMonotonic(0_ms64); + gSystemLayerAndClock.Clear(); + DataModel::Nullable openDuration; + openDuration.SetNonNull(2u); + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::nullopt), CHIP_NO_ERROR); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 0); + + gSystemLayerAndClock.AdvanceMonotonic(1000_ms64); + EXPECT_EQ(logic.HandleCloseCommand(), CHIP_NO_ERROR); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); + + // Ensure the timer was cancelled + gSystemLayerAndClock.AdvanceMonotonic(1000_ms64); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandOpenValveDurationNoLevel) +{ + TestDelegateNoLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { + .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = true, .supportsLevelStep = false + }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + gSystemLayerAndClock.SetMonotonic(0_ms64); + gSystemLayerAndClock.Clear(); + DataModel::Nullable openDuration; + openDuration.SetNonNull(2u); + EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::nullopt), CHIP_NO_ERROR); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 0); + + gSystemLayerAndClock.AdvanceMonotonic(1000_ms64); + EXPECT_EQ(logic.HandleCloseCommand(), CHIP_NO_ERROR); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); + + // Ensure the timer was cancelled + gSystemLayerAndClock.AdvanceMonotonic(1000_ms64); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); +} + +// while remaining duration is null and valve is open +TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandOpenValveNoDurationLevel) +{ + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + gSystemLayerAndClock.SetMonotonic(0_ms64); + gSystemLayerAndClock.Clear(); + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 0); + + gSystemLayerAndClock.AdvanceMonotonic(1000_ms64); + EXPECT_EQ(logic.HandleCloseCommand(), CHIP_NO_ERROR); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandOpenValveNoDurationNoLevel) +{ + TestDelegateNoLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { + .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = true, .supportsLevelStep = false + }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + gSystemLayerAndClock.SetMonotonic(0_ms64); + gSystemLayerAndClock.Clear(); + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 0); + + gSystemLayerAndClock.AdvanceMonotonic(1000_ms64); + EXPECT_EQ(logic.HandleCloseCommand(), CHIP_NO_ERROR); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); +} + // while valve is closed +TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandClosedLevel) +{ + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + gSystemLayerAndClock.SetMonotonic(0_ms64); + gSystemLayerAndClock.Clear(); + + EXPECT_EQ(logic.HandleCloseCommand(), CHIP_NO_ERROR); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandClosedNoLevel) +{ + TestDelegateNoLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { + .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = true, .supportsLevelStep = false + }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + gSystemLayerAndClock.SetMonotonic(0_ms64); + gSystemLayerAndClock.Clear(); + + EXPECT_EQ(logic.HandleCloseCommand(), CHIP_NO_ERROR); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); +} + +// Errors when Init hasn't been called. +TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandClosedBeforeInit) +{ + TestDelegateNoLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + EXPECT_EQ(logic.HandleCloseCommand(), CHIP_ERROR_INCORRECT_STATE); +} + +// Error returns when delegate retuns error on close command +TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandReturnsErrorLevel) +{ + TestDelegateLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync), + .supportsDefaultOpenLevel = true, + .supportsValveFault = true, + .supportsLevelStep = true }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + delegate.simulateCloseFailure = true; + + gSystemLayerAndClock.SetMonotonic(0_ms64); + gSystemLayerAndClock.Clear(); + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 0); + + gSystemLayerAndClock.AdvanceMonotonic(1000_ms64); + EXPECT_EQ(logic.HandleCloseCommand(), CHIP_ERROR_INTERNAL); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); +} + +TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCommandReturnsErrorNoLevel) +{ + TestDelegateNoLevel delegate; + TestPersistentStorageDelegate storageDelegate; + EndpointId endpoint = 0; + MockedMatterContext context(endpoint, storageDelegate); + ClusterLogic logic(delegate, context); + + ClusterConformance conformance = { + .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = true, .supportsLevelStep = false + }; + EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR); + + delegate.simulateCloseFailure = true; + + gSystemLayerAndClock.SetMonotonic(0_ms64); + gSystemLayerAndClock.Clear(); + EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 0); + + gSystemLayerAndClock.AdvanceMonotonic(1000_ms64); + EXPECT_EQ(logic.HandleCloseCommand(), CHIP_ERROR_INTERNAL); + EXPECT_EQ(delegate.numHandleCloseValveCalls, 1); +} //========================================================================================= // Tests for timing for async read updates to current / target level and state @@ -1972,10 +2184,9 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestCloseCalledAtOpenDurati //========================================================================================= // Tests for attribute callbacks from delegates //========================================================================================= -// TODO: Should the delegate call the cluster logic class direclty, or should this be piped through the delegate class so the app -// layer ONLY has to interact with the delegate? -// Test setter for valve fault Test attribute change notifications are sent out and not -// sent out when the attribute is change do the same value - add in prior +// TODO: Should the delegate call the cluster logic class direclty, or should this be piped through the delegate class so the +// app layer ONLY has to interact with the delegate? Test setter for valve fault Test attribute change notifications are sent +// out and not sent out when the attribute is change do the same value - add in prior //========================================================================================= // Tests for attribute callbacks from delegates From 2e4f0ffc0abf23bc5e5fb84ebaebe010e345b95f Mon Sep 17 00:00:00 2001 From: cecille Date: Mon, 16 Sep 2024 14:13:46 -0400 Subject: [PATCH 07/31] Valve: Disco ball - Access and command interfaces --- src/app/chip_data_model.gni | 2 + ...-configuration-and-control-cluster-logic.h | 2 +- ...nfiguration-and-control-matter-context.cpp | 6 +- ...configuration-and-control-matter-context.h | 2 +- ...configuration-and-control-server-disco.cpp | 195 ++++++++++++++++++ ...e-configuration-and-control-server-disco.h | 63 ++++++ src/app/tests/BUILD.gn | 1 + 7 files changed, 267 insertions(+), 4 deletions(-) create mode 100644 src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server-disco.cpp create mode 100644 src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server-disco.h diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index a260fed3842fc6..d1dcd5c17aa64e 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -451,6 +451,8 @@ template("chip_data_model") { "${_app_root}/clusters/${cluster}/valve-configuration-and-control-delegate.h", "${_app_root}/clusters/${cluster}/valve-configuration-and-control-matter-context.cpp", "${_app_root}/clusters/${cluster}/valve-configuration-and-control-matter-context.h", + "${_app_root}/clusters/${cluster}/valve-configuration-and-control-server-disco.cpp", + "${_app_root}/clusters/${cluster}/valve-configuration-and-control-server-disco.h", ] cflags += [ "-Wno-unused-private-field" ] } else { diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h index 2d1b61c9f86827..2704af52732cab 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h @@ -129,7 +129,7 @@ class ClusterLogic // CHIP_ERROR HandleClose(); // All Get functions: - // Return CHIP_ERROR_INVALID_STATE if the class has not been initialized. + // Return CHIP_ERROR_INCORRECT_STATE if the class has not been initialized. // Return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE if the attribute is not supported by the conformance. // Otherwise return CHIP_NO_ERROR and set the input parameter value to the current cluster state value CHIP_ERROR GetOpenDuration(DataModel::Nullable & openDuration); diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.cpp index ea1bbdb9849f89..cb6ef4b7f84613 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.cpp +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.cpp @@ -18,6 +18,8 @@ #include "valve-configuration-and-control-matter-context.h" +#include +#include #include namespace chip { @@ -59,9 +61,9 @@ CHIP_ERROR MatterContext::GetDefaultOpenLevel(uint8_t & returnVal) &returnVal, size); } -void MatterContext::MarkDirty(const AttributeId id) +void MatterContext::MarkDirty(const AttributeId attributeId) { - // Uh...how do we do this? + MatterReportingAttributeChangeCallback(mEndpoint, Id, attributeId); } } // namespace ValveConfigurationAndControl diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h index 40d8ec79f11007..93707b3b85d4cd 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h @@ -53,7 +53,7 @@ class MatterContext CHIP_ERROR GetDefaultOpenLevel(uint8_t & returnVal); // MarkDirty - virtual void MarkDirty(AttributeId id); + virtual void MarkDirty(AttributeId attributeId); virtual ~MatterContext() = default; diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server-disco.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server-disco.cpp new file mode 100644 index 00000000000000..6a7eec566633b2 --- /dev/null +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server-disco.cpp @@ -0,0 +1,195 @@ +/** + * + * 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 "valve-configuration-and-control-server-disco.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace ValveConfigurationAndControl { + +using namespace Attributes; +using namespace Commands; +using namespace Protocols::InteractionModel; +namespace { + +template +CHIP_ERROR EncodeRead(AttributeValueEncoder & aEncoder, const F & getter) +{ + T ret; + CHIP_ERROR err = getter(ret); + if (err == CHIP_NO_ERROR) + { + err = aEncoder.Encode(ret); + } + + // TODO: Should the logic return these directly? I didn't want to mix the IM layer into there, but this is annoying. + if (err == CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE) + { + return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); + } + if (err == CHIP_ERROR_INCORRECT_STATE) + { + // what actually gets returned here? This is really an internal error, so failure seems perhaps correct. + return CHIP_IM_GLOBAL_STATUS(Failure); + } + return err; +} + +} // namespace + +CHIP_ERROR Interface::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + switch (aPath.mAttributeId) + { + case OpenDuration::Id: { + typedef OpenDuration::TypeInfo::Type T; + return EncodeRead(aEncoder, [&logic = mClusterLogic](T & ret) -> CHIP_ERROR { return logic.GetOpenDuration(ret); }); + } + case DefaultOpenDuration::Id: { + typedef DefaultOpenDuration::TypeInfo::Type T; + return EncodeRead(aEncoder, + [&logic = mClusterLogic](T & ret) -> CHIP_ERROR { return logic.GetDefaultOpenDuration(ret); }); + } + case AutoCloseTime::Id: { + typedef AutoCloseTime::TypeInfo::Type T; + return EncodeRead(aEncoder, [&logic = mClusterLogic](T & ret) -> CHIP_ERROR { return logic.GetAutoCloseTime(ret); }); + } + case RemainingDuration::Id: { + typedef RemainingDuration::TypeInfo::Type T; + return EncodeRead(aEncoder, [&logic = mClusterLogic](T & ret) -> CHIP_ERROR { return logic.GetRemainingDuration(ret); }); + } + case CurrentState::Id: { + typedef CurrentState::TypeInfo::Type T; + return EncodeRead(aEncoder, [&logic = mClusterLogic](T & ret) -> CHIP_ERROR { return logic.GetCurrentState(ret); }); + } + case TargetState::Id: { + typedef TargetState::TypeInfo::Type T; + return EncodeRead(aEncoder, [&logic = mClusterLogic](T & ret) -> CHIP_ERROR { return logic.GetTargetState(ret); }); + } + case CurrentLevel::Id: { + typedef CurrentLevel::TypeInfo::Type T; + return EncodeRead(aEncoder, [&logic = mClusterLogic](T & ret) -> CHIP_ERROR { return logic.GetCurrentLevel(ret); }); + } + case TargetLevel::Id: { + typedef TargetLevel::TypeInfo::Type T; + return EncodeRead(aEncoder, [&logic = mClusterLogic](T & ret) -> CHIP_ERROR { return logic.GetTargetLevel(ret); }); + } + case DefaultOpenLevel::Id: { + typedef DefaultOpenLevel::TypeInfo::Type T; + return EncodeRead(aEncoder, [&logic = mClusterLogic](T & ret) -> CHIP_ERROR { return logic.GetDefaultOpenLevel(ret); }); + } + case ValveFault::Id: { + typedef ValveFault::TypeInfo::Type T; + return EncodeRead(aEncoder, [&logic = mClusterLogic](T & ret) -> CHIP_ERROR { return logic.GetValveFault(ret); }); + } + case LevelStep::Id: { + typedef LevelStep::TypeInfo::Type T; + return EncodeRead(aEncoder, [&logic = mClusterLogic](T & ret) -> CHIP_ERROR { return logic.GetLevelStep(ret); }); + } + default: + return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); + } +} + +CHIP_ERROR Interface::Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) +{ + switch (aPath.mAttributeId) + { + case DefaultOpenDuration::Id: { + DefaultOpenDuration::TypeInfo::Type val; + ReturnErrorOnFailure(aDecoder.Decode(val)); + return mClusterLogic.SetDefaultOpenDuration(val); + } + case DefaultOpenLevel::Id: { + DefaultOpenLevel::TypeInfo::Type val; + ReturnErrorOnFailure(aDecoder.Decode(val)); + return mClusterLogic.SetDefaultOpenLevel(val); + } + default: + return CHIP_IM_GLOBAL_STATUS(UnsupportedWrite); + } +} + +// CommandHandlerInterface +void Interface::InvokeCommand(HandlerContext & handlerContext) +{ + switch (handlerContext.mRequestPath.mCommandId) + { + case Open::Id: + HandleCommand( + handlerContext, [&logic = mClusterLogic](HandlerContext & ctx, const auto & commandData) { + // TODO: I used optional in the lower layers because I think we want to move to std::optional in general + // So here, I need to change over. But I can also change the Logic cluster to use Optional + CHIP_ERROR err = + logic.HandleOpenCommand(commandData.openDuration.std_optional(), commandData.targetLevel.std_optional()); + Status status = Status::Success; + if (err == CHIP_ERROR_INVALID_ARGUMENT) + { + status = Status::ConstraintError; + } + if (err != CHIP_NO_ERROR) + { + status = Status::Failure; + } + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); + }); + return; + case Close::Id: + HandleCommand(handlerContext, + [&logic = mClusterLogic](HandlerContext & ctx, const auto & commandData) { + CHIP_ERROR err = logic.HandleCloseCommand(); + Status status = Status::Success; + if (err == CHIP_ERROR_INVALID_ARGUMENT) + { + status = Status::ConstraintError; + } + if (err != CHIP_NO_ERROR) + { + status = Status::Failure; + } + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); + }); + return; + } +} + +CHIP_ERROR Interface::Init() +{ + AttributeAccessInterfaceRegistry::Instance().Register(this); + CommandHandlerInterfaceRegistry::Instance().RegisterCommandHandler(this); + return CHIP_NO_ERROR; +} + +} // namespace ValveConfigurationAndControl +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server-disco.h b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server-disco.h new file mode 100644 index 00000000000000..14b96c212d1293 --- /dev/null +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server-disco.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 +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace ValveConfigurationAndControl { + +// App should instantiate and init one Interface per endpoint +class Interface : public AttributeAccessInterface, public CommandHandlerInterface +{ +public: + Interface(EndpointId endpoint, ClusterLogic & clusterLogic) : + AttributeAccessInterface(Optional(endpoint), Id), CommandHandlerInterface(Optional(endpoint), Id), + mClusterLogic(clusterLogic) + {} + // AttributeAccessInterface + CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; + CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) override; + + // CommandHandlerInterface + void InvokeCommand(HandlerContext & handlerContext) override; + + // Registers this handler. + CHIP_ERROR Init(); + + // TODO: Shutdown - is there a way to unregister? + +private: + // This is owned by the caller and passed to the interface for its use. + ClusterLogic & mClusterLogic; +}; + +} // namespace ValveConfigurationAndControl +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index 00e73b1f8eae4f..a14c9543d9ba35 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -164,6 +164,7 @@ source_set("valve-configuration-and-control-test-srcs") { "${chip_root}/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h", ] public_deps = [ + "${chip_root}/src/app:interaction-model", "${chip_root}/src/app/common:cluster-objects", "${chip_root}/src/lib/core", "${chip_root}/src/lib/support", From 83d5b798526757a4029ab80208bcc9766ead0ad7 Mon Sep 17 00:00:00 2001 From: cecille Date: Tue, 17 Sep 2024 13:34:10 -0400 Subject: [PATCH 08/31] Valve: Disco ball - add example app --- examples/valve-app/linux/.gn | 25 + examples/valve-app/linux/BUILD.gn | 55 + examples/valve-app/linux/args.gni | 25 + examples/valve-app/linux/build_overrides | 1 + .../linux/include/CHIPProjectAppConfig.h | 38 + examples/valve-app/linux/main.cpp | 119 + .../linux/third_party/connectedhomeip | 1 + examples/valve-app/valve-common/BUILD.gn | 21 + .../valve-app/valve-common/valve-app.matter | 1712 ++++++ examples/valve-app/valve-common/valve-app.zap | 5229 +++++++++++++++++ ...valve-configuration-and-control-server.cpp | 2 +- 11 files changed, 7227 insertions(+), 1 deletion(-) create mode 100644 examples/valve-app/linux/.gn create mode 100644 examples/valve-app/linux/BUILD.gn create mode 100644 examples/valve-app/linux/args.gni create mode 120000 examples/valve-app/linux/build_overrides create mode 100644 examples/valve-app/linux/include/CHIPProjectAppConfig.h create mode 100644 examples/valve-app/linux/main.cpp create mode 120000 examples/valve-app/linux/third_party/connectedhomeip create mode 100755 examples/valve-app/valve-common/BUILD.gn create mode 100644 examples/valve-app/valve-common/valve-app.matter create mode 100644 examples/valve-app/valve-common/valve-app.zap diff --git a/examples/valve-app/linux/.gn b/examples/valve-app/linux/.gn new file mode 100644 index 00000000000000..5d1ce757507582 --- /dev/null +++ b/examples/valve-app/linux/.gn @@ -0,0 +1,25 @@ +# Copyright (c) 2020 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") + +# The location of the build configuration file. +buildconfig = "${build_root}/config/BUILDCONFIG.gn" + +# CHIP uses angle bracket includes. +check_system_includes = true + +default_args = { + import("//args.gni") +} diff --git a/examples/valve-app/linux/BUILD.gn b/examples/valve-app/linux/BUILD.gn new file mode 100644 index 00000000000000..86ea7a3900e210 --- /dev/null +++ b/examples/valve-app/linux/BUILD.gn @@ -0,0 +1,55 @@ +# 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. + +import("//build_overrides/chip.gni") + +import("${chip_root}/build/chip/tools.gni") +import("${chip_root}/src/app/common_flags.gni") + +assert(chip_build_tools) + +import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni") + +config("includes") { + include_dirs = [ + ".", + "include", + ] +} + +executable("valve-app") { + sources = [ + "include/CHIPProjectAppConfig.h", + "main.cpp", + ] + + deps = [ + "${chip_root}/examples/platform/linux:app-main", + "${chip_root}/examples/valve-app/valve-common", + "${chip_root}/src/lib", + ] + + cflags = [ "-Wconversion" ] + + include_dirs = [ "include" ] + output_dir = root_out_dir +} + +group("linux") { + deps = [ ":valve-app" ] +} + +group("default") { + deps = [ ":linux" ] +} diff --git a/examples/valve-app/linux/args.gni b/examples/valve-app/linux/args.gni new file mode 100644 index 00000000000000..b91c0bafdfc9aa --- /dev/null +++ b/examples/valve-app/linux/args.gni @@ -0,0 +1,25 @@ +# 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. + +import("//build_overrides/chip.gni") + +import("${chip_root}/config/standalone/args.gni") + +chip_device_project_config_include = "" +chip_project_config_include = "" +chip_system_project_config_include = "" + +chip_project_config_include_dirs = + [ "${chip_root}/examples/air-purifier-app/linux/include" ] +chip_project_config_include_dirs += [ "${chip_root}/config/standalone" ] diff --git a/examples/valve-app/linux/build_overrides b/examples/valve-app/linux/build_overrides new file mode 120000 index 00000000000000..e578e73312ebd1 --- /dev/null +++ b/examples/valve-app/linux/build_overrides @@ -0,0 +1 @@ +../../build_overrides \ No newline at end of file diff --git a/examples/valve-app/linux/include/CHIPProjectAppConfig.h b/examples/valve-app/linux/include/CHIPProjectAppConfig.h new file mode 100644 index 00000000000000..4db52afa30ad47 --- /dev/null +++ b/examples/valve-app/linux/include/CHIPProjectAppConfig.h @@ -0,0 +1,38 @@ +/* + * + * 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 the CHIPProjectConfig from config/standalone +#include + +#define CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY 0 + +#define CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT 0 + +#define CHIP_DEVICE_CONFIG_ENABLE_EXTENDED_DISCOVERY 1 + +#define CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONABLE_DEVICE_TYPE 1 + +#define CHIP_DEVICE_CONFIG_DEVICE_TYPE 0x0042 // Water valve + +#define CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONABLE_DEVICE_NAME 1 + +#define CHIP_DEVICE_ENABLE_PORT_PARAMS 1 + +#define CHIP_DEVICE_CONFIG_DEVICE_NAME "DISCO Drinks" diff --git a/examples/valve-app/linux/main.cpp b/examples/valve-app/linux/main.cpp new file mode 100644 index 00000000000000..b8f0000b7e5838 --- /dev/null +++ b/examples/valve-app/linux/main.cpp @@ -0,0 +1,119 @@ +/* + * + * 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::Clusters; +using namespace chip::app::Clusters::ValveConfigurationAndControl; +namespace { +class PrintOnlyDelegate : public NonLevelControlDelegate +{ +public: + PrintOnlyDelegate(EndpointId endpoint) : mEndpoint(endpoint) {} + CHIP_ERROR HandleOpenValve(ValveStateEnum & currentState, BitMask & valveFault) override + { + ChipLogProgress(NotSpecified, "VALVE IS OPENING!!!!!"); + state = ValveStateEnum::kOpen; + currentState = state; + return CHIP_NO_ERROR; + } + ValveStateEnum GetCurrentValveState() override { return state; } + CHIP_ERROR HandleCloseValve(ValveStateEnum & currentState, BitMask & valveFault) override + { + ChipLogProgress(NotSpecified, "VALVE IS CLOSING!!!!!"); + state = ValveStateEnum::kClosed; + currentState = state; + return CHIP_NO_ERROR; + } + +private: + ValveStateEnum state = ValveStateEnum::kClosed; + EndpointId mEndpoint; +}; + +class NonLevelValveEndpoint +{ +public: + NonLevelValveEndpoint(EndpointId endpoint) : + mEndpoint(endpoint), mContext(mEndpoint, storage), mDelegate(mEndpoint), mLogic(mDelegate, mContext), + mInterface(mEndpoint, mLogic) + {} + CHIP_ERROR Init() + { + ReturnErrorOnFailure(mLogic.Init(kConformance, kInitParams)); + ReturnErrorOnFailure(mInterface.Init()); + return CHIP_NO_ERROR; + } + +private: + const ClusterConformance kConformance = { + .featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = false, .supportsLevelStep = false + }; + const ClusterInitParameters kInitParams = { .currentState = DataModel::MakeNullable(ValveStateEnum::kClosed), + .currentLevel = DataModel::NullNullable, + .valveFault = 0, + .levelStep = 1 }; + EndpointId mEndpoint; + KvsPersistentStorageDelegate storage; + MatterContext mContext; + PrintOnlyDelegate mDelegate; + ClusterLogic mLogic; + Interface mInterface; +}; + +NonLevelValveEndpoint ep1(1); +NonLevelValveEndpoint ep2(2); +NonLevelValveEndpoint ep3(3); +NonLevelValveEndpoint ep4(4); +NonLevelValveEndpoint ep5(5); +NonLevelValveEndpoint ep6(6); +} // namespace + +void ApplicationInit() +{ + ChipLogError(NotSpecified, "App init!!!"); + ep1.Init(); + ep2.Init(); + ep3.Init(); + ep4.Init(); + ep5.Init(); + ep6.Init(); +} + +void ApplicationShutdown() +{ + ChipLogDetail(NotSpecified, "ApplicationShutdown()"); +} + +int main(int argc, char * argv[]) +{ + if (ChipLinuxAppInit(argc, argv) != 0) + { + return -1; + } + + ChipLinuxAppMainLoop(); + return 0; +} diff --git a/examples/valve-app/linux/third_party/connectedhomeip b/examples/valve-app/linux/third_party/connectedhomeip new file mode 120000 index 00000000000000..11a54ed360106c --- /dev/null +++ b/examples/valve-app/linux/third_party/connectedhomeip @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/examples/valve-app/valve-common/BUILD.gn b/examples/valve-app/valve-common/BUILD.gn new file mode 100755 index 00000000000000..d74baf0ee7c197 --- /dev/null +++ b/examples/valve-app/valve-common/BUILD.gn @@ -0,0 +1,21 @@ +# Copyright (c) 2020 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/chip.gni") +import("${chip_root}/src/app/chip_data_model.gni") + +chip_data_model("valve-common") { + zap_file = "valve-app.zap" + is_server = true +} diff --git a/examples/valve-app/valve-common/valve-app.matter b/examples/valve-app/valve-common/valve-app.matter new file mode 100644 index 00000000000000..d5ee79a78b9deb --- /dev/null +++ b/examples/valve-app/valve-common/valve-app.matter @@ -0,0 +1,1712 @@ +// This IDL was generated automatically by ZAP. +// It is for view/code review purposes only. + +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; +} + +enum AtomicRequestTypeEnum : enum8 { + kBeginWrite = 0; + kCommitWrite = 1; + kRollbackWrite = 2; +} + +enum FloorSurfaceTag : enum8 { + kCarpet = 0; + kCeramic = 1; + kConcrete = 2; + kCork = 3; + kDeepCarpet = 4; + kDirt = 5; + kEngineeredWood = 6; + kGlass = 7; + kGrass = 8; + kHardwood = 9; + kLaminate = 10; + kLinoleum = 11; + kMat = 12; + kMetal = 13; + kPlastic = 14; + kPolishedConcrete = 15; + kRubber = 16; + kRug = 17; + kSand = 18; + kStone = 19; + kTatami = 20; + kTerrazzo = 21; + kTile = 22; + kVinyl = 23; +} + +enum LandmarkTag : enum8 { + kAirConditioner = 0; + kAirPurifier = 1; + kBackDoor = 2; + kBarStool = 3; + kBathMat = 4; + kBathtub = 5; + kBed = 6; + kBookshelf = 7; + kChair = 8; + kChristmasTree = 9; + kCoatRack = 10; + kCoffeeTable = 11; + kCookingRange = 12; + kCouch = 13; + kCountertop = 14; + kCradle = 15; + kCrib = 16; + kDesk = 17; + kDiningTable = 18; + kDishwasher = 19; + kDoor = 20; + kDresser = 21; + kLaundryDryer = 22; + kFan = 23; + kFireplace = 24; + kFreezer = 25; + kFrontDoor = 26; + kHighChair = 27; + kKitchenIsland = 28; + kLamp = 29; + kLitterBox = 30; + kMirror = 31; + kNightstand = 32; + kOven = 33; + kPetBed = 34; + kPetBowl = 35; + kPetCrate = 36; + kRefrigerator = 37; + kScratchingPost = 38; + kShoeRack = 39; + kShower = 40; + kSideDoor = 41; + kSink = 42; + kSofa = 43; + kStove = 44; + kTable = 45; + kToilet = 46; + kTrashCan = 47; + kLaundryWasher = 48; + kWindow = 49; + kWineCooler = 50; +} + +enum PositionTag : enum8 { + kLeft = 0; + kRight = 1; + kTop = 2; + kBottom = 3; + kMiddle = 4; + kRow = 5; + kColumn = 6; +} + +enum RelativePositionTag : enum8 { + kUnder = 0; + kNextTo = 1; + kAround = 2; + kOn = 3; + kAbove = 4; + kFrontOf = 5; + kBehind = 6; +} + +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; +} + +struct LocationDescriptorStruct { + char_string<128> locationName = 0; + nullable int16s floorNumber = 1; + nullable AreaTypeTag areaType = 2; +} + +struct AtomicAttributeStatusStruct { + attrib_id attributeID = 0; + status statusCode = 1; +} + +/** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ +cluster Identify = 3 { + revision 4; + + enum EffectIdentifierEnum : enum8 { + kBlink = 0; + kBreathe = 1; + kOkay = 2; + kChannelChange = 11; + kFinishEffect = 254; + kStopEffect = 255; + } + + enum EffectVariantEnum : enum8 { + kDefault = 0; + } + + enum IdentifyTypeEnum : enum8 { + kNone = 0; + kLightOutput = 1; + kVisibleIndicator = 2; + kAudibleBeep = 3; + kDisplay = 4; + kActuator = 5; + } + + attribute int16u identifyTime = 0; + readonly attribute IdentifyTypeEnum identifyType = 1; + 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 IdentifyRequest { + int16u identifyTime = 0; + } + + request struct TriggerEffectRequest { + EffectIdentifierEnum effectIdentifier = 0; + EffectVariantEnum effectVariant = 1; + } + + /** Command description for Identify */ + command access(invoke: manage) Identify(IdentifyRequest): DefaultSuccess = 0; + /** Command description for TriggerEffect */ + command access(invoke: manage) TriggerEffect(TriggerEffectRequest): DefaultSuccess = 64; +} + +/** 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; + + bitmap Feature : bitmap32 { + kTagList = 0x1; + } + + struct DeviceTypeStruct { + devtype_id deviceType = 0; + int16u revision = 1; + } + + struct SemanticTagStruct { + nullable vendor_id mfgCode = 0; + enum8 namespaceID = 1; + enum8 tag = 2; + optional nullable char_string label = 3; + } + + readonly attribute DeviceTypeStruct deviceTypeList[] = 0; + readonly attribute cluster_id serverList[] = 1; + readonly attribute cluster_id clientList[] = 2; + readonly attribute endpoint_no partsList[] = 3; + readonly attribute optional SemanticTagStruct tagList[] = 4; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; +} + +/** 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 + cluster instances. */ +cluster AccessControl = 31 { + revision 2; + + enum AccessControlEntryAuthModeEnum : enum8 { + kPASE = 1; + kCASE = 2; + kGroup = 3; + } + + enum AccessControlEntryPrivilegeEnum : enum8 { + kView = 1; + kProxyView = 2; + kOperate = 3; + kManage = 4; + kAdminister = 5; + } + + enum AccessRestrictionTypeEnum : enum8 { + kAttributeAccessForbidden = 0; + kAttributeWriteForbidden = 1; + kCommandForbidden = 2; + kEventForbidden = 3; + } + + enum ChangeTypeEnum : enum8 { + kChanged = 0; + kAdded = 1; + kRemoved = 2; + } + + bitmap Feature : bitmap32 { + kExtension = 0x1; + kManagedDevice = 0x2; + } + + struct AccessRestrictionStruct { + AccessRestrictionTypeEnum type = 0; + nullable int32u id = 1; + } + + struct CommissioningAccessRestrictionEntryStruct { + endpoint_no endpoint = 0; + cluster_id cluster = 1; + AccessRestrictionStruct restrictions[] = 2; + } + + fabric_scoped struct AccessRestrictionEntryStruct { + fabric_sensitive endpoint_no endpoint = 0; + fabric_sensitive cluster_id cluster = 1; + fabric_sensitive AccessRestrictionStruct restrictions[] = 2; + fabric_idx fabricIndex = 254; + } + + struct AccessControlTargetStruct { + nullable cluster_id cluster = 0; + nullable endpoint_no endpoint = 1; + nullable devtype_id deviceType = 2; + } + + fabric_scoped struct AccessControlEntryStruct { + fabric_sensitive AccessControlEntryPrivilegeEnum privilege = 1; + fabric_sensitive AccessControlEntryAuthModeEnum authMode = 2; + nullable fabric_sensitive int64u subjects[] = 3; + nullable fabric_sensitive AccessControlTargetStruct targets[] = 4; + fabric_idx fabricIndex = 254; + } + + fabric_scoped struct AccessControlExtensionStruct { + fabric_sensitive octet_string<128> data = 1; + fabric_idx fabricIndex = 254; + } + + fabric_sensitive info event access(read: administer) AccessControlEntryChanged = 0 { + nullable node_id adminNodeID = 1; + nullable int16u adminPasscodeID = 2; + ChangeTypeEnum changeType = 3; + nullable AccessControlEntryStruct latestValue = 4; + fabric_idx fabricIndex = 254; + } + + fabric_sensitive info event access(read: administer) AccessControlExtensionChanged = 1 { + nullable node_id adminNodeID = 1; + nullable int16u adminPasscodeID = 2; + ChangeTypeEnum changeType = 3; + nullable AccessControlExtensionStruct latestValue = 4; + fabric_idx fabricIndex = 254; + } + + fabric_sensitive info event access(read: administer) FabricRestrictionReviewUpdate = 2 { + int64u token = 0; + optional long_char_string instruction = 1; + optional long_char_string ARLRequestFlowUrl = 2; + fabric_idx fabricIndex = 254; + } + + attribute access(read: administer, write: administer) AccessControlEntryStruct acl[] = 0; + attribute access(read: administer, write: administer) optional AccessControlExtensionStruct extension[] = 1; + readonly attribute int16u subjectsPerAccessControlEntry = 2; + readonly attribute int16u targetsPerAccessControlEntry = 3; + readonly attribute int16u accessControlEntriesPerFabric = 4; + readonly attribute optional CommissioningAccessRestrictionEntryStruct commissioningARL[] = 5; + readonly attribute optional AccessRestrictionEntryStruct arl[] = 6; + 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 ReviewFabricRestrictionsRequest { + CommissioningAccessRestrictionEntryStruct arl[] = 0; + } + + response struct ReviewFabricRestrictionsResponse = 1 { + int64u token = 0; + } + + /** This command signals to the service associated with the device vendor that the fabric administrator would like a review of the current restrictions on the accessing fabric. */ + fabric command access(invoke: administer) ReviewFabricRestrictions(ReviewFabricRestrictionsRequest): ReviewFabricRestrictionsResponse = 0; +} + +/** This cluster provides attributes and events for determining basic information about Nodes, which supports both + Commissioning and operational determination of Node characteristics, such as Vendor ID, Product ID and serial number, + which apply to the whole Node. Also allows setting user device information such as location. */ +cluster BasicInformation = 40 { + revision 3; + + enum ColorEnum : enum8 { + kBlack = 0; + kNavy = 1; + kGreen = 2; + kTeal = 3; + kMaroon = 4; + kPurple = 5; + kOlive = 6; + kGray = 7; + kBlue = 8; + kLime = 9; + kAqua = 10; + kRed = 11; + kFuchsia = 12; + kYellow = 13; + kWhite = 14; + kNickel = 15; + kChrome = 16; + kBrass = 17; + kCopper = 18; + kSilver = 19; + kGold = 20; + } + + enum ProductFinishEnum : enum8 { + kOther = 0; + kMatte = 1; + kSatin = 2; + kPolished = 3; + kRugged = 4; + kFabric = 5; + } + + struct CapabilityMinimaStruct { + int16u caseSessionsPerFabric = 0; + int16u subscriptionsPerFabric = 1; + } + + struct ProductAppearanceStruct { + ProductFinishEnum finish = 0; + nullable ColorEnum primaryColor = 1; + } + + critical event StartUp = 0 { + int32u softwareVersion = 0; + } + + critical event ShutDown = 1 { + } + + info event Leave = 2 { + fabric_idx fabricIndex = 0; + } + + info event ReachableChanged = 3 { + boolean reachableNewValue = 0; + } + + readonly attribute int16u dataModelRevision = 0; + readonly attribute char_string<32> vendorName = 1; + readonly attribute vendor_id vendorID = 2; + readonly attribute char_string<32> productName = 3; + readonly attribute int16u productID = 4; + attribute access(write: manage) char_string<32> nodeLabel = 5; + attribute access(write: administer) char_string<2> location = 6; + readonly attribute int16u hardwareVersion = 7; + readonly attribute char_string<64> hardwareVersionString = 8; + readonly attribute int32u softwareVersion = 9; + readonly attribute char_string<64> softwareVersionString = 10; + readonly attribute optional char_string<16> manufacturingDate = 11; + readonly attribute optional char_string<32> partNumber = 12; + readonly attribute optional long_char_string<256> productURL = 13; + readonly attribute optional char_string<64> productLabel = 14; + readonly attribute optional char_string<32> serialNumber = 15; + attribute access(write: manage) optional boolean localConfigDisabled = 16; + readonly attribute optional boolean reachable = 17; + readonly attribute char_string<32> uniqueID = 18; + readonly attribute CapabilityMinimaStruct capabilityMinima = 19; + readonly attribute optional ProductAppearanceStruct productAppearance = 20; + readonly attribute int32u specificationVersion = 21; + readonly attribute int16u maxPathsPerInvoke = 22; + 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; + + command MfgSpecificPing(): DefaultSuccess = 0; +} + +/** This cluster is used to manage global aspects of the Commissioning flow. */ +cluster GeneralCommissioning = 48 { + revision 1; // NOTE: Default/not specifically set + + enum CommissioningErrorEnum : enum8 { + kOK = 0; + kValueOutsideRange = 1; + kInvalidAuthentication = 2; + kNoFailSafe = 3; + kBusyWithOtherAdmin = 4; + kRequiredTCNotAccepted = 5; + kTCAcknowledgementsNotReceived = 6; + kTCMinVersionNotMet = 7; + } + + enum RegulatoryLocationTypeEnum : enum8 { + kIndoor = 0; + kOutdoor = 1; + kIndoorOutdoor = 2; + } + + bitmap Feature : bitmap32 { + kTermsAndConditions = 0x1; + } + + struct BasicCommissioningInfo { + int16u failSafeExpiryLengthSeconds = 0; + int16u maxCumulativeFailsafeSeconds = 1; + } + + attribute access(write: administer) int64u breadcrumb = 0; + readonly attribute BasicCommissioningInfo basicCommissioningInfo = 1; + readonly attribute RegulatoryLocationTypeEnum regulatoryConfig = 2; + readonly attribute RegulatoryLocationTypeEnum locationCapability = 3; + readonly attribute boolean supportsConcurrentConnection = 4; + provisional readonly attribute access(read: administer) optional int16u TCAcceptedVersion = 5; + provisional readonly attribute access(read: administer) optional int16u TCMinRequiredVersion = 6; + provisional readonly attribute access(read: administer) optional bitmap16 TCAcknowledgements = 7; + provisional readonly attribute access(read: administer) optional boolean TCAcknowledgementsRequired = 8; + 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 ArmFailSafeRequest { + int16u expiryLengthSeconds = 0; + int64u breadcrumb = 1; + } + + response struct ArmFailSafeResponse = 1 { + CommissioningErrorEnum errorCode = 0; + char_string<128> debugText = 1; + } + + request struct SetRegulatoryConfigRequest { + RegulatoryLocationTypeEnum newRegulatoryConfig = 0; + char_string<2> countryCode = 1; + int64u breadcrumb = 2; + } + + response struct SetRegulatoryConfigResponse = 3 { + CommissioningErrorEnum errorCode = 0; + char_string debugText = 1; + } + + response struct CommissioningCompleteResponse = 5 { + CommissioningErrorEnum errorCode = 0; + char_string debugText = 1; + } + + request struct SetTCAcknowledgementsRequest { + int16u TCVersion = 0; + bitmap16 TCUserResponse = 1; + } + + response struct SetTCAcknowledgementsResponse = 7 { + CommissioningErrorEnum errorCode = 0; + } + + /** Arm the persistent fail-safe timer with an expiry time of now + ExpiryLengthSeconds using device clock */ + command access(invoke: administer) ArmFailSafe(ArmFailSafeRequest): ArmFailSafeResponse = 0; + /** Set the regulatory configuration to be used during commissioning */ + command access(invoke: administer) SetRegulatoryConfig(SetRegulatoryConfigRequest): SetRegulatoryConfigResponse = 2; + /** Signals the Server that the Client has successfully completed all steps of Commissioning/Recofiguration needed during fail-safe period. */ + fabric command access(invoke: administer) CommissioningComplete(): CommissioningCompleteResponse = 4; + /** This command sets the user acknowledgements received in the Enhanced Setup Flow Terms and Conditions into the node. */ + command access(invoke: administer) SetTCAcknowledgements(SetTCAcknowledgementsRequest): SetTCAcknowledgementsResponse = 6; +} + +/** Functionality to configure, enable, disable network credentials and access on a Matter device. */ +cluster NetworkCommissioning = 49 { + revision 1; // NOTE: Default/not specifically set + + enum NetworkCommissioningStatusEnum : enum8 { + kSuccess = 0; + kOutOfRange = 1; + kBoundsExceeded = 2; + kNetworkIDNotFound = 3; + kDuplicateNetworkID = 4; + kNetworkNotFound = 5; + kRegulatoryError = 6; + kAuthFailure = 7; + kUnsupportedSecurity = 8; + kOtherConnectionFailure = 9; + kIPV6Failed = 10; + kIPBindFailed = 11; + kUnknownError = 12; + } + + enum WiFiBandEnum : enum8 { + k2G4 = 0; + k3G65 = 1; + k5G = 2; + k6G = 3; + k60G = 4; + k1G = 5; + } + + bitmap Feature : bitmap32 { + kWiFiNetworkInterface = 0x1; + kThreadNetworkInterface = 0x2; + kEthernetNetworkInterface = 0x4; + kPerDeviceCredentials = 0x8; + } + + bitmap ThreadCapabilitiesBitmap : bitmap16 { + kIsBorderRouterCapable = 0x1; + kIsRouterCapable = 0x2; + kIsSleepyEndDeviceCapable = 0x4; + kIsFullThreadDevice = 0x8; + kIsSynchronizedSleepyEndDeviceCapable = 0x10; + } + + bitmap WiFiSecurityBitmap : bitmap8 { + kUnencrypted = 0x1; + kWEP = 0x2; + kWPAPersonal = 0x4; + kWPA2Personal = 0x8; + kWPA3Personal = 0x10; + kWPA3MatterPDC = 0x20; + } + + struct NetworkInfoStruct { + octet_string<32> networkID = 0; + boolean connected = 1; + optional nullable octet_string<20> networkIdentifier = 2; + optional nullable octet_string<20> clientIdentifier = 3; + } + + struct ThreadInterfaceScanResultStruct { + int16u panId = 0; + int64u extendedPanId = 1; + char_string<16> networkName = 2; + int16u channel = 3; + int8u version = 4; + octet_string<8> extendedAddress = 5; + int8s rssi = 6; + int8u lqi = 7; + } + + struct WiFiInterfaceScanResultStruct { + WiFiSecurityBitmap security = 0; + octet_string<32> ssid = 1; + octet_string<6> bssid = 2; + int16u channel = 3; + WiFiBandEnum wiFiBand = 4; + int8s rssi = 5; + } + + readonly attribute access(read: administer) int8u maxNetworks = 0; + readonly attribute access(read: administer) NetworkInfoStruct networks[] = 1; + readonly attribute optional int8u scanMaxTimeSeconds = 2; + readonly attribute optional int8u connectMaxTimeSeconds = 3; + attribute access(write: administer) boolean interfaceEnabled = 4; + readonly attribute access(read: administer) nullable NetworkCommissioningStatusEnum lastNetworkingStatus = 5; + readonly attribute access(read: administer) nullable octet_string<32> lastNetworkID = 6; + readonly attribute access(read: administer) nullable int32s lastConnectErrorValue = 7; + provisional readonly attribute optional WiFiBandEnum supportedWiFiBands[] = 8; + provisional readonly attribute optional ThreadCapabilitiesBitmap supportedThreadFeatures = 9; + provisional readonly attribute optional int16u threadVersion = 10; + 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 ScanNetworksRequest { + optional nullable octet_string<32> ssid = 0; + optional int64u breadcrumb = 1; + } + + response struct ScanNetworksResponse = 1 { + NetworkCommissioningStatusEnum networkingStatus = 0; + optional char_string debugText = 1; + optional WiFiInterfaceScanResultStruct wiFiScanResults[] = 2; + optional ThreadInterfaceScanResultStruct threadScanResults[] = 3; + } + + request struct AddOrUpdateWiFiNetworkRequest { + octet_string<32> ssid = 0; + octet_string<64> credentials = 1; + optional int64u breadcrumb = 2; + optional octet_string<140> networkIdentity = 3; + optional octet_string<20> clientIdentifier = 4; + optional octet_string<32> possessionNonce = 5; + } + + request struct AddOrUpdateThreadNetworkRequest { + octet_string<254> operationalDataset = 0; + optional int64u breadcrumb = 1; + } + + request struct RemoveNetworkRequest { + octet_string<32> networkID = 0; + optional int64u breadcrumb = 1; + } + + response struct NetworkConfigResponse = 5 { + NetworkCommissioningStatusEnum networkingStatus = 0; + optional char_string<512> debugText = 1; + optional int8u networkIndex = 2; + optional octet_string<140> clientIdentity = 3; + optional octet_string<64> possessionSignature = 4; + } + + request struct ConnectNetworkRequest { + octet_string<32> networkID = 0; + optional int64u breadcrumb = 1; + } + + response struct ConnectNetworkResponse = 7 { + NetworkCommissioningStatusEnum networkingStatus = 0; + optional char_string debugText = 1; + nullable int32s errorValue = 2; + } + + request struct ReorderNetworkRequest { + octet_string<32> networkID = 0; + int8u networkIndex = 1; + optional int64u breadcrumb = 2; + } + + request struct QueryIdentityRequest { + octet_string<20> keyIdentifier = 0; + optional octet_string<32> possessionNonce = 1; + } + + response struct QueryIdentityResponse = 10 { + octet_string<140> identity = 0; + optional octet_string<64> possessionSignature = 1; + } + + /** Detemine the set of networks the device sees as available. */ + command access(invoke: administer) ScanNetworks(ScanNetworksRequest): ScanNetworksResponse = 0; + /** Add or update the credentials for a given Wi-Fi network. */ + command access(invoke: administer) AddOrUpdateWiFiNetwork(AddOrUpdateWiFiNetworkRequest): NetworkConfigResponse = 2; + /** Add or update the credentials for a given Thread network. */ + command access(invoke: administer) AddOrUpdateThreadNetwork(AddOrUpdateThreadNetworkRequest): NetworkConfigResponse = 3; + /** Remove the definition of a given network (including its credentials). */ + command access(invoke: administer) RemoveNetwork(RemoveNetworkRequest): NetworkConfigResponse = 4; + /** Connect to the specified network, using previously-defined credentials. */ + command access(invoke: administer) ConnectNetwork(ConnectNetworkRequest): ConnectNetworkResponse = 6; + /** Modify the order in which networks will be presented in the Networks attribute. */ + command access(invoke: administer) ReorderNetwork(ReorderNetworkRequest): NetworkConfigResponse = 8; + /** Retrieve details about and optionally proof of possession of a network client identity. */ + command access(invoke: administer) QueryIdentity(QueryIdentityRequest): QueryIdentityResponse = 9; +} + +/** The General Diagnostics Cluster, along with other diagnostics clusters, provide a means to acquire standardized diagnostics metrics that MAY be used by a Node to assist a user or Administrative Node in diagnosing potential problems. */ +cluster GeneralDiagnostics = 51 { + revision 2; + + enum BootReasonEnum : enum8 { + kUnspecified = 0; + kPowerOnReboot = 1; + kBrownOutReset = 2; + kSoftwareWatchdogReset = 3; + kHardwareWatchdogReset = 4; + kSoftwareUpdateCompleted = 5; + kSoftwareReset = 6; + } + + enum HardwareFaultEnum : enum8 { + kUnspecified = 0; + kRadio = 1; + kSensor = 2; + kResettableOverTemp = 3; + kNonResettableOverTemp = 4; + kPowerSource = 5; + kVisualDisplayFault = 6; + kAudioOutputFault = 7; + kUserInterfaceFault = 8; + kNonVolatileMemoryError = 9; + kTamperDetected = 10; + } + + enum InterfaceTypeEnum : enum8 { + kUnspecified = 0; + kWiFi = 1; + kEthernet = 2; + kCellular = 3; + kThread = 4; + } + + enum NetworkFaultEnum : enum8 { + kUnspecified = 0; + kHardwareFailure = 1; + kNetworkJammed = 2; + kConnectionFailed = 3; + } + + enum RadioFaultEnum : enum8 { + kUnspecified = 0; + kWiFiFault = 1; + kCellularFault = 2; + kThreadFault = 3; + kNFCFault = 4; + kBLEFault = 5; + kEthernetFault = 6; + } + + bitmap Feature : bitmap32 { + kDataModelTest = 0x1; + } + + struct NetworkInterface { + char_string<32> name = 0; + boolean isOperational = 1; + nullable boolean offPremiseServicesReachableIPv4 = 2; + nullable boolean offPremiseServicesReachableIPv6 = 3; + octet_string<8> hardwareAddress = 4; + octet_string IPv4Addresses[] = 5; + octet_string IPv6Addresses[] = 6; + InterfaceTypeEnum type = 7; + } + + critical event HardwareFaultChange = 0 { + HardwareFaultEnum current[] = 0; + HardwareFaultEnum previous[] = 1; + } + + critical event RadioFaultChange = 1 { + RadioFaultEnum current[] = 0; + RadioFaultEnum previous[] = 1; + } + + critical event NetworkFaultChange = 2 { + NetworkFaultEnum current[] = 0; + NetworkFaultEnum previous[] = 1; + } + + critical event BootReason = 3 { + BootReasonEnum bootReason = 0; + } + + readonly attribute NetworkInterface networkInterfaces[] = 0; + readonly attribute int16u rebootCount = 1; + readonly attribute optional int64u upTime = 2; + readonly attribute optional int32u totalOperationalHours = 3; + readonly attribute optional BootReasonEnum bootReason = 4; + readonly attribute optional HardwareFaultEnum activeHardwareFaults[] = 5; + readonly attribute optional RadioFaultEnum activeRadioFaults[] = 6; + readonly attribute optional NetworkFaultEnum activeNetworkFaults[] = 7; + readonly attribute boolean testEventTriggersEnabled = 8; + 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 TestEventTriggerRequest { + octet_string<16> enableKey = 0; + int64u eventTrigger = 1; + } + + response struct TimeSnapshotResponse = 2 { + systime_ms systemTimeMs = 0; + nullable posix_ms posixTimeMs = 1; + } + + request struct PayloadTestRequestRequest { + octet_string<16> enableKey = 0; + int8u value = 1; + int16u count = 2; + } + + response struct PayloadTestResponse = 4 { + octet_string payload = 0; + } + + /** Provide a means for certification tests to trigger some test-plan-specific events */ + command access(invoke: manage) TestEventTrigger(TestEventTriggerRequest): DefaultSuccess = 0; + /** Take a snapshot of system time and epoch time. */ + command TimeSnapshot(): TimeSnapshotResponse = 1; + /** Request a variable length payload response. */ + command PayloadTestRequest(PayloadTestRequestRequest): PayloadTestResponse = 3; +} + +/** Commands to trigger a Node to allow a new Administrator to commission it. */ +cluster AdministratorCommissioning = 60 { + revision 1; // NOTE: Default/not specifically set + + enum CommissioningWindowStatusEnum : enum8 { + kWindowNotOpen = 0; + kEnhancedWindowOpen = 1; + kBasicWindowOpen = 2; + } + + enum StatusCode : enum8 { + kBusy = 2; + kPAKEParameterError = 3; + kWindowNotOpen = 4; + } + + bitmap Feature : bitmap32 { + kBasic = 0x1; + } + + readonly attribute CommissioningWindowStatusEnum windowStatus = 0; + readonly attribute nullable fabric_idx adminFabricIndex = 1; + readonly attribute nullable vendor_id adminVendorId = 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; + + request struct OpenCommissioningWindowRequest { + int16u commissioningTimeout = 0; + octet_string PAKEPasscodeVerifier = 1; + int16u discriminator = 2; + int32u iterations = 3; + octet_string<32> salt = 4; + } + + request struct OpenBasicCommissioningWindowRequest { + int16u commissioningTimeout = 0; + } + + /** This command is used by a current Administrator to instruct a Node to go into commissioning mode using enhanced commissioning method. */ + timed command access(invoke: administer) OpenCommissioningWindow(OpenCommissioningWindowRequest): DefaultSuccess = 0; + /** This command is used by a current Administrator to instruct a Node to go into commissioning mode using basic commissioning method, if the node supports it. */ + timed command access(invoke: administer) OpenBasicCommissioningWindow(OpenBasicCommissioningWindowRequest): DefaultSuccess = 1; + /** This command is used by a current Administrator to instruct a Node to revoke any active Open Commissioning Window or Open Basic Commissioning Window command. */ + timed command access(invoke: administer) RevokeCommissioning(): DefaultSuccess = 2; +} + +/** This cluster is used to add or remove Operational Credentials on a Commissionee or Node, as well as manage the associated Fabrics. */ +cluster OperationalCredentials = 62 { + revision 1; // NOTE: Default/not specifically set + + enum CertificateChainTypeEnum : enum8 { + kDACCertificate = 1; + kPAICertificate = 2; + } + + enum NodeOperationalCertStatusEnum : enum8 { + kOK = 0; + kInvalidPublicKey = 1; + kInvalidNodeOpId = 2; + kInvalidNOC = 3; + kMissingCsr = 4; + kTableFull = 5; + kInvalidAdminSubject = 6; + kFabricConflict = 9; + kLabelConflict = 10; + kInvalidFabricIndex = 11; + } + + fabric_scoped struct FabricDescriptorStruct { + octet_string<65> rootPublicKey = 1; + vendor_id vendorID = 2; + fabric_id fabricID = 3; + node_id nodeID = 4; + char_string<32> label = 5; + fabric_idx fabricIndex = 254; + } + + fabric_scoped struct NOCStruct { + fabric_sensitive octet_string noc = 1; + nullable fabric_sensitive octet_string icac = 2; + fabric_idx fabricIndex = 254; + } + + readonly attribute access(read: administer) NOCStruct NOCs[] = 0; + readonly attribute FabricDescriptorStruct fabrics[] = 1; + readonly attribute int8u supportedFabrics = 2; + readonly attribute int8u commissionedFabrics = 3; + readonly attribute octet_string trustedRootCertificates[] = 4; + readonly attribute int8u currentFabricIndex = 5; + 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 AttestationRequestRequest { + octet_string<32> attestationNonce = 0; + } + + response struct AttestationResponse = 1 { + octet_string<900> attestationElements = 0; + octet_string<64> attestationSignature = 1; + } + + request struct CertificateChainRequestRequest { + CertificateChainTypeEnum certificateType = 0; + } + + response struct CertificateChainResponse = 3 { + octet_string<600> certificate = 0; + } + + request struct CSRRequestRequest { + octet_string<32> CSRNonce = 0; + optional boolean isForUpdateNOC = 1; + } + + response struct CSRResponse = 5 { + octet_string NOCSRElements = 0; + octet_string attestationSignature = 1; + } + + request struct AddNOCRequest { + octet_string<400> NOCValue = 0; + optional octet_string<400> ICACValue = 1; + octet_string<16> IPKValue = 2; + int64u caseAdminSubject = 3; + vendor_id adminVendorId = 4; + } + + request struct UpdateNOCRequest { + octet_string NOCValue = 0; + optional octet_string ICACValue = 1; + } + + response struct NOCResponse = 8 { + NodeOperationalCertStatusEnum statusCode = 0; + optional fabric_idx fabricIndex = 1; + optional char_string<128> debugText = 2; + } + + request struct UpdateFabricLabelRequest { + char_string<32> label = 0; + } + + request struct RemoveFabricRequest { + fabric_idx fabricIndex = 0; + } + + request struct AddTrustedRootCertificateRequest { + octet_string rootCACertificate = 0; + } + + /** Sender is requesting attestation information from the receiver. */ + command access(invoke: administer) AttestationRequest(AttestationRequestRequest): AttestationResponse = 0; + /** Sender is requesting a device attestation certificate from the receiver. */ + command access(invoke: administer) CertificateChainRequest(CertificateChainRequestRequest): CertificateChainResponse = 2; + /** Sender is requesting a certificate signing request (CSR) from the receiver. */ + command access(invoke: administer) CSRRequest(CSRRequestRequest): CSRResponse = 4; + /** Sender is requesting to add the new node operational certificates. */ + command access(invoke: administer) AddNOC(AddNOCRequest): NOCResponse = 6; + /** Sender is requesting to update the node operational certificates. */ + fabric command access(invoke: administer) UpdateNOC(UpdateNOCRequest): NOCResponse = 7; + /** This command SHALL be used by an Administrative Node to set the user-visible Label field for a given Fabric, as reflected by entries in the Fabrics attribute. */ + fabric command access(invoke: administer) UpdateFabricLabel(UpdateFabricLabelRequest): NOCResponse = 9; + /** This command is used by Administrative Nodes to remove a given fabric index and delete all associated fabric-scoped data. */ + command access(invoke: administer) RemoveFabric(RemoveFabricRequest): NOCResponse = 10; + /** This command SHALL add a Trusted Root CA Certificate, provided as its CHIP Certificate representation. */ + command access(invoke: administer) AddTrustedRootCertificate(AddTrustedRootCertificateRequest): DefaultSuccess = 11; +} + +/** The Group Key Management Cluster is the mechanism by which group keys are managed. */ +cluster GroupKeyManagement = 63 { + revision 1; // NOTE: Default/not specifically set + + enum GroupKeySecurityPolicyEnum : enum8 { + kTrustFirst = 0; + kCacheAndSync = 1; + } + + bitmap Feature : bitmap32 { + kCacheAndSync = 0x1; + } + + fabric_scoped struct GroupInfoMapStruct { + group_id groupId = 1; + endpoint_no endpoints[] = 2; + optional char_string<16> groupName = 3; + fabric_idx fabricIndex = 254; + } + + fabric_scoped struct GroupKeyMapStruct { + group_id groupId = 1; + int16u groupKeySetID = 2; + fabric_idx fabricIndex = 254; + } + + struct GroupKeySetStruct { + int16u groupKeySetID = 0; + GroupKeySecurityPolicyEnum groupKeySecurityPolicy = 1; + nullable octet_string<16> epochKey0 = 2; + nullable epoch_us epochStartTime0 = 3; + nullable octet_string<16> epochKey1 = 4; + nullable epoch_us epochStartTime1 = 5; + nullable octet_string<16> epochKey2 = 6; + nullable epoch_us epochStartTime2 = 7; + } + + attribute access(write: manage) GroupKeyMapStruct groupKeyMap[] = 0; + readonly attribute GroupInfoMapStruct groupTable[] = 1; + readonly attribute int16u maxGroupsPerFabric = 2; + readonly attribute int16u maxGroupKeysPerFabric = 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 KeySetWriteRequest { + GroupKeySetStruct groupKeySet = 0; + } + + request struct KeySetReadRequest { + int16u groupKeySetID = 0; + } + + response struct KeySetReadResponse = 2 { + GroupKeySetStruct groupKeySet = 0; + } + + request struct KeySetRemoveRequest { + int16u groupKeySetID = 0; + } + + response struct KeySetReadAllIndicesResponse = 5 { + int16u groupKeySetIDs[] = 0; + } + + /** Write a new set of keys for the given key set id. */ + fabric command access(invoke: administer) KeySetWrite(KeySetWriteRequest): DefaultSuccess = 0; + /** Read the keys for a given key set id. */ + fabric command access(invoke: administer) KeySetRead(KeySetReadRequest): KeySetReadResponse = 1; + /** Revoke a Root Key from a Group */ + fabric command access(invoke: administer) KeySetRemove(KeySetRemoveRequest): DefaultSuccess = 3; + /** Return the list of Group Key Sets associated with the accessing fabric */ + fabric command access(invoke: administer) KeySetReadAllIndices(): KeySetReadAllIndicesResponse = 4; +} + +/** This cluster is used to configure a valve. */ +cluster ValveConfigurationAndControl = 129 { + revision 1; + + enum StatusCodeEnum : enum8 { + kFailureDueToFault = 2; + } + + enum ValveStateEnum : enum8 { + kClosed = 0; + kOpen = 1; + kTransitioning = 2; + } + + bitmap Feature : bitmap32 { + kTimeSync = 0x1; + kLevel = 0x2; + } + + bitmap ValveFaultBitmap : bitmap16 { + kGeneralFault = 0x1; + kBlocked = 0x2; + kLeaking = 0x4; + kNotConnected = 0x8; + kShortCircuit = 0x10; + kCurrentExceeded = 0x20; + } + + info event ValveStateChanged = 0 { + ValveStateEnum valveState = 0; + optional percent valveLevel = 1; + } + + info event ValveFault = 1 { + ValveFaultBitmap valveFault = 0; + } + + readonly attribute nullable elapsed_s openDuration = 0; + attribute nullable elapsed_s defaultOpenDuration = 1; + readonly attribute optional nullable epoch_us autoCloseTime = 2; + readonly attribute nullable elapsed_s remainingDuration = 3; + readonly attribute nullable ValveStateEnum currentState = 4; + readonly attribute nullable ValveStateEnum targetState = 5; + readonly attribute optional nullable percent currentLevel = 6; + readonly attribute optional nullable percent targetLevel = 7; + attribute optional percent defaultOpenLevel = 8; + readonly attribute optional ValveFaultBitmap valveFault = 9; + readonly attribute optional int8u levelStep = 10; + 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 OpenRequest { + optional nullable elapsed_s openDuration = 0; + optional percent targetLevel = 1; + } + + /** This command is used to set the valve to its open position. */ + command Open(OpenRequest): DefaultSuccess = 0; + /** This command is used to set the valve to its closed position. */ + command Close(): DefaultSuccess = 1; +} + +endpoint 0 { + device type ma_rootdevice = 22, version 1; + + + server cluster Descriptor { + callback attribute deviceTypeList; + callback attribute serverList; + callback attribute clientList; + callback attribute partsList; + callback attribute featureMap; + callback attribute clusterRevision; + } + + server cluster AccessControl { + emits event AccessControlEntryChanged; + emits event AccessControlExtensionChanged; + callback attribute acl; + callback attribute extension; + callback attribute subjectsPerAccessControlEntry; + callback attribute targetsPerAccessControlEntry; + callback attribute accessControlEntriesPerFabric; + callback attribute attributeList; + ram attribute featureMap default = 0; + callback attribute clusterRevision; + } + + server cluster BasicInformation { + emits event StartUp; + emits event ShutDown; + emits event Leave; + callback attribute dataModelRevision; + callback attribute vendorName; + callback attribute vendorID; + callback attribute productName; + callback attribute productID; + persist attribute nodeLabel; + callback attribute location; + callback attribute hardwareVersion; + callback attribute hardwareVersionString; + callback attribute softwareVersion; + callback attribute softwareVersionString; + callback attribute manufacturingDate; + callback attribute partNumber; + callback attribute productURL; + callback attribute productLabel; + callback attribute serialNumber; + persist attribute localConfigDisabled default = 0; + callback attribute uniqueID; + callback attribute capabilityMinima; + callback attribute specificationVersion; + callback attribute maxPathsPerInvoke; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 3; + } + + server cluster GeneralCommissioning { + ram attribute breadcrumb default = 0x0000000000000000; + callback attribute basicCommissioningInfo; + callback attribute regulatoryConfig; + callback attribute locationCapability; + callback attribute supportsConcurrentConnection; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 0x0001; + + handle command ArmFailSafe; + handle command ArmFailSafeResponse; + handle command SetRegulatoryConfig; + handle command SetRegulatoryConfigResponse; + handle command CommissioningComplete; + handle command CommissioningCompleteResponse; + } + + server cluster NetworkCommissioning { + ram attribute maxNetworks; + callback attribute networks; + ram attribute scanMaxTimeSeconds; + ram attribute connectMaxTimeSeconds; + ram attribute interfaceEnabled; + ram attribute lastNetworkingStatus; + ram attribute lastNetworkID; + ram attribute lastConnectErrorValue; + ram attribute featureMap default = 1; + ram attribute clusterRevision default = 0x0001; + + handle command ScanNetworks; + handle command ScanNetworksResponse; + handle command AddOrUpdateWiFiNetwork; + handle command AddOrUpdateThreadNetwork; + handle command RemoveNetwork; + handle command NetworkConfigResponse; + handle command ConnectNetwork; + handle command ConnectNetworkResponse; + handle command ReorderNetwork; + } + + server cluster GeneralDiagnostics { + emits event BootReason; + callback attribute networkInterfaces; + callback attribute rebootCount; + callback attribute upTime; + callback attribute totalOperationalHours; + callback attribute bootReason; + callback attribute activeHardwareFaults; + callback attribute activeRadioFaults; + callback attribute activeNetworkFaults; + callback attribute testEventTriggersEnabled default = false; + callback attribute featureMap; + callback attribute clusterRevision; + + handle command TestEventTrigger; + handle command TimeSnapshot; + handle command TimeSnapshotResponse; + } + + server cluster AdministratorCommissioning { + callback attribute windowStatus; + callback attribute adminFabricIndex; + callback attribute adminVendorId; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 0x0001; + + handle command OpenCommissioningWindow; + handle command RevokeCommissioning; + } + + server cluster OperationalCredentials { + callback attribute NOCs; + callback attribute fabrics; + callback attribute supportedFabrics; + callback attribute commissionedFabrics; + callback attribute trustedRootCertificates; + callback attribute currentFabricIndex; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 0x0001; + + handle command AttestationRequest; + handle command AttestationResponse; + handle command CertificateChainRequest; + handle command CertificateChainResponse; + handle command CSRRequest; + handle command CSRResponse; + handle command AddNOC; + handle command UpdateNOC; + handle command NOCResponse; + handle command UpdateFabricLabel; + handle command RemoveFabric; + handle command AddTrustedRootCertificate; + } + + server cluster GroupKeyManagement { + callback attribute groupKeyMap; + callback attribute groupTable; + callback attribute maxGroupsPerFabric; + callback attribute maxGroupKeysPerFabric; + callback attribute featureMap; + callback attribute clusterRevision; + + handle command KeySetWrite; + handle command KeySetRead; + handle command KeySetReadResponse; + handle command KeySetRemove; + handle command KeySetReadAllIndices; + handle command KeySetReadAllIndicesResponse; + } +} +endpoint 1 { + device type ma_water_valve = 66, version 1; + + + server cluster Identify { + ram attribute identifyTime default = 0x0; + ram attribute identifyType default = 0x0; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 4; + + handle command Identify; + } + + server cluster Descriptor { + callback attribute deviceTypeList; + callback attribute serverList; + callback attribute clientList; + callback attribute partsList; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + callback attribute featureMap; + callback attribute clusterRevision; + } + + server cluster ValveConfigurationAndControl { + callback attribute openDuration; + callback attribute defaultOpenDuration; + callback attribute remainingDuration; + callback attribute currentState; + callback attribute targetState; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 1; + + handle command Open; + handle command Close; + } +} +endpoint 2 { + device type ma_water_valve = 66, version 1; + + + server cluster Identify { + ram attribute identifyTime default = 0x0; + ram attribute identifyType default = 0x0; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 4; + + handle command Identify; + } + + server cluster Descriptor { + callback attribute deviceTypeList; + callback attribute serverList; + callback attribute clientList; + callback attribute partsList; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + callback attribute featureMap; + callback attribute clusterRevision; + } + + server cluster ValveConfigurationAndControl { + callback attribute openDuration; + callback attribute defaultOpenDuration; + callback attribute remainingDuration; + callback attribute currentState; + callback attribute targetState; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 1; + + handle command Open; + handle command Close; + } +} +endpoint 3 { + device type ma_water_valve = 66, version 1; + + + server cluster Identify { + ram attribute identifyTime default = 0x0; + ram attribute identifyType default = 0x0; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 4; + + handle command Identify; + } + + server cluster Descriptor { + callback attribute deviceTypeList; + callback attribute serverList; + callback attribute clientList; + callback attribute partsList; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + callback attribute featureMap; + callback attribute clusterRevision; + } + + server cluster ValveConfigurationAndControl { + callback attribute openDuration; + callback attribute defaultOpenDuration; + callback attribute remainingDuration; + callback attribute currentState; + callback attribute targetState; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 1; + + handle command Open; + handle command Close; + } +} +endpoint 4 { + device type ma_water_valve = 66, version 1; + + + server cluster Identify { + ram attribute identifyTime default = 0x0; + ram attribute identifyType default = 0x0; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 4; + + handle command Identify; + } + + server cluster Descriptor { + callback attribute deviceTypeList; + callback attribute serverList; + callback attribute clientList; + callback attribute partsList; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + callback attribute featureMap; + callback attribute clusterRevision; + } + + server cluster ValveConfigurationAndControl { + callback attribute openDuration; + callback attribute defaultOpenDuration; + callback attribute remainingDuration; + callback attribute currentState; + callback attribute targetState; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 1; + + handle command Open; + handle command Close; + } +} +endpoint 5 { + device type ma_water_valve = 66, version 1; + + + server cluster Identify { + ram attribute identifyTime default = 0x0; + ram attribute identifyType default = 0x0; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 4; + + handle command Identify; + } + + server cluster Descriptor { + callback attribute deviceTypeList; + callback attribute serverList; + callback attribute clientList; + callback attribute partsList; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + callback attribute featureMap; + callback attribute clusterRevision; + } + + server cluster ValveConfigurationAndControl { + callback attribute openDuration; + callback attribute defaultOpenDuration; + callback attribute remainingDuration; + callback attribute currentState; + callback attribute targetState; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 1; + + handle command Open; + handle command Close; + } +} +endpoint 6 { + device type ma_water_valve = 66, version 1; + + + server cluster Identify { + ram attribute identifyTime default = 0x0; + ram attribute identifyType default = 0x0; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 4; + + handle command Identify; + } + + server cluster Descriptor { + callback attribute deviceTypeList; + callback attribute serverList; + callback attribute clientList; + callback attribute partsList; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + callback attribute featureMap; + callback attribute clusterRevision; + } + + server cluster ValveConfigurationAndControl { + callback attribute openDuration; + callback attribute defaultOpenDuration; + callback attribute remainingDuration; + callback attribute currentState; + callback attribute targetState; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 1; + + handle command Open; + handle command Close; + } +} + + diff --git a/examples/valve-app/valve-common/valve-app.zap b/examples/valve-app/valve-common/valve-app.zap new file mode 100644 index 00000000000000..868a7e82bc1b76 --- /dev/null +++ b/examples/valve-app/valve-common/valve-app.zap @@ -0,0 +1,5229 @@ +{ + "fileFormat": 2, + "featureLevel": 103, + "creator": "zap", + "keyValuePairs": [ + { + "key": "commandDiscovery", + "value": "1" + }, + { + "key": "defaultResponsePolicy", + "value": "always" + }, + { + "key": "manufacturerCodes", + "value": "0x1002" + } + ], + "package": [ + { + "pathRelativity": "relativeToZap", + "path": "../../../src/app/zap-templates/zcl/zcl.json", + "type": "zcl-properties", + "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": [ + { + "id": 1, + "name": "MA-rootdevice", + "deviceTypeRef": { + "code": 22, + "profileId": 259, + "label": "MA-rootdevice", + "name": "MA-rootdevice" + }, + "deviceTypes": [ + { + "code": 22, + "profileId": 259, + "label": "MA-rootdevice", + "name": "MA-rootdevice" + } + ], + "deviceVersions": [ + 1 + ], + "deviceIdentifiers": [ + 22 + ], + "deviceTypeName": "MA-rootdevice", + "deviceTypeCode": 22, + "deviceTypeProfileId": 259, + "clusters": [ + { + "name": "Descriptor", + "code": 29, + "mfgCode": null, + "define": "DESCRIPTOR_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "DeviceTypeList", + "code": 0, + "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": "ServerList", + "code": 1, + "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": "ClientList", + "code": 2, + "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": "PartsList", + "code": 3, + "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": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Access Control", + "code": 31, + "mfgCode": null, + "define": "ACCESS_CONTROL_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "ACL", + "code": 0, + "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": "Extension", + "code": 1, + "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": "SubjectsPerAccessControlEntry", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TargetsPerAccessControlEntry", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AccessControlEntriesPerFabric", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "int16u", + "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": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ], + "events": [ + { + "name": "AccessControlEntryChanged", + "code": 0, + "mfgCode": null, + "side": "server", + "included": 1 + }, + { + "name": "AccessControlExtensionChanged", + "code": 1, + "mfgCode": null, + "side": "server", + "included": 1 + } + ] + }, + { + "name": "Basic Information", + "code": 40, + "mfgCode": null, + "define": "BASIC_INFORMATION_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "DataModelRevision", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "VendorName", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "VendorID", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "vendor_id", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "ProductName", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "ProductID", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "NodeLabel", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "NVM", + "singleton": 1, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "Location", + "code": 6, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "HardwareVersion", + "code": 7, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "HardwareVersionString", + "code": 8, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "SoftwareVersion", + "code": 9, + "mfgCode": null, + "side": "server", + "type": "int32u", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "SoftwareVersionString", + "code": 10, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "ManufacturingDate", + "code": 11, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "PartNumber", + "code": 12, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "ProductURL", + "code": 13, + "mfgCode": null, + "side": "server", + "type": "long_char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "ProductLabel", + "code": 14, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "SerialNumber", + "code": 15, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "LocalConfigDisabled", + "code": 16, + "mfgCode": null, + "side": "server", + "type": "boolean", + "included": 1, + "storageOption": "NVM", + "singleton": 1, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "UniqueID", + "code": 18, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "CapabilityMinima", + "code": 19, + "mfgCode": null, + "side": "server", + "type": "CapabilityMinimaStruct", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "SpecificationVersion", + "code": 21, + "mfgCode": null, + "side": "server", + "type": "int32u", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "MaxPathsPerInvoke", + "code": 22, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 1, + "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": 1, + "bounded": 0, + "defaultValue": "3", + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + } + ], + "events": [ + { + "name": "StartUp", + "code": 0, + "mfgCode": null, + "side": "server", + "included": 1 + }, + { + "name": "ShutDown", + "code": 1, + "mfgCode": null, + "side": "server", + "included": 1 + }, + { + "name": "Leave", + "code": 2, + "mfgCode": null, + "side": "server", + "included": 1 + } + ] + }, + { + "name": "General Commissioning", + "code": 48, + "mfgCode": null, + "define": "GENERAL_COMMISSIONING_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "ArmFailSafe", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "ArmFailSafeResponse", + "code": 1, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "SetRegulatoryConfig", + "code": 2, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "SetRegulatoryConfigResponse", + "code": 3, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "CommissioningComplete", + "code": 4, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "CommissioningCompleteResponse", + "code": 5, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "Breadcrumb", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "int64u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0000000000000000", + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "BasicCommissioningInfo", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "BasicCommissioningInfo", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "RegulatoryConfig", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "RegulatoryLocationTypeEnum", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "LocationCapability", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "RegulatoryLocationTypeEnum", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "SupportsConcurrentConnection", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "boolean", + "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": "0x0001", + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + } + ] + }, + { + "name": "Network Commissioning", + "code": 49, + "mfgCode": null, + "define": "NETWORK_COMMISSIONING_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "ScanNetworks", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "ScanNetworksResponse", + "code": 1, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "AddOrUpdateWiFiNetwork", + "code": 2, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "AddOrUpdateThreadNetwork", + "code": 3, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "RemoveNetwork", + "code": 4, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "NetworkConfigResponse", + "code": 5, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "ConnectNetwork", + "code": 6, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "ConnectNetworkResponse", + "code": 7, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "ReorderNetwork", + "code": 8, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "MaxNetworks", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "Networks", + "code": 1, + "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": "ScanMaxTimeSeconds", + "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": "ConnectMaxTimeSeconds", + "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": "InterfaceEnabled", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "boolean", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "LastNetworkingStatus", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "NetworkCommissioningStatusEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "LastNetworkID", + "code": 6, + "mfgCode": null, + "side": "server", + "type": "octet_string", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "LastConnectErrorValue", + "code": 7, + "mfgCode": null, + "side": "server", + "type": "int32s", + "included": 1, + "storageOption": "RAM", + "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": "1", + "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": "0x0001", + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + } + ] + }, + { + "name": "General Diagnostics", + "code": 51, + "mfgCode": null, + "define": "GENERAL_DIAGNOSTICS_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "TestEventTrigger", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "TimeSnapshot", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "TimeSnapshotResponse", + "code": 2, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "NetworkInterfaces", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "RebootCount", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "UpTime", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "int64u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TotalOperationalHours", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "int32u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BootReason", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "BootReasonEnum", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ActiveHardwareFaults", + "code": 5, + "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": "ActiveRadioFaults", + "code": 6, + "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": "ActiveNetworkFaults", + "code": 7, + "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": "TestEventTriggersEnabled", + "code": 8, + "mfgCode": null, + "side": "server", + "type": "boolean", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "false", + "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": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + } + ], + "events": [ + { + "name": "BootReason", + "code": 3, + "mfgCode": null, + "side": "server", + "included": 1 + } + ] + }, + { + "name": "Administrator Commissioning", + "code": 60, + "mfgCode": null, + "define": "ADMINISTRATOR_COMMISSIONING_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "OpenCommissioningWindow", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "RevokeCommissioning", + "code": 2, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "WindowStatus", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "CommissioningWindowStatusEnum", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AdminFabricIndex", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "fabric_idx", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AdminVendorId", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "vendor_id", + "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": "0x0001", + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + } + ] + }, + { + "name": "Operational Credentials", + "code": 62, + "mfgCode": null, + "define": "OPERATIONAL_CREDENTIALS_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "AttestationRequest", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "AttestationResponse", + "code": 1, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "CertificateChainRequest", + "code": 2, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "CertificateChainResponse", + "code": 3, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "CSRRequest", + "code": 4, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "CSRResponse", + "code": 5, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "AddNOC", + "code": 6, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "UpdateNOC", + "code": 7, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "NOCResponse", + "code": 8, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "UpdateFabricLabel", + "code": 9, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "RemoveFabric", + "code": 10, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "AddTrustedRootCertificate", + "code": 11, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "NOCs", + "code": 0, + "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": "Fabrics", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "SupportedFabrics", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "CommissionedFabrics", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "TrustedRootCertificates", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "CurrentFabricIndex", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "int8u", + "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": "0x0001", + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + } + ] + }, + { + "name": "Group Key Management", + "code": 63, + "mfgCode": null, + "define": "GROUP_KEY_MANAGEMENT_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "KeySetWrite", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "KeySetRead", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "KeySetReadResponse", + "code": 2, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "KeySetRemove", + "code": 3, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "KeySetReadAllIndices", + "code": 4, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "KeySetReadAllIndicesResponse", + "code": 5, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "GroupKeyMap", + "code": 0, + "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": "GroupTable", + "code": 1, + "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": "MaxGroupsPerFabric", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "MaxGroupKeysPerFabric", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "int16u", + "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": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + } + ] + }, + { + "id": 2, + "name": "Anonymous Endpoint Type", + "deviceTypeRef": { + "code": 66, + "profileId": 259, + "label": "MA-water-valve", + "name": "MA-water-valve" + }, + "deviceTypes": [ + { + "code": 66, + "profileId": 259, + "label": "MA-water-valve", + "name": "MA-water-valve" + } + ], + "deviceVersions": [ + 1 + ], + "deviceIdentifiers": [ + 66 + ], + "deviceTypeName": "MA-water-valve", + "deviceTypeCode": 66, + "deviceTypeProfileId": 259, + "clusters": [ + { + "name": "Identify", + "code": 3, + "mfgCode": null, + "define": "IDENTIFY_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "Identify", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "IdentifyTime", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "IdentifyType", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "IdentifyTypeEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0", + "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": "4", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Descriptor", + "code": 29, + "mfgCode": null, + "define": "DESCRIPTOR_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "DeviceTypeList", + "code": 0, + "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": "ServerList", + "code": 1, + "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": "ClientList", + "code": 2, + "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": "PartsList", + "code": 3, + "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": "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": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Valve Configuration and Control", + "code": 129, + "mfgCode": null, + "define": "VALVE_CONFIGURATION_AND_CONTROL_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "Open", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "Close", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "OpenDuration", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "DefaultOpenDuration", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "RemainingDuration", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "CurrentState", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "ValveStateEnum", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TargetState", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "ValveStateEnum", + "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": 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 + } + ] + } + ] + }, + { + "id": 3, + "name": "Anonymous Endpoint Type", + "deviceTypeRef": { + "code": 66, + "profileId": 259, + "label": "MA-water-valve", + "name": "MA-water-valve" + }, + "deviceTypes": [ + { + "code": 66, + "profileId": 259, + "label": "MA-water-valve", + "name": "MA-water-valve" + } + ], + "deviceVersions": [ + 1 + ], + "deviceIdentifiers": [ + 66 + ], + "deviceTypeName": "MA-water-valve", + "deviceTypeCode": 66, + "deviceTypeProfileId": 259, + "clusters": [ + { + "name": "Identify", + "code": 3, + "mfgCode": null, + "define": "IDENTIFY_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "Identify", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "IdentifyTime", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "IdentifyType", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "IdentifyTypeEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0", + "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": "4", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Descriptor", + "code": 29, + "mfgCode": null, + "define": "DESCRIPTOR_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "DeviceTypeList", + "code": 0, + "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": "ServerList", + "code": 1, + "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": "ClientList", + "code": 2, + "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": "PartsList", + "code": 3, + "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": "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": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Valve Configuration and Control", + "code": 129, + "mfgCode": null, + "define": "VALVE_CONFIGURATION_AND_CONTROL_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "Open", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "Close", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "OpenDuration", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "DefaultOpenDuration", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "RemainingDuration", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "CurrentState", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "ValveStateEnum", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TargetState", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "ValveStateEnum", + "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": 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 + } + ] + } + ] + }, + { + "id": 4, + "name": "Anonymous Endpoint Type", + "deviceTypeRef": { + "code": 66, + "profileId": 259, + "label": "MA-water-valve", + "name": "MA-water-valve" + }, + "deviceTypes": [ + { + "code": 66, + "profileId": 259, + "label": "MA-water-valve", + "name": "MA-water-valve" + } + ], + "deviceVersions": [ + 1 + ], + "deviceIdentifiers": [ + 66 + ], + "deviceTypeName": "MA-water-valve", + "deviceTypeCode": 66, + "deviceTypeProfileId": 259, + "clusters": [ + { + "name": "Identify", + "code": 3, + "mfgCode": null, + "define": "IDENTIFY_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "Identify", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "IdentifyTime", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "IdentifyType", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "IdentifyTypeEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0", + "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": "4", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Descriptor", + "code": 29, + "mfgCode": null, + "define": "DESCRIPTOR_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "DeviceTypeList", + "code": 0, + "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": "ServerList", + "code": 1, + "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": "ClientList", + "code": 2, + "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": "PartsList", + "code": 3, + "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": "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": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Valve Configuration and Control", + "code": 129, + "mfgCode": null, + "define": "VALVE_CONFIGURATION_AND_CONTROL_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "Open", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "Close", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "OpenDuration", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "DefaultOpenDuration", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "RemainingDuration", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "CurrentState", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "ValveStateEnum", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TargetState", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "ValveStateEnum", + "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": 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 + } + ] + } + ] + }, + { + "id": 5, + "name": "Anonymous Endpoint Type", + "deviceTypeRef": { + "code": 66, + "profileId": 259, + "label": "MA-water-valve", + "name": "MA-water-valve" + }, + "deviceTypes": [ + { + "code": 66, + "profileId": 259, + "label": "MA-water-valve", + "name": "MA-water-valve" + } + ], + "deviceVersions": [ + 1 + ], + "deviceIdentifiers": [ + 66 + ], + "deviceTypeName": "MA-water-valve", + "deviceTypeCode": 66, + "deviceTypeProfileId": 259, + "clusters": [ + { + "name": "Identify", + "code": 3, + "mfgCode": null, + "define": "IDENTIFY_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "Identify", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "IdentifyTime", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "IdentifyType", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "IdentifyTypeEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0", + "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": "4", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Descriptor", + "code": 29, + "mfgCode": null, + "define": "DESCRIPTOR_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "DeviceTypeList", + "code": 0, + "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": "ServerList", + "code": 1, + "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": "ClientList", + "code": 2, + "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": "PartsList", + "code": 3, + "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": "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": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Valve Configuration and Control", + "code": 129, + "mfgCode": null, + "define": "VALVE_CONFIGURATION_AND_CONTROL_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "Open", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "Close", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "OpenDuration", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "DefaultOpenDuration", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "RemainingDuration", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "CurrentState", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "ValveStateEnum", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TargetState", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "ValveStateEnum", + "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": 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 + } + ] + } + ] + }, + { + "id": 6, + "name": "Anonymous Endpoint Type", + "deviceTypeRef": { + "code": 66, + "profileId": 259, + "label": "MA-water-valve", + "name": "MA-water-valve" + }, + "deviceTypes": [ + { + "code": 66, + "profileId": 259, + "label": "MA-water-valve", + "name": "MA-water-valve" + } + ], + "deviceVersions": [ + 1 + ], + "deviceIdentifiers": [ + 66 + ], + "deviceTypeName": "MA-water-valve", + "deviceTypeCode": 66, + "deviceTypeProfileId": 259, + "clusters": [ + { + "name": "Identify", + "code": 3, + "mfgCode": null, + "define": "IDENTIFY_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "Identify", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "IdentifyTime", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "IdentifyType", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "IdentifyTypeEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0", + "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": "4", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Descriptor", + "code": 29, + "mfgCode": null, + "define": "DESCRIPTOR_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "DeviceTypeList", + "code": 0, + "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": "ServerList", + "code": 1, + "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": "ClientList", + "code": 2, + "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": "PartsList", + "code": 3, + "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": "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": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Valve Configuration and Control", + "code": 129, + "mfgCode": null, + "define": "VALVE_CONFIGURATION_AND_CONTROL_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "Open", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "Close", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "OpenDuration", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "DefaultOpenDuration", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "RemainingDuration", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "CurrentState", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "ValveStateEnum", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TargetState", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "ValveStateEnum", + "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": 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 + } + ] + } + ] + }, + { + "id": 7, + "name": "Anonymous Endpoint Type", + "deviceTypeRef": { + "code": 66, + "profileId": 259, + "label": "MA-water-valve", + "name": "MA-water-valve" + }, + "deviceTypes": [ + { + "code": 66, + "profileId": 259, + "label": "MA-water-valve", + "name": "MA-water-valve" + } + ], + "deviceVersions": [ + 1 + ], + "deviceIdentifiers": [ + 66 + ], + "deviceTypeName": "MA-water-valve", + "deviceTypeCode": 66, + "deviceTypeProfileId": 259, + "clusters": [ + { + "name": "Identify", + "code": 3, + "mfgCode": null, + "define": "IDENTIFY_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "Identify", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "IdentifyTime", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "IdentifyType", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "IdentifyTypeEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0", + "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": "4", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Descriptor", + "code": 29, + "mfgCode": null, + "define": "DESCRIPTOR_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "DeviceTypeList", + "code": 0, + "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": "ServerList", + "code": 1, + "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": "ClientList", + "code": 2, + "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": "PartsList", + "code": 3, + "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": "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": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Valve Configuration and Control", + "code": 129, + "mfgCode": null, + "define": "VALVE_CONFIGURATION_AND_CONTROL_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "Open", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "Close", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "OpenDuration", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "DefaultOpenDuration", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "RemainingDuration", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "CurrentState", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "ValveStateEnum", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TargetState", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "ValveStateEnum", + "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": 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 + } + ] + } + ] + } + ], + "endpoints": [ + { + "endpointTypeName": "MA-rootdevice", + "endpointTypeIndex": 0, + "profileId": 259, + "endpointId": 0, + "networkId": 0, + "parentEndpointIdentifier": null + }, + { + "endpointTypeName": "Anonymous Endpoint Type", + "endpointTypeIndex": 1, + "profileId": 259, + "endpointId": 1, + "networkId": 0, + "parentEndpointIdentifier": null + }, + { + "endpointTypeName": "Anonymous Endpoint Type", + "endpointTypeIndex": 2, + "profileId": 259, + "endpointId": 2, + "networkId": 0, + "parentEndpointIdentifier": null + }, + { + "endpointTypeName": "Anonymous Endpoint Type", + "endpointTypeIndex": 3, + "profileId": 259, + "endpointId": 3, + "networkId": 0, + "parentEndpointIdentifier": null + }, + { + "endpointTypeName": "Anonymous Endpoint Type", + "endpointTypeIndex": 4, + "profileId": 259, + "endpointId": 4, + "networkId": 0, + "parentEndpointIdentifier": null + }, + { + "endpointTypeName": "Anonymous Endpoint Type", + "endpointTypeIndex": 5, + "profileId": 259, + "endpointId": 5, + "networkId": 0, + "parentEndpointIdentifier": null + }, + { + "endpointTypeName": "Anonymous Endpoint Type", + "endpointTypeIndex": 6, + "profileId": 259, + "endpointId": 6, + "networkId": 0, + "parentEndpointIdentifier": null + } + ] +} \ No newline at end of file diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.cpp index f6fa4e8fee6332..ef5baea2de6ec7 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.cpp +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.cpp @@ -514,5 +514,5 @@ bool emberAfValveConfigurationAndControlClusterCloseCallback( void MatterValveConfigurationAndControlPluginServerInitCallback() { - AttributeAccessInterfaceRegistry::Instance().Register(&gAttrAccess); + // AttributeAccessInterfaceRegistry::Instance().Register(&gAttrAccess); } From 100a9676cb15d2ef89e26c4d441ad171a14d59c4 Mon Sep 17 00:00:00 2001 From: cecille Date: Thu, 19 Sep 2024 09:18:29 -0400 Subject: [PATCH 09/31] Add endpoint marker to print --- examples/valve-app/linux/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/valve-app/linux/main.cpp b/examples/valve-app/linux/main.cpp index b8f0000b7e5838..159ca16c7dc9ac 100644 --- a/examples/valve-app/linux/main.cpp +++ b/examples/valve-app/linux/main.cpp @@ -34,7 +34,7 @@ class PrintOnlyDelegate : public NonLevelControlDelegate PrintOnlyDelegate(EndpointId endpoint) : mEndpoint(endpoint) {} CHIP_ERROR HandleOpenValve(ValveStateEnum & currentState, BitMask & valveFault) override { - ChipLogProgress(NotSpecified, "VALVE IS OPENING!!!!!"); + ChipLogProgress(NotSpecified, "VALVE IS OPENING on endpoint %u!!!!!", mEndpoint); state = ValveStateEnum::kOpen; currentState = state; return CHIP_NO_ERROR; @@ -42,7 +42,7 @@ class PrintOnlyDelegate : public NonLevelControlDelegate ValveStateEnum GetCurrentValveState() override { return state; } CHIP_ERROR HandleCloseValve(ValveStateEnum & currentState, BitMask & valveFault) override { - ChipLogProgress(NotSpecified, "VALVE IS CLOSING!!!!!"); + ChipLogProgress(NotSpecified, "VALVE IS CLOSING on endpoint %u!!!!!", mEndpoint); state = ValveStateEnum::kClosed; currentState = state; return CHIP_NO_ERROR; From 52449a3f818202c52844b79dc08132dbc59f647e Mon Sep 17 00:00:00 2001 From: Cecille Freeman Date: Wed, 25 Sep 2024 22:12:21 -0700 Subject: [PATCH 10/31] add feature map and cluster revision --- ...onfiguration-and-control-cluster-logic.cpp | 12 ++++++++ ...-configuration-and-control-cluster-logic.h | 9 +++++- ...configuration-and-control-server-disco.cpp | 8 +++++ .../TestValveConfigurationAndControl.cpp | 30 +++++++++++++++++++ 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp index 19db47e8162b69..94111d83d7cd18 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp @@ -276,6 +276,18 @@ CHIP_ERROR ClusterLogic::GetLevelStep(uint8_t & levelStep) levelStep = mState.GetState().levelStep; return CHIP_NO_ERROR; } +CHIP_ERROR ClusterLogic::GetFeatureMap(Attributes::FeatureMap::TypeInfo::Type & featureMap) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); + featureMap = mConformance.featureMap; + return CHIP_NO_ERROR; +} +CHIP_ERROR ClusterLogic::GetClusterRevision(Attributes::ClusterRevision::TypeInfo::Type & clusterRevision) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); + clusterRevision = kClusterRevision; + return CHIP_NO_ERROR; +} CHIP_ERROR ClusterLogic::SetDefaultOpenDuration(const DataModel::Nullable & defaultOpenDuration) { diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h index 2704af52732cab..dd1aacd1d7d38b 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h @@ -22,6 +22,7 @@ #include "valve-configuration-and-control-delegate.h" #include "valve-configuration-and-control-matter-context.h" +#include #include #include #include @@ -84,7 +85,7 @@ struct ClusterState class ClusterStateAttributes { public: - explicit ClusterStateAttributes(MatterContext & matterContext) : mMatterContext(matterContext){}; + explicit ClusterStateAttributes(MatterContext & matterContext) : mMatterContext(matterContext) {}; void Init(ClusterInitParameters initialState); const ClusterState & GetState() { return mState; } @@ -143,6 +144,8 @@ class ClusterLogic CHIP_ERROR GetDefaultOpenLevel(Percent & defaultOpenLevel); CHIP_ERROR GetValveFault(BitMask & valveFault); CHIP_ERROR GetLevelStep(uint8_t & levelStep); + CHIP_ERROR GetFeatureMap(Attributes::FeatureMap::TypeInfo::Type & featureMap); + CHIP_ERROR GetClusterRevision(Attributes::ClusterRevision::TypeInfo::Type & clusterRevision); // All Set functions // Return CHIP_ERROR_INCORRECT_STATE if the class has not been initialized. @@ -181,6 +184,10 @@ class ClusterLogic CHIP_ERROR HandleCloseCommand(); private: + // This cluster implements version 1 of the valve cluster. Do not change this revision without updating + // the cluster to implement the newest features. + // TODO: consider implementing the server such that multiple revisions can be supported + static constexpr Attributes::ClusterRevision::TypeInfo::Type kClusterRevision = 1u; // Determines if the level value is allowed per the level step. bool ValueCompliesWithLevelStep(const uint8_t value); // Returns the target level to send to the delegate based on the targetLevel command field, the device conformance and the diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server-disco.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server-disco.cpp index 6a7eec566633b2..7aebf0db153fe6 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server-disco.cpp +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server-disco.cpp @@ -115,6 +115,14 @@ CHIP_ERROR Interface::Read(const ConcreteReadAttributePath & aPath, AttributeVal typedef LevelStep::TypeInfo::Type T; return EncodeRead(aEncoder, [&logic = mClusterLogic](T & ret) -> CHIP_ERROR { return logic.GetLevelStep(ret); }); } + case FeatureMap::Id: { + typedef FeatureMap::TypeInfo::Type T; + return EncodeRead(aEncoder, [&logic = mClusterLogic](T & ret) -> CHIP_ERROR { return logic.GetFeatureMap(ret); }); + } + case ClusterRevision::Id: { + typedef ClusterRevision::TypeInfo::Type T; + return EncodeRead(aEncoder, [&logic = mClusterLogic](T & ret) -> CHIP_ERROR { return logic.GetClusterRevision(ret); }); + } default: return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); } diff --git a/src/app/tests/TestValveConfigurationAndControl.cpp b/src/app/tests/TestValveConfigurationAndControl.cpp index 5d138ba82133f4..8108403918298d 100644 --- a/src/app/tests/TestValveConfigurationAndControl.cpp +++ b/src/app/tests/TestValveConfigurationAndControl.cpp @@ -105,6 +105,8 @@ namespace { // These are globals because SetUpTestSuite is static and I'm not shaving that yak today. System::TimerAndMockClock gSystemLayerAndClock = System::TimerAndMockClock(); System::Clock::ClockBase * gSavedClock = nullptr; + +constexpr uint16_t kExpectedClusterRevision = 1u; } // namespace class TestValveConfigurationAndControlClusterLogic : public ::testing::Test @@ -406,6 +408,8 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesAllFeature Percent valPercent; uint8_t val8; BitMask valBitmap; + Attributes::FeatureMap::TypeInfo::Type featureMap; + Attributes::ClusterRevision::TypeInfo::Type clusterRevision; EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); @@ -439,6 +443,12 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesAllFeature EXPECT_EQ(logic.GetLevelStep(val8), CHIP_NO_ERROR); EXPECT_EQ(val8, 1); + + EXPECT_EQ(logic.GetFeatureMap(featureMap), CHIP_NO_ERROR); + EXPECT_EQ(featureMap, conformance.featureMap); + + EXPECT_EQ(logic.GetClusterRevision(clusterRevision), CHIP_NO_ERROR); + EXPECT_EQ(clusterRevision, kExpectedClusterRevision); } // This test ensures that attributes that are not supported by the conformance properly return errors @@ -463,6 +473,8 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesNoFeatures Percent valPercent; uint8_t val8; BitMask valBitmap; + Attributes::FeatureMap::TypeInfo::Type featureMap; + Attributes::ClusterRevision::TypeInfo::Type clusterRevision; EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); @@ -490,6 +502,12 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesNoFeatures EXPECT_EQ(logic.GetValveFault(valBitmap), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); EXPECT_EQ(logic.GetLevelStep(val8), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + + EXPECT_EQ(logic.GetFeatureMap(featureMap), CHIP_NO_ERROR); + EXPECT_EQ(featureMap, conformance.featureMap); + + EXPECT_EQ(logic.GetClusterRevision(clusterRevision), CHIP_NO_ERROR); + EXPECT_EQ(clusterRevision, kExpectedClusterRevision); } // This test ensures that all attribute getters return the given starting state values before changes. @@ -526,6 +544,8 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesStartingSt Percent valPercent; uint8_t val8; BitMask valBitmap; + Attributes::FeatureMap::TypeInfo::Type featureMap; + Attributes::ClusterRevision::TypeInfo::Type clusterRevision; EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR); EXPECT_EQ(valElapsedSNullable, state.openDuration); @@ -559,6 +579,12 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesStartingSt EXPECT_EQ(logic.GetLevelStep(val8), CHIP_NO_ERROR); EXPECT_EQ(val8, state.levelStep); + + EXPECT_EQ(logic.GetFeatureMap(featureMap), CHIP_NO_ERROR); + EXPECT_EQ(featureMap, conformance.featureMap); + + EXPECT_EQ(logic.GetClusterRevision(clusterRevision), CHIP_NO_ERROR); + EXPECT_EQ(clusterRevision, kExpectedClusterRevision); } // This test ensures that all attribute getter functions properly error on an uninitialized cluster. @@ -576,6 +602,8 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesUninitiali Percent valPercent; uint8_t val8; BitMask valBitmap; + Attributes::FeatureMap::TypeInfo::Type featureMap; + Attributes::ClusterRevision::TypeInfo::Type clusterRevision; EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_ERROR_INCORRECT_STATE); EXPECT_EQ(logic.GetDefaultOpenDuration(valElapsedSNullable), CHIP_ERROR_INCORRECT_STATE); @@ -588,6 +616,8 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestGetAttributesUninitiali EXPECT_EQ(logic.GetDefaultOpenLevel(valPercent), CHIP_ERROR_INCORRECT_STATE); EXPECT_EQ(logic.GetValveFault(valBitmap), CHIP_ERROR_INCORRECT_STATE); EXPECT_EQ(logic.GetLevelStep(val8), CHIP_ERROR_INCORRECT_STATE); + EXPECT_EQ(logic.GetFeatureMap(featureMap), CHIP_ERROR_INCORRECT_STATE); + EXPECT_EQ(logic.GetClusterRevision(clusterRevision), CHIP_ERROR_INCORRECT_STATE); } //========================================================================================= From 58cfb19d1a3020df425df8c888c6eb29d50e2b40 Mon Sep 17 00:00:00 2001 From: Cecille Freeman Date: Fri, 27 Sep 2024 08:09:27 -0700 Subject: [PATCH 11/31] Fix conformance --- .../valve-app/valve-common/valve-app.matter | 19 +++--- examples/valve-app/valve-common/valve-app.zap | 64 +++---------------- 2 files changed, 16 insertions(+), 67 deletions(-) diff --git a/examples/valve-app/valve-common/valve-app.matter b/examples/valve-app/valve-common/valve-app.matter index d5ee79a78b9deb..392adac7c19067 100644 --- a/examples/valve-app/valve-common/valve-app.matter +++ b/examples/valve-app/valve-common/valve-app.matter @@ -1278,7 +1278,6 @@ endpoint 0 { emits event AccessControlEntryChanged; emits event AccessControlExtensionChanged; callback attribute acl; - callback attribute extension; callback attribute subjectsPerAccessControlEntry; callback attribute targetsPerAccessControlEntry; callback attribute accessControlEntriesPerFabric; @@ -1313,7 +1312,7 @@ endpoint 0 { callback attribute specificationVersion; callback attribute maxPathsPerInvoke; ram attribute featureMap default = 0; - ram attribute clusterRevision default = 3; + ram attribute clusterRevision default = 4; } server cluster GeneralCommissioning { @@ -1323,7 +1322,7 @@ endpoint 0 { callback attribute locationCapability; callback attribute supportsConcurrentConnection; ram attribute featureMap default = 0; - ram attribute clusterRevision default = 0x0001; + ram attribute clusterRevision default = 0x0002; handle command ArmFailSafe; handle command ArmFailSafeResponse; @@ -1336,8 +1335,6 @@ endpoint 0 { server cluster NetworkCommissioning { ram attribute maxNetworks; callback attribute networks; - ram attribute scanMaxTimeSeconds; - ram attribute connectMaxTimeSeconds; ram attribute interfaceEnabled; ram attribute lastNetworkingStatus; ram attribute lastNetworkID; @@ -1438,7 +1435,7 @@ endpoint 1 { callback attribute eventList; callback attribute attributeList; ram attribute featureMap default = 0; - ram attribute clusterRevision default = 4; + ram attribute clusterRevision default = 5; handle command Identify; } @@ -1485,7 +1482,7 @@ endpoint 2 { callback attribute eventList; callback attribute attributeList; ram attribute featureMap default = 0; - ram attribute clusterRevision default = 4; + ram attribute clusterRevision default = 5; handle command Identify; } @@ -1532,7 +1529,7 @@ endpoint 3 { callback attribute eventList; callback attribute attributeList; ram attribute featureMap default = 0; - ram attribute clusterRevision default = 4; + ram attribute clusterRevision default = 5; handle command Identify; } @@ -1579,7 +1576,7 @@ endpoint 4 { callback attribute eventList; callback attribute attributeList; ram attribute featureMap default = 0; - ram attribute clusterRevision default = 4; + ram attribute clusterRevision default = 5; handle command Identify; } @@ -1626,7 +1623,7 @@ endpoint 5 { callback attribute eventList; callback attribute attributeList; ram attribute featureMap default = 0; - ram attribute clusterRevision default = 4; + ram attribute clusterRevision default = 5; handle command Identify; } @@ -1673,7 +1670,7 @@ endpoint 6 { callback attribute eventList; callback attribute attributeList; ram attribute featureMap default = 0; - ram attribute clusterRevision default = 4; + ram attribute clusterRevision default = 5; handle command Identify; } diff --git a/examples/valve-app/valve-common/valve-app.zap b/examples/valve-app/valve-common/valve-app.zap index 868a7e82bc1b76..00963f1a251821 100644 --- a/examples/valve-app/valve-common/valve-app.zap +++ b/examples/valve-app/valve-common/valve-app.zap @@ -191,22 +191,6 @@ "maxInterval": 65534, "reportableChange": 0 }, - { - "name": "Extension", - "code": 1, - "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": "SubjectsPerAccessControlEntry", "code": 2, @@ -691,7 +675,7 @@ "storageOption": "RAM", "singleton": 1, "bounded": 0, - "defaultValue": "3", + "defaultValue": "4", "reportable": 1, "minInterval": 0, "maxInterval": 65344, @@ -886,7 +870,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "0x0001", + "defaultValue": "0x0002", "reportable": 1, "minInterval": 0, "maxInterval": 65344, @@ -1008,38 +992,6 @@ "maxInterval": 65534, "reportableChange": 0 }, - { - "name": "ScanMaxTimeSeconds", - "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": "ConnectMaxTimeSeconds", - "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": "InterfaceEnabled", "code": 4, @@ -2028,7 +1980,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "4", + "defaultValue": "5", "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -2579,7 +2531,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "4", + "defaultValue": "5", "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -3130,7 +3082,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "4", + "defaultValue": "5", "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -3681,7 +3633,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "4", + "defaultValue": "5", "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -4232,7 +4184,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "4", + "defaultValue": "5", "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -4783,7 +4735,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "4", + "defaultValue": "5", "reportable": 1, "minInterval": 1, "maxInterval": 65534, From 6be91cd58b735e0073e91e76ecb5c14bfa42e37d Mon Sep 17 00:00:00 2001 From: Cecille Freeman Date: Fri, 27 Sep 2024 10:19:18 -0700 Subject: [PATCH 12/31] Add taglists to distinguish endpoints --- examples/valve-app/linux/main.cpp | 78 +++++++++++++++ examples/valve-app/valve-common/valve-app.zap | 96 +++++++++++++++++++ 2 files changed, 174 insertions(+) diff --git a/examples/valve-app/linux/main.cpp b/examples/valve-app/linux/main.cpp index 159ca16c7dc9ac..c82d2141deeb8a 100644 --- a/examples/valve-app/linux/main.cpp +++ b/examples/valve-app/linux/main.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -89,6 +90,76 @@ NonLevelValveEndpoint ep3(3); NonLevelValveEndpoint ep4(4); NonLevelValveEndpoint ep5(5); NonLevelValveEndpoint ep6(6); + +// from https://github.com/CHIP-Specifications/connectedhomeip-spec/blob/master/src/namespaces/Namespace-Common-Number.adoc +constexpr const uint8_t kNamespaceCommonNumber = 0x7; +constexpr const uint8_t kNamespaceCommonPosition = 0x8; +constexpr const uint8_t kNamespaceCommonPositionRow = 0x5; +constexpr const uint8_t kLiquidIdentificationTag = 0xAA; + +// TODO: Pull this from the CMD line - would need to be non-const, but that's fine +// This wouldn't work on an embedded device though, so leaving for now. +constexpr const char kEp1Label[] = "Bourbon"; +constexpr const char kEp2Label[] = "Campari"; +constexpr const char kEp3Label[] = "Vermouth"; +constexpr const char kEp4Label[] = "Gin"; +constexpr const char kEp5Label[] = "Sour mix"; +constexpr const char kEp6Label[] = "Simple syrup"; + +const Clusters::Descriptor::Structs::SemanticTagStruct::Type gEp1TagList[] = { + { .namespaceID = kNamespaceCommonNumber, .tag = 1 }, + { .namespaceID = kNamespaceCommonPosition, + .tag = kNamespaceCommonPositionRow, + .label = chip::MakeOptional(DataModel::Nullable("1"_span)) }, + { .mfgCode = DataModel::Nullable(chip::VendorId::Google), + .tag = kLiquidIdentificationTag, + .label = chip::MakeOptional(DataModel::Nullable(CharSpan::fromCharString(kEp1Label))) }, +}; +const Clusters::Descriptor::Structs::SemanticTagStruct::Type gEp2TagList[] = { + { .namespaceID = kNamespaceCommonNumber, .tag = 2 }, + { .namespaceID = kNamespaceCommonPosition, + .tag = kNamespaceCommonPositionRow, + .label = chip::MakeOptional(DataModel::Nullable("2"_span)) }, + { .mfgCode = DataModel::Nullable(chip::VendorId::Google), + .tag = kLiquidIdentificationTag, + .label = chip::MakeOptional(DataModel::Nullable(CharSpan::fromCharString(kEp2Label))) }, +}; +const Clusters::Descriptor::Structs::SemanticTagStruct::Type gEp3TagList[] = { + { .namespaceID = kNamespaceCommonNumber, .tag = 3 }, + { .namespaceID = kNamespaceCommonPosition, + .tag = kNamespaceCommonPositionRow, + .label = chip::MakeOptional(DataModel::Nullable("3"_span)) }, + { .mfgCode = DataModel::Nullable(chip::VendorId::Google), + .tag = kLiquidIdentificationTag, + .label = chip::MakeOptional(DataModel::Nullable(CharSpan::fromCharString(kEp3Label))) }, +}; +const Clusters::Descriptor::Structs::SemanticTagStruct::Type gEp4TagList[] = { + { .namespaceID = kNamespaceCommonNumber, .tag = 4 }, + { .namespaceID = kNamespaceCommonPosition, + .tag = kNamespaceCommonPositionRow, + .label = chip::MakeOptional(DataModel::Nullable("4"_span)) }, + { .mfgCode = DataModel::Nullable(chip::VendorId::Google), + .tag = kLiquidIdentificationTag, + .label = chip::MakeOptional(DataModel::Nullable(CharSpan::fromCharString(kEp4Label))) }, +}; +const Clusters::Descriptor::Structs::SemanticTagStruct::Type gEp5TagList[] = { + { .namespaceID = kNamespaceCommonNumber, .tag = 5 }, + { .namespaceID = kNamespaceCommonPosition, + .tag = kNamespaceCommonPositionRow, + .label = chip::MakeOptional(DataModel::Nullable("5"_span)) }, + { .mfgCode = DataModel::Nullable(chip::VendorId::Google), + .tag = kLiquidIdentificationTag, + .label = chip::MakeOptional(DataModel::Nullable(CharSpan::fromCharString(kEp5Label))) }, +}; +const Clusters::Descriptor::Structs::SemanticTagStruct::Type gEp6TagList[] = { + { .namespaceID = kNamespaceCommonNumber, .tag = 6 }, + { .namespaceID = kNamespaceCommonPosition, + .tag = kNamespaceCommonPositionRow, + .label = chip::MakeOptional(DataModel::Nullable("6"_span)) }, + { .mfgCode = DataModel::Nullable(chip::VendorId::Google), + .tag = kLiquidIdentificationTag, + .label = chip::MakeOptional(DataModel::Nullable(CharSpan::fromCharString(kEp6Label))) }, +}; } // namespace void ApplicationInit() @@ -100,6 +171,13 @@ void ApplicationInit() ep4.Init(); ep5.Init(); ep6.Init(); + // TODO: Can we pull these from the command line or something so these can be swapped on the fly? + SetTagList(/* endpoint= */ 1, Span(gEp1TagList)); + SetTagList(/* endpoint= */ 2, Span(gEp2TagList)); + SetTagList(/* endpoint= */ 3, Span(gEp3TagList)); + SetTagList(/* endpoint= */ 4, Span(gEp4TagList)); + SetTagList(/* endpoint= */ 5, Span(gEp5TagList)); + SetTagList(/* endpoint= */ 6, Span(gEp6TagList)); } void ApplicationShutdown() diff --git a/examples/valve-app/valve-common/valve-app.zap b/examples/valve-app/valve-common/valve-app.zap index 00963f1a251821..1ca7dff0a2b17a 100644 --- a/examples/valve-app/valve-common/valve-app.zap +++ b/examples/valve-app/valve-common/valve-app.zap @@ -2060,6 +2060,22 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "TagList", + "code": 4, + "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, @@ -2611,6 +2627,22 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "TagList", + "code": 4, + "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, @@ -3162,6 +3194,22 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "TagList", + "code": 4, + "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, @@ -3713,6 +3761,22 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "TagList", + "code": 4, + "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, @@ -4264,6 +4328,22 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "TagList", + "code": 4, + "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, @@ -4815,6 +4895,22 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "TagList", + "code": 4, + "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, From bc8f3d414e4f85c96424219194d9430766ce6b83 Mon Sep 17 00:00:00 2001 From: Cecille Freeman Date: Sat, 28 Sep 2024 19:10:12 -0700 Subject: [PATCH 13/31] Use Node label instead of tags --- examples/valve-app/linux/main.cpp | 37 +------------------------------ 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/examples/valve-app/linux/main.cpp b/examples/valve-app/linux/main.cpp index c82d2141deeb8a..b5b7d1621f76d2 100644 --- a/examples/valve-app/linux/main.cpp +++ b/examples/valve-app/linux/main.cpp @@ -91,74 +91,39 @@ NonLevelValveEndpoint ep4(4); NonLevelValveEndpoint ep5(5); NonLevelValveEndpoint ep6(6); -// from https://github.com/CHIP-Specifications/connectedhomeip-spec/blob/master/src/namespaces/Namespace-Common-Number.adoc -constexpr const uint8_t kNamespaceCommonNumber = 0x7; +// from https://github.com/CHIP-Specifications/connectedhomeip-spec/blob/master/src/namespaces/Namespace-Common-Position.adoc constexpr const uint8_t kNamespaceCommonPosition = 0x8; constexpr const uint8_t kNamespaceCommonPositionRow = 0x5; -constexpr const uint8_t kLiquidIdentificationTag = 0xAA; - -// TODO: Pull this from the CMD line - would need to be non-const, but that's fine -// This wouldn't work on an embedded device though, so leaving for now. -constexpr const char kEp1Label[] = "Bourbon"; -constexpr const char kEp2Label[] = "Campari"; -constexpr const char kEp3Label[] = "Vermouth"; -constexpr const char kEp4Label[] = "Gin"; -constexpr const char kEp5Label[] = "Sour mix"; -constexpr const char kEp6Label[] = "Simple syrup"; const Clusters::Descriptor::Structs::SemanticTagStruct::Type gEp1TagList[] = { - { .namespaceID = kNamespaceCommonNumber, .tag = 1 }, { .namespaceID = kNamespaceCommonPosition, .tag = kNamespaceCommonPositionRow, .label = chip::MakeOptional(DataModel::Nullable("1"_span)) }, - { .mfgCode = DataModel::Nullable(chip::VendorId::Google), - .tag = kLiquidIdentificationTag, - .label = chip::MakeOptional(DataModel::Nullable(CharSpan::fromCharString(kEp1Label))) }, }; const Clusters::Descriptor::Structs::SemanticTagStruct::Type gEp2TagList[] = { - { .namespaceID = kNamespaceCommonNumber, .tag = 2 }, { .namespaceID = kNamespaceCommonPosition, .tag = kNamespaceCommonPositionRow, .label = chip::MakeOptional(DataModel::Nullable("2"_span)) }, - { .mfgCode = DataModel::Nullable(chip::VendorId::Google), - .tag = kLiquidIdentificationTag, - .label = chip::MakeOptional(DataModel::Nullable(CharSpan::fromCharString(kEp2Label))) }, }; const Clusters::Descriptor::Structs::SemanticTagStruct::Type gEp3TagList[] = { - { .namespaceID = kNamespaceCommonNumber, .tag = 3 }, { .namespaceID = kNamespaceCommonPosition, .tag = kNamespaceCommonPositionRow, .label = chip::MakeOptional(DataModel::Nullable("3"_span)) }, - { .mfgCode = DataModel::Nullable(chip::VendorId::Google), - .tag = kLiquidIdentificationTag, - .label = chip::MakeOptional(DataModel::Nullable(CharSpan::fromCharString(kEp3Label))) }, }; const Clusters::Descriptor::Structs::SemanticTagStruct::Type gEp4TagList[] = { - { .namespaceID = kNamespaceCommonNumber, .tag = 4 }, { .namespaceID = kNamespaceCommonPosition, .tag = kNamespaceCommonPositionRow, .label = chip::MakeOptional(DataModel::Nullable("4"_span)) }, - { .mfgCode = DataModel::Nullable(chip::VendorId::Google), - .tag = kLiquidIdentificationTag, - .label = chip::MakeOptional(DataModel::Nullable(CharSpan::fromCharString(kEp4Label))) }, }; const Clusters::Descriptor::Structs::SemanticTagStruct::Type gEp5TagList[] = { - { .namespaceID = kNamespaceCommonNumber, .tag = 5 }, { .namespaceID = kNamespaceCommonPosition, .tag = kNamespaceCommonPositionRow, .label = chip::MakeOptional(DataModel::Nullable("5"_span)) }, - { .mfgCode = DataModel::Nullable(chip::VendorId::Google), - .tag = kLiquidIdentificationTag, - .label = chip::MakeOptional(DataModel::Nullable(CharSpan::fromCharString(kEp5Label))) }, }; const Clusters::Descriptor::Structs::SemanticTagStruct::Type gEp6TagList[] = { - { .namespaceID = kNamespaceCommonNumber, .tag = 6 }, { .namespaceID = kNamespaceCommonPosition, .tag = kNamespaceCommonPositionRow, .label = chip::MakeOptional(DataModel::Nullable("6"_span)) }, - { .mfgCode = DataModel::Nullable(chip::VendorId::Google), - .tag = kLiquidIdentificationTag, - .label = chip::MakeOptional(DataModel::Nullable(CharSpan::fromCharString(kEp6Label))) }, }; } // namespace From ab2b7bdd0435141c2a9761e869368cc8e5d8f389 Mon Sep 17 00:00:00 2001 From: Cecille Freeman Date: Sat, 28 Sep 2024 12:40:03 -0700 Subject: [PATCH 14/31] Valve: fix write to use KVS manager properly --- examples/valve-app/linux/main.cpp | 10 ++++-- ...configuration-and-control-server-disco.cpp | 35 +++++++++++++------ 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/examples/valve-app/linux/main.cpp b/examples/valve-app/linux/main.cpp index b5b7d1621f76d2..d86f65ca1d5e43 100644 --- a/examples/valve-app/linux/main.cpp +++ b/examples/valve-app/linux/main.cpp @@ -54,11 +54,15 @@ class PrintOnlyDelegate : public NonLevelControlDelegate EndpointId mEndpoint; }; +// TODO: Does this conflict with the kvs delegate that's statically allocated in the AppMain? +// I think as long as the manager is the same, we're ok, but need to confirm. +KvsPersistentStorageDelegate sStorage; + class NonLevelValveEndpoint { public: NonLevelValveEndpoint(EndpointId endpoint) : - mEndpoint(endpoint), mContext(mEndpoint, storage), mDelegate(mEndpoint), mLogic(mDelegate, mContext), + mEndpoint(endpoint), mContext(mEndpoint, sStorage), mDelegate(mEndpoint), mLogic(mDelegate, mContext), mInterface(mEndpoint, mLogic) {} CHIP_ERROR Init() @@ -77,7 +81,6 @@ class NonLevelValveEndpoint .valveFault = 0, .levelStep = 1 }; EndpointId mEndpoint; - KvsPersistentStorageDelegate storage; MatterContext mContext; PrintOnlyDelegate mDelegate; ClusterLogic mLogic; @@ -130,6 +133,9 @@ const Clusters::Descriptor::Structs::SemanticTagStruct::Type gEp6TagList[] = { void ApplicationInit() { ChipLogError(NotSpecified, "App init!!!"); + chip::DeviceLayer::PersistedStorage::KeyValueStoreManager & kvsManager = DeviceLayer::PersistedStorage::KeyValueStoreMgr(); + sStorage.Init(&kvsManager); + ep1.Init(); ep2.Init(); ep3.Init(); diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server-disco.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server-disco.cpp index 7aebf0db153fe6..ad105c43714329 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server-disco.cpp +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server-disco.cpp @@ -41,17 +41,12 @@ using namespace Commands; using namespace Protocols::InteractionModel; namespace { -template -CHIP_ERROR EncodeRead(AttributeValueEncoder & aEncoder, const F & getter) +CHIP_ERROR TranslateErrorToIMStatus(CHIP_ERROR err) { - T ret; - CHIP_ERROR err = getter(ret); if (err == CHIP_NO_ERROR) { - err = aEncoder.Encode(ret); + return err; } - - // TODO: Should the logic return these directly? I didn't want to mix the IM layer into there, but this is annoying. if (err == CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE) { return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); @@ -61,7 +56,26 @@ CHIP_ERROR EncodeRead(AttributeValueEncoder & aEncoder, const F & getter) // what actually gets returned here? This is really an internal error, so failure seems perhaps correct. return CHIP_IM_GLOBAL_STATUS(Failure); } - return err; + if (err == CHIP_ERROR_INVALID_ARGUMENT) + { + return CHIP_IM_GLOBAL_STATUS(ConstraintError); + } + // Catch-all error + return CHIP_IM_GLOBAL_STATUS(Failure); +} + +template +CHIP_ERROR EncodeRead(AttributeValueEncoder & aEncoder, const F & getter) +{ + T ret; + CHIP_ERROR err = getter(ret); + if (err == CHIP_NO_ERROR) + { + err = aEncoder.Encode(ret); + } + + // TODO: Should the logic return these directly? I didn't want to mix the IM layer into there, but this is annoying. + return TranslateErrorToIMStatus(err); } } // namespace @@ -135,12 +149,13 @@ CHIP_ERROR Interface::Write(const ConcreteDataAttributePath & aPath, AttributeVa case DefaultOpenDuration::Id: { DefaultOpenDuration::TypeInfo::Type val; ReturnErrorOnFailure(aDecoder.Decode(val)); - return mClusterLogic.SetDefaultOpenDuration(val); + return TranslateErrorToIMStatus(mClusterLogic.SetDefaultOpenDuration(val)); } + break; case DefaultOpenLevel::Id: { DefaultOpenLevel::TypeInfo::Type val; ReturnErrorOnFailure(aDecoder.Decode(val)); - return mClusterLogic.SetDefaultOpenLevel(val); + return TranslateErrorToIMStatus(mClusterLogic.SetDefaultOpenLevel(val)); } default: return CHIP_IM_GLOBAL_STATUS(UnsupportedWrite); From 07122935742f03c1fc27fbe0d99bbfb090b9a525 Mon Sep 17 00:00:00 2001 From: Cecille Freeman Date: Sat, 28 Sep 2024 09:10:28 -0700 Subject: [PATCH 15/31] TC-DESC-2.2: Fix comparison when mfg label is in the list Unit tests included, also tested on an example app with mfg tags --- src/python_testing/TestMatterTestingSupport.py | 8 ++++++++ src/python_testing/taglist_and_topology_test_support.py | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/src/python_testing/TestMatterTestingSupport.py b/src/python_testing/TestMatterTestingSupport.py index d2a259f154adc6..888aa0b8b077f9 100644 --- a/src/python_testing/TestMatterTestingSupport.py +++ b/src/python_testing/TestMatterTestingSupport.py @@ -548,6 +548,14 @@ def test_tag_list_problems(self): problems = find_tag_list_problems(roots, device_types, simple) asserts.assert_equal(len(problems), 0, "Unexpected problems found in list") + # Tags with mfg tags + tag_mfg = Clusters.Descriptor.Structs.SemanticTagStruct(mfgCode=0xFFF1, label="test") + tag_label = Clusters.Descriptor.Structs.SemanticTagStruct(tag=1, label="test") + simple[1][Clusters.Descriptor][Clusters.Descriptor.Attributes.TagList] = [tag1, tag_mfg] + simple[2][Clusters.Descriptor][Clusters.Descriptor.Attributes.TagList] = [tag1, tag_label] + problems = find_tag_list_problems(roots, device_types, simple) + asserts.assert_equal(len(problems), 0, "Unexpected problems found in list") + def test_root_node_tag_list_functions(self): # Example topology - see comment above for the layout. # There are 4 direct children of root 0 diff --git a/src/python_testing/taglist_and_topology_test_support.py b/src/python_testing/taglist_and_topology_test_support.py index bf5c085bf84d12..5456d9ada46022 100644 --- a/src/python_testing/taglist_and_topology_test_support.py +++ b/src/python_testing/taglist_and_topology_test_support.py @@ -18,6 +18,7 @@ import functools from collections import defaultdict from dataclasses import dataclass, field +from chip.clusters.Types import Nullable, NullValue from typing import Any import chip.clusters as Clusters @@ -143,12 +144,16 @@ def create_device_type_list_for_root(direct_children, endpoint_dict: dict[int, A def cmp_tag_list(a: Clusters.Descriptor.Structs.SemanticTagStruct, b: Clusters.Descriptor.Structs.SemanticTagStruct): + if type(a.mfgCode) != type(b.mfgCode): + return -1 if type(a.mfgCode) is Nullable else 1 if a.mfgCode != b.mfgCode: return -1 if a.mfgCode < b.mfgCode else 1 if a.namespaceID != b.namespaceID: return -1 if a.namespaceID < b.namespaceID else 1 if a.tag != b.tag: return -1 if a.tag < b.tag else 1 + if type(a.label) != type(b.label): + return -1 if type(a.label) is Nullable or a.label is None else 1 if a.label != b.label: return -1 if a.label < b.label else 1 return 0 From 2710453a4c9a78767ad7765086345da9e3f81c8f Mon Sep 17 00:00:00 2001 From: Cecille Freeman Date: Sat, 28 Sep 2024 09:55:59 -0700 Subject: [PATCH 16/31] TC-VALCC-3.1: Allow immediate open of valve If the valve can be opened before the command is complete or before the next read is done, the test will fail because it expects the transitioning phase to happen. In the spec: When the movement is complete, the device SHALL set the CurrentState attribute to the Open value. ^ this can happen before the end of the command. --- src/python_testing/TC_VALCC_3_1.py | 54 ++++++++++++++++-------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/python_testing/TC_VALCC_3_1.py b/src/python_testing/TC_VALCC_3_1.py index e24c09db66c35d..9bb3dd8d306ce5 100644 --- a/src/python_testing/TC_VALCC_3_1.py +++ b/src/python_testing/TC_VALCC_3_1.py @@ -45,11 +45,13 @@ def steps_TC_VALCC_3_1(self) -> list[TestStep]: steps = [ TestStep(1, "Commissioning, already done", is_commissioning=True), TestStep(2, "Send Open command"), - TestStep(3, "Read TargetState attribute"), - TestStep(4, "Read CurrentState attribute"), + TestStep(3, "Read CurrentState and TargetState attribute until CurrentState is Open", + "Target state is Open while CurrentState is Transitioning"), + TestStep(4, "Read CurrentState and TargetState attribute", "CurrentState is Open, TargetState is NULL"), TestStep(5, "Send Close command"), - TestStep(6, "Read TargetState attribute"), - TestStep(7, "Read CurrentState attribute"), + TestStep(6, "Read CurrentState and TargetState attribute until CurrentState is Open", + "Target state is Closed while CurrentState is Transitioning"), + TestStep(7, "Read CurrentState and TargetState attribute", "CurrentState is Closed, TargetState is NULL"), ] return steps @@ -65,7 +67,8 @@ async def test_TC_VALCC_3_1(self): endpoint = self.user_params.get("endpoint", 1) self.step(1) - attributes = Clusters.ValveConfigurationAndControl.Attributes + cluster = Clusters.ValveConfigurationAndControl + attributes = cluster.Attributes self.step(2) try: @@ -75,24 +78,26 @@ async def test_TC_VALCC_3_1(self): pass self.step(3) - target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) - - asserts.assert_true(target_state_dut is not NullValue, "TargetState is null") - asserts.assert_equal(target_state_dut, Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kOpen, - "TargetState is not the expected value") - self.step(4) + # Read target state first in case the current state goes to open between these calls. + # The test re-reads target state after CurrentState goes to open. + target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) - asserts.assert_true(current_state_dut is not NullValue, "CurrentState is null") - while current_state_dut is Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kTransitioning: + while current_state_dut is cluster.Enums.ValveStateEnum.kTransitioning: + asserts.assert_equal(target_state_dut, cluster.Enums.ValveStateEnum.kOpen, "TargetState is incorrect") time.sleep(1) + target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) asserts.assert_true(current_state_dut is not NullValue, "CurrentState is null") + asserts.assert_true(target_state_dut is not NullValue, "TargetState is null") - asserts.assert_equal(current_state_dut, Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kOpen, - "CurrentState is not the expected value") + self.step(4) + current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) + asserts.assert_equal(current_state_dut, cluster.Enums.ValveStateEnum.kOpen, "CurrentState is incorrect") + target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) + asserts.assert_true(target_state_dut is NullValue, "TargetState is not null") self.step(5) try: @@ -103,23 +108,22 @@ async def test_TC_VALCC_3_1(self): self.step(6) target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) - - asserts.assert_true(target_state_dut is not NullValue, "TargetState is null") - asserts.assert_equal(target_state_dut, Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kClosed, - "TargetState is not the expected value") - - self.step(7) current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) - asserts.assert_true(current_state_dut is not NullValue, "CurrentState is null") - while current_state_dut is Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kTransitioning: + while current_state_dut is cluster.Enums.ValveStateEnum.kTransitioning: + asserts.assert_equal(target_state_dut, cluster.Enums.ValveStateEnum.kClosed, "TargetState is incorrect") time.sleep(1) + target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) asserts.assert_true(current_state_dut is not NullValue, "CurrentState is null") + asserts.assert_true(target_state_dut is not NullValue, "TargetState is null") - asserts.assert_equal(current_state_dut, Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kClosed, - "CurrentState is not the expected value") + self.step(7) + current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) + asserts.assert_equal(current_state_dut, cluster.Enums.ValveStateEnum.kClosed, "CurrentState is incorrect") + target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) + asserts.assert_true(target_state_dut is NullValue, "TargetState is not null") if __name__ == "__main__": From e0195d7e1a206f466ce5088300d5c484b0f84592 Mon Sep 17 00:00:00 2001 From: Cecille Freeman Date: Sat, 28 Sep 2024 12:41:22 -0700 Subject: [PATCH 17/31] TC-VALCC-4.2: unconditionally write open duration This way we can ensure the open duration is writeable. Also add a cleanup step. --- src/python_testing/TC_VALCC_4_2.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/python_testing/TC_VALCC_4_2.py b/src/python_testing/TC_VALCC_4_2.py index 7e331f5f0fa33a..d85aa99b2a107d 100644 --- a/src/python_testing/TC_VALCC_4_2.py +++ b/src/python_testing/TC_VALCC_4_2.py @@ -55,6 +55,7 @@ def steps_TC_VALCC_4_2(self) -> list[TestStep]: TestStep(8, "Send Close command"), TestStep(9, "Read OpenDuration attribute"), TestStep(10, "Read RemainingDuration attribute"), + TestStep(11, "Write DefaultOpenDuration back to original value") ] return steps @@ -73,16 +74,11 @@ async def test_TC_VALCC_4_2(self): attributes = Clusters.ValveConfigurationAndControl.Attributes self.step("2a") - defaultOpenDuration = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.DefaultOpenDuration) + originalDefaultOpenDuration = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.DefaultOpenDuration) self.step("2b") - if defaultOpenDuration is NullValue: - defaultOpenDuration = 60 - - result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, attributes.DefaultOpenDuration(defaultOpenDuration))]) - asserts.assert_equal(result[0].Status, Status.Success, "DefaultOpenDuration write failed") - else: - logging.info("Test step skipped") + defaultOpenDuration = 60 + await self.write_single_attribute(attributes.DefaultOpenDuration(defaultOpenDuration)) self.step(3) try: @@ -129,6 +125,9 @@ async def test_TC_VALCC_4_2(self): remaining_duration_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.RemainingDuration) asserts.assert_true(remaining_duration_dut is NullValue, "RemainingDuration is not null") + self.step(11) + await self.write_single_attribute(attributes.DefaultOpenDuration(originalDefaultOpenDuration)) + if __name__ == "__main__": default_matter_test_main() From eb2dcbf8d245d1a3950432581db79594427ea533 Mon Sep 17 00:00:00 2001 From: Cecille Freeman Date: Sat, 28 Sep 2024 12:50:35 -0700 Subject: [PATCH 18/31] VALCC: Remove top-level PICS Self-select the valve tests off the device information rather than the PICS. This makes it easier to run the tests since you can just run them all unconditionally and they will run if required. --- src/python_testing/TC_VALCC_2_1.py | 12 +--- src/python_testing/TC_VALCC_3_1.py | 12 +--- src/python_testing/TC_VALCC_3_2.py | 94 +++++++++++------------------ src/python_testing/TC_VALCC_3_3.py | 97 +++++++++++------------------- src/python_testing/TC_VALCC_3_4.py | 27 ++------- src/python_testing/TC_VALCC_4_1.py | 12 +--- src/python_testing/TC_VALCC_4_2.py | 12 +--- src/python_testing/TC_VALCC_4_3.py | 39 ++---------- src/python_testing/TC_VALCC_4_4.py | 34 +++-------- src/python_testing/TC_VALCC_4_5.py | 12 +--- 10 files changed, 101 insertions(+), 250 deletions(-) diff --git a/src/python_testing/TC_VALCC_2_1.py b/src/python_testing/TC_VALCC_2_1.py index ba9e98e3dbe7a3..4df720bf3c709b 100644 --- a/src/python_testing/TC_VALCC_2_1.py +++ b/src/python_testing/TC_VALCC_2_1.py @@ -28,7 +28,7 @@ import chip.clusters as Clusters from chip.clusters.Types import NullValue -from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from matter_testing_support import MatterBaseTest, TestStep, default_matter_test_main, has_cluster, run_if_endpoint_matches from mobly import asserts @@ -58,16 +58,10 @@ def steps_TC_VALCC_2_1(self) -> list[TestStep]: ] return steps - def pics_TC_VALCC_2_1(self) -> list[str]: - pics = [ - "VALCC.S", - ] - return pics - - @async_test_body + @run_if_endpoint_matches(has_cluster(Clusters.ValveConfigurationAndControl)) async def test_TC_VALCC_2_1(self): - endpoint = self.user_params.get("endpoint", 1) + endpoint = self.matter_test_config.endpoint self.step(1) attributes = Clusters.ValveConfigurationAndControl.Attributes diff --git a/src/python_testing/TC_VALCC_3_1.py b/src/python_testing/TC_VALCC_3_1.py index 9bb3dd8d306ce5..0dea5231a9c787 100644 --- a/src/python_testing/TC_VALCC_3_1.py +++ b/src/python_testing/TC_VALCC_3_1.py @@ -29,7 +29,7 @@ import chip.clusters as Clusters from chip.clusters.Types import NullValue from chip.interaction_model import InteractionModelError, Status -from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from matter_testing_support import MatterBaseTest, TestStep, default_matter_test_main, has_cluster, run_if_endpoint_matches from mobly import asserts @@ -55,16 +55,10 @@ def steps_TC_VALCC_3_1(self) -> list[TestStep]: ] return steps - def pics_TC_VALCC_3_1(self) -> list[str]: - pics = [ - "VALCC.S", - ] - return pics - - @async_test_body + @run_if_endpoint_matches(has_cluster(Clusters.ValveConfigurationAndControl)) async def test_TC_VALCC_3_1(self): - endpoint = self.user_params.get("endpoint", 1) + endpoint = self.matter_test_config.endpoint self.step(1) cluster = Clusters.ValveConfigurationAndControl diff --git a/src/python_testing/TC_VALCC_3_2.py b/src/python_testing/TC_VALCC_3_2.py index 9ef0886d8b0cba..888eed5a913a95 100644 --- a/src/python_testing/TC_VALCC_3_2.py +++ b/src/python_testing/TC_VALCC_3_2.py @@ -30,7 +30,7 @@ import chip.clusters as Clusters from chip.clusters.Types import NullValue from chip.interaction_model import InteractionModelError, Status -from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from matter_testing_support import MatterBaseTest, TestStep, default_matter_test_main, has_feature, run_if_endpoint_matches from mobly import asserts @@ -55,92 +55,66 @@ def steps_TC_VALCC_3_2(self) -> list[TestStep]: ] return steps - def pics_TC_VALCC_3_2(self) -> list[str]: - pics = [ - "VALCC.S", - ] - return pics - - @async_test_body + @run_if_endpoint_matches(has_feature(Clusters.ValveConfigurationAndControl, Clusters.ValveConfigurationAndControl.Bitmaps.Feature.kLevel)) async def test_TC_VALCC_3_2(self): - endpoint = self.user_params.get("endpoint", 1) + endpoint = self.matter_test_config.endpoint self.step(1) attributes = Clusters.ValveConfigurationAndControl.Attributes self.step(2) - feature_map = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.FeatureMap) - - is_lvl_feature_supported = feature_map & Clusters.ValveConfigurationAndControl.Bitmaps.Feature.kLevel + # Done as part of the test initialization self.step(3) - if is_lvl_feature_supported: - try: - await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Open(targetLevel=100), endpoint=endpoint) - except InteractionModelError as e: - asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") - pass - else: - logging.info("Test step skipped") + try: + await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Open(targetLevel=100), endpoint=endpoint) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass self.step(4) - if is_lvl_feature_supported: - target_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetLevel) + target_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetLevel) - asserts.assert_true(target_level_dut is not NullValue, "TargetLevel is null") - asserts.assert_equal(target_level_dut, 100, "TargetLevel is not the expected value") - else: - logging.info("Test step skipped") + asserts.assert_true(target_level_dut is not NullValue, "TargetLevel is null") + asserts.assert_equal(target_level_dut, 100, "TargetLevel is not the expected value") self.step(5) - if is_lvl_feature_supported: - current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) - asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") + current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) + asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") - while current_level_dut != 100: - time.sleep(1) + while current_level_dut != 100: + time.sleep(1) - current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) - asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") + current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) + asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") - asserts.assert_equal(current_level_dut, 100, "CurrentLevel is not the expected value") - else: - logging.info("Test step skipped") + asserts.assert_equal(current_level_dut, 100, "CurrentLevel is not the expected value") self.step(6) - if is_lvl_feature_supported: - try: - await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Close(), endpoint=endpoint) - except InteractionModelError as e: - asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") - pass - else: - logging.info("Test step skipped") + try: + await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Close(), endpoint=endpoint) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass self.step(7) - if is_lvl_feature_supported: - target_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetLevel) + target_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetLevel) - asserts.assert_true(target_level_dut is not NullValue, "TargetLevel is null") - asserts.assert_equal(target_level_dut, 0, "TargetLevel is not the expected value") - else: - logging.info("Test step skipped") + asserts.assert_true(target_level_dut is not NullValue, "TargetLevel is null") + asserts.assert_equal(target_level_dut, 0, "TargetLevel is not the expected value") self.step(8) - if is_lvl_feature_supported: - current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) - asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") + current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) + asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") - while current_level_dut is Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kTransitioning: - time.sleep(1) + while current_level_dut is Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kTransitioning: + time.sleep(1) - current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) - asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") + current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) + asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") - asserts.assert_equal(current_level_dut, 0, "CurrentLevel is not the expected value") - else: - logging.info("Test step skipped") + asserts.assert_equal(current_level_dut, 0, "CurrentLevel is not the expected value") if __name__ == "__main__": diff --git a/src/python_testing/TC_VALCC_3_3.py b/src/python_testing/TC_VALCC_3_3.py index ddf3ba83d1b5c7..062c082b454a92 100644 --- a/src/python_testing/TC_VALCC_3_3.py +++ b/src/python_testing/TC_VALCC_3_3.py @@ -30,7 +30,7 @@ import chip.clusters as Clusters from chip.clusters.Types import NullValue from chip.interaction_model import InteractionModelError, Status -from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from matter_testing_support import MatterBaseTest, TestStep, default_matter_test_main, has_attribute, run_if_endpoint_matches from mobly import asserts @@ -56,96 +56,69 @@ def steps_TC_VALCC_3_3(self) -> list[TestStep]: ] return steps - def pics_TC_VALCC_3_3(self) -> list[str]: - pics = [ - "VALCC.S", - ] - return pics - - @async_test_body + @run_if_endpoint_matches(has_attribute(Clusters.ValveConfigurationAndControl.Attributes.DefaultOpenLevel)) async def test_TC_VALCC_3_3(self): - endpoint = self.user_params.get("endpoint", 1) + endpoint = self.matter_test_config.endpoint self.step(1) attributes = Clusters.ValveConfigurationAndControl.Attributes self.step(2) - attribute_list = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.AttributeList) + # Done as part of the test initialization self.step(3) - if attributes.DefaultOpenLevel.attribute_id in attribute_list: - defaultOpenLevel = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.DefaultOpenLevel) - else: - logging.info("Test step skipped") + defaultOpenLevel = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.DefaultOpenLevel) self.step(4) - if attributes.DefaultOpenLevel.attribute_id in attribute_list: - try: - await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Open(), endpoint=endpoint) - except InteractionModelError as e: - asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") - pass - else: - logging.info("Test step skipped") + try: + await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Open(), endpoint=endpoint) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass self.step(5) - if attributes.DefaultOpenLevel.attribute_id in attribute_list: - target_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetLevel) + target_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetLevel) - asserts.assert_true(target_level_dut is not NullValue, "TargetLevel is null") - asserts.assert_equal(target_level_dut, defaultOpenLevel, "TargetLevel is not the expected value") - else: - logging.info("Test step skipped") + asserts.assert_true(target_level_dut is not NullValue, "TargetLevel is null") + asserts.assert_equal(target_level_dut, defaultOpenLevel, "TargetLevel is not the expected value") self.step(6) - if attributes.DefaultOpenLevel.attribute_id in attribute_list: - current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) - asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") + current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) + asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") - while current_level_dut != defaultOpenLevel: - time.sleep(1) + while current_level_dut != defaultOpenLevel: + time.sleep(1) - current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) - asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") + current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) + asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") - asserts.assert_equal(current_level_dut, defaultOpenLevel, "CurrentLevel is not the expected value") - else: - logging.info("Test step skipped") + asserts.assert_equal(current_level_dut, defaultOpenLevel, "CurrentLevel is not the expected value") self.step(7) - if attributes.DefaultOpenLevel.attribute_id in attribute_list: - try: - await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Close(), endpoint=endpoint) - except InteractionModelError as e: - asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") - pass - else: - logging.info("Test step skipped") + try: + await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Close(), endpoint=endpoint) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass self.step(8) - if attributes.DefaultOpenLevel.attribute_id in attribute_list: - target_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetLevel) + target_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetLevel) - asserts.assert_true(target_level_dut is not NullValue, "TargetLevel is null") - asserts.assert_equal(target_level_dut, 0, "TargetLevel is not the expected value") - else: - logging.info("Test step skipped") + asserts.assert_true(target_level_dut is not NullValue, "TargetLevel is null") + asserts.assert_equal(target_level_dut, 0, "TargetLevel is not the expected value") self.step(9) - if attributes.DefaultOpenLevel.attribute_id in attribute_list: - current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) - asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") + current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) + asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") - while current_level_dut is Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kTransitioning: - time.sleep(1) + while current_level_dut is Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kTransitioning: + time.sleep(1) - current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) - asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") + current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) + asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") - asserts.assert_equal(current_level_dut, 0, "CurrentLevel is not the expected value") - else: - logging.info("Test step skipped") + asserts.assert_equal(current_level_dut, 0, "CurrentLevel is not the expected value") if __name__ == "__main__": diff --git a/src/python_testing/TC_VALCC_3_4.py b/src/python_testing/TC_VALCC_3_4.py index 906854011aceb3..24bba2d9dcc96c 100644 --- a/src/python_testing/TC_VALCC_3_4.py +++ b/src/python_testing/TC_VALCC_3_4.py @@ -28,7 +28,7 @@ import chip.clusters as Clusters from chip.interaction_model import InteractionModelError, Status -from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from matter_testing_support import MatterBaseTest, TestStep, default_matter_test_main, has_attribute, run_if_endpoint_matches from mobly import asserts @@ -51,36 +51,17 @@ def steps_TC_VALCC_3_4(self) -> list[TestStep]: ] return steps - def pics_TC_VALCC_3_4(self) -> list[str]: - pics = [ - "VALCC.S", - ] - return pics - - @async_test_body + @run_if_endpoint_matches(has_attribute(Clusters.ValveConfigurationAndControl.Attributes.LevelStep)) async def test_TC_VALCC_3_4(self): - endpoint = self.user_params.get("endpoint", 1) + endpoint = self.matter_test_config.endpoint self.step(1) attributes = Clusters.ValveConfigurationAndControl.Attributes self.step(2) - attribute_list = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.AttributeList) - self.step(3) - if attributes.LevelStep.attribute_id not in attribute_list: - logging.info("LevelStep not supported skipping test case") - - # Skipping all remainig steps - for step in self.get_test_steps(self.current_test_info.name)[self.current_step_index:]: - self.step(step.test_plan_number) - logging.info("Test step skipped") - - return - - else: - logging.info("Test step skipped") + # Steps 2 and three are handled by the decorator self.step(4) levelStep = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.LevelStep) diff --git a/src/python_testing/TC_VALCC_4_1.py b/src/python_testing/TC_VALCC_4_1.py index 183151af614893..646f9ac25d5bcc 100644 --- a/src/python_testing/TC_VALCC_4_1.py +++ b/src/python_testing/TC_VALCC_4_1.py @@ -29,7 +29,7 @@ import chip.clusters as Clusters from chip.clusters.Types import NullValue from chip.interaction_model import InteractionModelError, Status -from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from matter_testing_support import MatterBaseTest, TestStep, default_matter_test_main, has_cluster, run_if_endpoint_matches from mobly import asserts @@ -55,16 +55,10 @@ def steps_TC_VALCC_4_1(self) -> list[TestStep]: ] return steps - def pics_TC_VALCC_4_1(self) -> list[str]: - pics = [ - "VALCC.S", - ] - return pics - - @async_test_body + @run_if_endpoint_matches(has_cluster(Clusters.ValveConfigurationAndControl)) async def test_TC_VALCC_4_1(self): - endpoint = self.user_params.get("endpoint", 1) + endpoint = self.matter_test_config.endpoint self.step(1) attributes = Clusters.ValveConfigurationAndControl.Attributes diff --git a/src/python_testing/TC_VALCC_4_2.py b/src/python_testing/TC_VALCC_4_2.py index d85aa99b2a107d..72940a391ae8f6 100644 --- a/src/python_testing/TC_VALCC_4_2.py +++ b/src/python_testing/TC_VALCC_4_2.py @@ -30,7 +30,7 @@ import chip.clusters as Clusters from chip.clusters.Types import NullValue from chip.interaction_model import InteractionModelError, Status -from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from matter_testing_support import MatterBaseTest, TestStep, default_matter_test_main, has_cluster, run_if_endpoint_matches from mobly import asserts @@ -59,16 +59,10 @@ def steps_TC_VALCC_4_2(self) -> list[TestStep]: ] return steps - def pics_TC_VALCC_4_2(self) -> list[str]: - pics = [ - "VALCC.S", - ] - return pics - - @async_test_body + @run_if_endpoint_matches(has_cluster(Clusters.ValveConfigurationAndControl)) async def test_TC_VALCC_4_2(self): - endpoint = self.user_params.get("endpoint", 1) + endpoint = self.matter_test_config.endpoint self.step(1) attributes = Clusters.ValveConfigurationAndControl.Attributes diff --git a/src/python_testing/TC_VALCC_4_3.py b/src/python_testing/TC_VALCC_4_3.py index cdb8c38ca7e286..cb5bb010779d90 100644 --- a/src/python_testing/TC_VALCC_4_3.py +++ b/src/python_testing/TC_VALCC_4_3.py @@ -29,7 +29,7 @@ import chip.clusters as Clusters from chip.clusters.Types import NullValue from chip.interaction_model import InteractionModelError, Status -from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from matter_testing_support import MatterBaseTest, TestStep, default_matter_test_main, has_feature, run_if_endpoint_matches from mobly import asserts @@ -55,38 +55,17 @@ def steps_TC_VALCC_4_3(self) -> list[TestStep]: ] return steps - def pics_TC_VALCC_4_3(self) -> list[str]: - pics = [ - "VALCC.S", - ] - return pics - - @async_test_body + @run_if_endpoint_matches(has_feature(Clusters.ValveConfigurationAndControl, Clusters.ValveConfigurationAndControl.Bitmaps.Feature.kTimeSync)) async def test_TC_VALCC_4_3(self): - endpoint = self.user_params.get("endpoint", 1) + endpoint = self.matter_test_config.endpoint self.step(1) attributes = Clusters.ValveConfigurationAndControl.Attributes self.step("2a") - feature_map = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.FeatureMap) - - is_ts_feature_supported = feature_map & Clusters.ValveConfigurationAndControl.Bitmaps.Feature.kTimeSync - self.step("2b") - if not is_ts_feature_supported: - logging.info("TimeSync feature not supported skipping test case") - - # Skipping all remainig steps - for step in self.get_test_steps(self.current_test_info.name)[self.current_step_index:]: - self.step(step.test_plan_number) - logging.info("Test step skipped") - - return - - else: - logging.info("Test step skipped") + # steps 2a and 2b are handled by the decorator self.step("3a") utcTime = await self.read_single_attribute_check_success(endpoint=0, cluster=Clusters.Objects.TimeSynchronization, attribute=Clusters.TimeSynchronization.Attributes.UTCTime) @@ -95,16 +74,10 @@ async def test_TC_VALCC_4_3(self): if utcTime is not NullValue: logging.info("UTCTime is not null, skipping test case") - # Skipping all remainig steps - for step in self.get_test_steps(self.current_test_info.name)[self.current_step_index:]: - self.step(step.test_plan_number) - logging.info("Test step skipped") - + # Skipping all remaining steps + self.skip_all_remaining_steps(4) return - else: - logging.info("Test step skipped") - self.step(4) try: await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Open(), endpoint=endpoint) diff --git a/src/python_testing/TC_VALCC_4_4.py b/src/python_testing/TC_VALCC_4_4.py index 4fd1692da370bf..23d1b71a9ddc06 100644 --- a/src/python_testing/TC_VALCC_4_4.py +++ b/src/python_testing/TC_VALCC_4_4.py @@ -29,7 +29,8 @@ import chip.clusters as Clusters from chip.clusters.Types import NullValue from chip.interaction_model import InteractionModelError, Status -from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main, utc_time_in_matter_epoch +from matter_testing_support import (MatterBaseTest, TestStep, default_matter_test_main, has_feature, run_if_endpoint_matches, + utc_time_in_matter_epoch) from mobly import asserts @@ -63,38 +64,17 @@ def steps_TC_VALCC_4_4(self) -> list[TestStep]: ] return steps - def pics_TC_VALCC_4_4(self) -> list[str]: - pics = [ - "VALCC.S", - ] - return pics - - @async_test_body + @run_if_endpoint_matches(has_feature(Clusters.ValveConfigurationAndControl, Clusters.ValveConfigurationAndControl.Bitmaps.Feature.kTimeSync)) async def test_TC_VALCC_4_4(self): - endpoint = self.user_params.get("endpoint", 1) + endpoint = self.matter_test_config.endpoint self.step(1) attributes = Clusters.ValveConfigurationAndControl.Attributes self.step("2a") - feature_map = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.FeatureMap) - - is_ts_feature_supported = feature_map & Clusters.ValveConfigurationAndControl.Bitmaps.Feature.kTimeSync - self.step("2b") - if not is_ts_feature_supported: - logging.info("TimeSync feature not supported skipping test case") - - # Skipping all remainig steps - for step in self.get_test_steps(self.current_test_info.name)[self.current_step_index:]: - self.step(step.test_plan_number) - logging.info("Test step skipped") - - return - - else: - logging.info("Test step skipped") + # Test steps 2a and 2b are handled by the decorator self.step("3a") utcTime = await self.read_single_attribute_check_success(endpoint=0, cluster=Clusters.Objects.TimeSynchronization, attribute=Clusters.TimeSynchronization.Attributes.UTCTime) @@ -110,7 +90,7 @@ async def test_TC_VALCC_4_4(self): pass else: - logging.info("Test step skipped") + self.mark_current_step_skipped() self.step(4) try: @@ -154,7 +134,7 @@ async def test_TC_VALCC_4_4(self): result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, attributes.DefaultOpenDuration(defaultOpenDuration))]) asserts.assert_equal(result[0].Status, Status.Success, "DefaultOpenDuration write failed") else: - logging.info("Test step skipped") + self.mark_current_step_skipped() self.step(10) try: diff --git a/src/python_testing/TC_VALCC_4_5.py b/src/python_testing/TC_VALCC_4_5.py index b07f4281d1c7c6..e94cd19c8fd217 100644 --- a/src/python_testing/TC_VALCC_4_5.py +++ b/src/python_testing/TC_VALCC_4_5.py @@ -29,7 +29,7 @@ import chip.clusters as Clusters from chip.clusters.Types import NullValue from chip.interaction_model import InteractionModelError, Status -from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from matter_testing_support import MatterBaseTest, TestStep, default_matter_test_main, has_cluster, run_if_endpoint_matches from mobly import asserts @@ -55,16 +55,10 @@ def steps_TC_VALCC_4_5(self) -> list[TestStep]: ] return steps - def pics_TC_VALCC_4_5(self) -> list[str]: - pics = [ - "VALCC.S", - ] - return pics - - @async_test_body + @run_if_endpoint_matches(has_cluster(Clusters.ValveConfigurationAndControl)) async def test_TC_VALCC_4_5(self): - endpoint = self.user_params.get("endpoint", 1) + endpoint = self.matter_test_config.endpoint self.step(1) attributes = Clusters.ValveConfigurationAndControl.Attributes From 3e30568638ed54db071c2f997173d1d85721f056 Mon Sep 17 00:00:00 2001 From: Cecille Freeman Date: Mon, 30 Sep 2024 11:45:55 -0700 Subject: [PATCH 19/31] controller start: not done or tested --- examples/valve-app/controller/__init__.py | 71 +++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 examples/valve-app/controller/__init__.py diff --git a/examples/valve-app/controller/__init__.py b/examples/valve-app/controller/__init__.py new file mode 100644 index 00000000000000..1d5a9a756de9ab --- /dev/null +++ b/examples/valve-app/controller/__init__.py @@ -0,0 +1,71 @@ +from enum import StrEnum +from chip import ChipDeviceCtrl +import chip.clusters as Clusters + + +class Bottle(StrEnum): + kBourbon = "bourbon" + kGin = "gin" + kCampari = "campari" + kVermouth = "vermouth" + kSourMix = "sour mix" + kSimpleSyrup = "simple syrup", + +# One once == approx 12s. + + +class Oz: + def __init__(self, oz: float): + self.oz = oz + + def time(self): + return self.oz * 12 + + +class DrinkMachine: + def __init__(self, devCtrl: ChipDeviceCtrl, node_id: int): + self.dev_ctrl = devCtrl + self.node_id = node_id + # TODO: Should this actually be modelled as an aggregator with bridged nodes so I can have a nodelabel? + # Right now I'm going to leave this as something on the app side because it's a demo, but it's weird that we don't have writeable labels unless you model it strangely. Spec issue? + self.bottles: dict[Bottle, int] = {Bottle.kBourbon: 1, Bottle.kGin: 2, + Bottle.kCampari: 3, Bottle.kVermouth: 4, Bottle.kSourMix: 5, Bottle.kSimpleSyrup: 6} + self.recipes: dict[str, dict[Bottle, Oz]] = {} + self.add_recipe("negroni", {Bottle.kGin: Oz(1.5), Bottle.kCampari: Oz(1.5), Bottle.kVermouth: Oz(1.5)}) + self.add_recipe("boulevardier", {Bottle.kBourbon: Oz(1.5), Bottle.kCampari: Oz(1.5), Bottle.kVermouth: Oz(1.5)}) + self.add_recipe("bourbon sour", {Bottle.kBourbon: Oz(2), Bottle.kSourMix: Oz(1), Bottle.kSimpleSyrup: Oz(1.5)}) + self.add_recipe("martini", {Bottle.kGin: Oz(2), Bottle.kVermouth: Oz(0.25)}) + self.add_recipe("gimlet", {Bottle.kGin: Oz(2.5), Bottle.kSourMix: Oz(0.5), Bottle.kSimpleSyrup: Oz(0.5)}) + self.add_recipe("old fashioned", {Bottle.kBourbon: Oz(2), Bottle.kSimpleSyrup: Oz(0.125)}) + + def set_bottle_names(self, bottles: dict[Bottle, int]) -> bool: + ''' Bottle is a dict of bottle name to endpoint and should contain all 6 endpoints at once''' + if len(bottles) != 6: + return False + self.bottles = bottles + + def get_bottle_names(self): + return self.Bottle + + def add_recipe(self, name: str, ingredients: dict[Bottle, Oz]): + # TODO: should store somewhere permanent - simplest is to write out to file. In the meanwhile, we have a few pre-populated + self.recipes[name] = ingredients + + async def dispense(self, recipe: str): + # TODO: be a bit nicer on the comparison here. Strings as keys aren't great, but I want the flexibility to add non-standard recipes + if recipe not in self.recipes.keys(): + print(f"Unable to find the specified recipe. Available Recipes: {self.recipes.keys()}") + return + required_bottles = set(self.recipes[recipe].keys()) + if not required_bottles.issubset(set(self.bottles)): + print('Recipe requires an ingredient that is not loaded into the drink machine') + print(f'Recipe requires: {required_bottles}') + print(f'Availble: {self.bottles}') + return + + ingredients = self.recipes[recipe] + for bottle, amount in ingredients.items(): + ep = self.bottles[bottle] + time = amount.time() + await self.dev_ctrl.SendCommand(nodeid=self.node_id, endpoint=ep, + payload=Clusters.ValveConfigurationAndControl.Commands.Open(openDuration=time)) From b99441764b3be364333a68dfa99c537cad4f0579 Mon Sep 17 00:00:00 2001 From: cecille Date: Mon, 30 Sep 2024 17:07:45 -0400 Subject: [PATCH 20/31] controller now tested --- .../{valve-app => valve}/controller/__init__.py | 14 +++++++++----- examples/{valve-app => valve}/linux/.gn | 0 examples/{valve-app => valve}/linux/BUILD.gn | 2 +- examples/{valve-app => valve}/linux/args.gni | 0 .../{valve-app => valve}/linux/build_overrides | 0 .../linux/include/CHIPProjectAppConfig.h | 0 examples/{valve-app => valve}/linux/main.cpp | 4 ++-- .../linux/third_party/connectedhomeip | 0 .../{valve-app => valve}/valve-common/BUILD.gn | 0 .../valve-common/valve-app.matter | 0 .../valve-common/valve-app.zap | 0 11 files changed, 12 insertions(+), 8 deletions(-) rename examples/{valve-app => valve}/controller/__init__.py (90%) rename examples/{valve-app => valve}/linux/.gn (100%) rename examples/{valve-app => valve}/linux/BUILD.gn (96%) rename examples/{valve-app => valve}/linux/args.gni (100%) rename examples/{valve-app => valve}/linux/build_overrides (100%) rename examples/{valve-app => valve}/linux/include/CHIPProjectAppConfig.h (100%) rename examples/{valve-app => valve}/linux/main.cpp (97%) rename examples/{valve-app => valve}/linux/third_party/connectedhomeip (100%) rename examples/{valve-app => valve}/valve-common/BUILD.gn (100%) rename examples/{valve-app => valve}/valve-common/valve-app.matter (100%) rename examples/{valve-app => valve}/valve-common/valve-app.zap (100%) diff --git a/examples/valve-app/controller/__init__.py b/examples/valve/controller/__init__.py similarity index 90% rename from examples/valve-app/controller/__init__.py rename to examples/valve/controller/__init__.py index 1d5a9a756de9ab..7d76b665aa0e35 100644 --- a/examples/valve-app/controller/__init__.py +++ b/examples/valve/controller/__init__.py @@ -11,14 +11,13 @@ class Bottle(StrEnum): kSourMix = "sour mix" kSimpleSyrup = "simple syrup", -# One once == approx 12s. - class Oz: def __init__(self, oz: float): self.oz = oz def time(self): + # One oz == approx 12s. return self.oz * 12 @@ -26,7 +25,7 @@ class DrinkMachine: def __init__(self, devCtrl: ChipDeviceCtrl, node_id: int): self.dev_ctrl = devCtrl self.node_id = node_id - # TODO: Should this actually be modelled as an aggregator with bridged nodes so I can have a nodelabel? + # TODO: Should this actually be modelled as an aggregator with bridged nodes so I can have a NodeLabel? # Right now I'm going to leave this as something on the app side because it's a demo, but it's weird that we don't have writeable labels unless you model it strangely. Spec issue? self.bottles: dict[Bottle, int] = {Bottle.kBourbon: 1, Bottle.kGin: 2, Bottle.kCampari: 3, Bottle.kVermouth: 4, Bottle.kSourMix: 5, Bottle.kSimpleSyrup: 6} @@ -37,6 +36,8 @@ def __init__(self, devCtrl: ChipDeviceCtrl, node_id: int): self.add_recipe("martini", {Bottle.kGin: Oz(2), Bottle.kVermouth: Oz(0.25)}) self.add_recipe("gimlet", {Bottle.kGin: Oz(2.5), Bottle.kSourMix: Oz(0.5), Bottle.kSimpleSyrup: Oz(0.5)}) self.add_recipe("old fashioned", {Bottle.kBourbon: Oz(2), Bottle.kSimpleSyrup: Oz(0.125)}) + self.add_recipe("shot of bourbon", {Bottle.kBourbon: Oz(1.5)}) + self.add_recipe("shot of gin", {Bottle.kGin: Oz(1.5)}) def set_bottle_names(self, bottles: dict[Bottle, int]) -> bool: ''' Bottle is a dict of bottle name to endpoint and should contain all 6 endpoints at once''' @@ -45,7 +46,10 @@ def set_bottle_names(self, bottles: dict[Bottle, int]) -> bool: self.bottles = bottles def get_bottle_names(self): - return self.Bottle + return self.bottles + + def get_recipes(self): + return self.recipes def add_recipe(self, name: str, ingredients: dict[Bottle, Oz]): # TODO: should store somewhere permanent - simplest is to write out to file. In the meanwhile, we have a few pre-populated @@ -60,7 +64,7 @@ async def dispense(self, recipe: str): if not required_bottles.issubset(set(self.bottles)): print('Recipe requires an ingredient that is not loaded into the drink machine') print(f'Recipe requires: {required_bottles}') - print(f'Availble: {self.bottles}') + print(f'Available: {self.bottles}') return ingredients = self.recipes[recipe] diff --git a/examples/valve-app/linux/.gn b/examples/valve/linux/.gn similarity index 100% rename from examples/valve-app/linux/.gn rename to examples/valve/linux/.gn diff --git a/examples/valve-app/linux/BUILD.gn b/examples/valve/linux/BUILD.gn similarity index 96% rename from examples/valve-app/linux/BUILD.gn rename to examples/valve/linux/BUILD.gn index 86ea7a3900e210..d4a43214538098 100644 --- a/examples/valve-app/linux/BUILD.gn +++ b/examples/valve/linux/BUILD.gn @@ -36,7 +36,7 @@ executable("valve-app") { deps = [ "${chip_root}/examples/platform/linux:app-main", - "${chip_root}/examples/valve-app/valve-common", + "${chip_root}/examples/valve/valve-common", "${chip_root}/src/lib", ] diff --git a/examples/valve-app/linux/args.gni b/examples/valve/linux/args.gni similarity index 100% rename from examples/valve-app/linux/args.gni rename to examples/valve/linux/args.gni diff --git a/examples/valve-app/linux/build_overrides b/examples/valve/linux/build_overrides similarity index 100% rename from examples/valve-app/linux/build_overrides rename to examples/valve/linux/build_overrides diff --git a/examples/valve-app/linux/include/CHIPProjectAppConfig.h b/examples/valve/linux/include/CHIPProjectAppConfig.h similarity index 100% rename from examples/valve-app/linux/include/CHIPProjectAppConfig.h rename to examples/valve/linux/include/CHIPProjectAppConfig.h diff --git a/examples/valve-app/linux/main.cpp b/examples/valve/linux/main.cpp similarity index 97% rename from examples/valve-app/linux/main.cpp rename to examples/valve/linux/main.cpp index d86f65ca1d5e43..83df6e1d7d746e 100644 --- a/examples/valve-app/linux/main.cpp +++ b/examples/valve/linux/main.cpp @@ -35,7 +35,7 @@ class PrintOnlyDelegate : public NonLevelControlDelegate PrintOnlyDelegate(EndpointId endpoint) : mEndpoint(endpoint) {} CHIP_ERROR HandleOpenValve(ValveStateEnum & currentState, BitMask & valveFault) override { - ChipLogProgress(NotSpecified, "VALVE IS OPENING on endpoint %u!!!!!", mEndpoint); + ChipLogError(NotSpecified, "\n\nVALVE IS OPENING on endpoint %u!!!!!\n\n", mEndpoint); state = ValveStateEnum::kOpen; currentState = state; return CHIP_NO_ERROR; @@ -43,7 +43,7 @@ class PrintOnlyDelegate : public NonLevelControlDelegate ValveStateEnum GetCurrentValveState() override { return state; } CHIP_ERROR HandleCloseValve(ValveStateEnum & currentState, BitMask & valveFault) override { - ChipLogProgress(NotSpecified, "VALVE IS CLOSING on endpoint %u!!!!!", mEndpoint); + ChipLogError(NotSpecified, "\n\nVALVE IS CLOSING on endpoint %u!!!!!\n\n", mEndpoint); state = ValveStateEnum::kClosed; currentState = state; return CHIP_NO_ERROR; diff --git a/examples/valve-app/linux/third_party/connectedhomeip b/examples/valve/linux/third_party/connectedhomeip similarity index 100% rename from examples/valve-app/linux/third_party/connectedhomeip rename to examples/valve/linux/third_party/connectedhomeip diff --git a/examples/valve-app/valve-common/BUILD.gn b/examples/valve/valve-common/BUILD.gn similarity index 100% rename from examples/valve-app/valve-common/BUILD.gn rename to examples/valve/valve-common/BUILD.gn diff --git a/examples/valve-app/valve-common/valve-app.matter b/examples/valve/valve-common/valve-app.matter similarity index 100% rename from examples/valve-app/valve-common/valve-app.matter rename to examples/valve/valve-common/valve-app.matter diff --git a/examples/valve-app/valve-common/valve-app.zap b/examples/valve/valve-common/valve-app.zap similarity index 100% rename from examples/valve-app/valve-common/valve-app.zap rename to examples/valve/valve-common/valve-app.zap From f01c8f56e42d207ea32d71d9ce12730684f0834d Mon Sep 17 00:00:00 2001 From: cecille Date: Tue, 1 Oct 2024 13:00:01 -0400 Subject: [PATCH 21/31] Add readme and additional functions for controller --- examples/valve/controller/README.md | 73 +++++++++++++++++++++++++++ examples/valve/controller/__init__.py | 9 ++++ 2 files changed, 82 insertions(+) create mode 100644 examples/valve/controller/README.md diff --git a/examples/valve/controller/README.md b/examples/valve/controller/README.md new file mode 100644 index 00000000000000..3e7fe774da2cdb --- /dev/null +++ b/examples/valve/controller/README.md @@ -0,0 +1,73 @@ +# Running the drinks machine app and controller + +To run this example matter application, you need a version of the example app +and a controller. The example app can be compiled with gn / ninja, the +controller functions can be used through the chip-repl python shell + +## App + +### Compiling the app on linux + +From the root of the chip tree + +``` +cd examples/valve/linux +gn gen out/debug +ninja -C out/debug +``` + +### Running the app on linux + +From the root of the chip tree + +``` +./examples/valve/linux/out/debug/valve-app --KVS /tmp/valve_kvs.json +``` + +The KVS file is where the commissioning data is stored for the app, so be sure +to start the app with the same kvs file if you do not want to re-commission it +every time. All the standard linux app flags also work for this app. Use --help +to see the available options. + +## Controller + +### Compiling the chip-repl + +To compile the chip-repl, from the root of the chip tree: + +``` +. scripts/activate.sh +./scripts/build_python.sh -i out/pyenv +source out/pyenv/activate +out/pyenv/chip-repl -- +``` + +The chip-repl is a shell that lets you directly call python functions. It +instantiates a controller object that can be used to communicate with devices. +The controller is called devCtrl. By default, its KVS is at +/tmp/repl-storage.json, but this can be changed on the command line if desired. +Use --help to see options. + +### Commissioning the valve app + +As long as the controller and the application KVSs are kept constant, the app +should only need to be commissioned once. + +To commission the device use: + +``` +from chip import ChipDeviceCtrl +await devCtrl.CommissionOnNetwork(nodeId=1, setupPinCode=20202021, filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=3840) +``` + +### Interacting with teh valve app + +To create a drinks machine controller: + +``` +import examples.valve.controller as DrinksController +dm = DrinksController.DrinkMachine(devCtrl, 1) +``` + +You can now call functions on the drinks machine controller. Tab completion will +work within the repl, so you can see the available options. diff --git a/examples/valve/controller/__init__.py b/examples/valve/controller/__init__.py index 7d76b665aa0e35..312465a9bc4757 100644 --- a/examples/valve/controller/__init__.py +++ b/examples/valve/controller/__init__.py @@ -1,5 +1,6 @@ from enum import StrEnum from chip import ChipDeviceCtrl +from chip.clusters.Types import NullValue import chip.clusters as Clusters @@ -73,3 +74,11 @@ async def dispense(self, recipe: str): time = amount.time() await self.dev_ctrl.SendCommand(nodeid=self.node_id, endpoint=ep, payload=Clusters.ValveConfigurationAndControl.Commands.Open(openDuration=time)) + + async def prime(self, endpoint: int): + await self.dev_ctrl.SendCommand(nodeid=self.node_id, endpoint=endpoint, + payload=Clusters.ValveConfigurationAndControl.Commands.Open(openDuration=NullValue)) + + async def stop(self, endpoint: int): + await self.dev_ctrl.SendCommand(nodeid=self.node_id, endpoint=endpoint, + payload=Clusters.ValveConfigurationAndControl.Commands.Close()) From 84a08998841d409b9a88de2fa04c8c47d6502d40 Mon Sep 17 00:00:00 2001 From: cecille Date: Fri, 4 Oct 2024 09:59:30 -0400 Subject: [PATCH 22/31] valve: Set target state to transitioning first --- ...onfiguration-and-control-cluster-logic.cpp | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp index 94111d83d7cd18..8bad33e492fbd5 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp @@ -370,6 +370,12 @@ CHIP_ERROR ClusterLogic::HandleOpenLevel(const std::optional & targetLe { return err; } + + mState.SetTargetLevel(realTargetLevel); + mState.SetCurrentLevel(returnedCurrentLevel); + mState.SetTargetState(DataModel::Nullable(ValveStateEnum::kOpen)); + mState.SetCurrentState(DataModel::Nullable(ValveStateEnum::kTransitioning)); + if (returnedCurrentLevel == realTargetLevel) { mState.SetTargetLevel(DataModel::NullNullable); @@ -379,10 +385,6 @@ CHIP_ERROR ClusterLogic::HandleOpenLevel(const std::optional & targetLe } else { - mState.SetTargetLevel(realTargetLevel); - mState.SetCurrentLevel(returnedCurrentLevel); - mState.SetTargetState(DataModel::Nullable(ValveStateEnum::kOpen)); - mState.SetCurrentState(DataModel::Nullable(ValveStateEnum::kTransitioning)); // TODO: Need to start a timer to continue querying the device for updates. Or just let the delegate handle this? } return CHIP_NO_ERROR; @@ -396,6 +398,10 @@ CHIP_ERROR ClusterLogic::HandleOpenNoLevel() ValveStateEnum returnedState = ValveStateEnum::kUnknownEnumValue; BitMask returnedValveFault = 0; + // Per the spec, set these to transitioning regardless + mState.SetTargetState(DataModel::Nullable(ValveStateEnum::kOpen)); + mState.SetCurrentState(DataModel::Nullable(ValveStateEnum::kTransitioning)); + CHIP_ERROR err = mClusterDriver.HandleOpenValve(returnedState, returnedValveFault); if (mConformance.supportsValveFault) { @@ -403,17 +409,17 @@ CHIP_ERROR ClusterLogic::HandleOpenNoLevel() } if (err != CHIP_NO_ERROR) { + // TODO: How should the target and current be set in this case? return err; } + if (returnedState == ValveStateEnum::kOpen) { - mState.SetTargetLevel(DataModel::NullNullable); + mState.SetTargetState(DataModel::NullNullable); mState.SetCurrentState(DataModel::Nullable(ValveStateEnum::kOpen)); } else { - mState.SetTargetState(DataModel::Nullable(ValveStateEnum::kOpen)); - mState.SetCurrentState(DataModel::Nullable(ValveStateEnum::kTransitioning)); // TODO: Need to start a timer to continue querying the device for updates. Or just let the delegate handle this? } return CHIP_NO_ERROR; @@ -480,6 +486,9 @@ CHIP_ERROR ClusterLogic::HandleCloseInternal() if (mConformance.HasFeature(Feature::kLevel)) { Percent currentLevel; + mState.SetTargetLevel(0); + mState.SetTargetState(DataModel::Nullable(ValveStateEnum::kClosed)); + mState.SetCurrentState(DataModel::Nullable(ValveStateEnum::kTransitioning)); err = mClusterDriver.HandleCloseValve(currentLevel, faults); if (err == CHIP_NO_ERROR) { @@ -487,16 +496,20 @@ CHIP_ERROR ClusterLogic::HandleCloseInternal() if (currentLevel == 0) { mState.SetCurrentState(DataModel::Nullable(ValveStateEnum::kClosed)); + mState.SetTargetState(DataModel::NullNullable); + mState.SetTargetLevel(DataModel::NullNullable); } else { - mState.SetCurrentState(DataModel::Nullable(ValveStateEnum::kTransitioning)); + // TODO: start a timer here to query the delegate? } } } else { ValveStateEnum state; + mState.SetTargetState(DataModel::Nullable(ValveStateEnum::kClosed)); + mState.SetCurrentState(DataModel::Nullable(ValveStateEnum::kTransitioning)); err = mClusterDriver.HandleCloseValve(state, faults); if (err == CHIP_NO_ERROR) { From bdadfb0c4ede09bdd247bcf9d36fe11593da8cec Mon Sep 17 00:00:00 2001 From: cecille Date: Fri, 4 Oct 2024 09:59:50 -0400 Subject: [PATCH 23/31] fixup for 3.1 test --- src/python_testing/TC_VALCC_3_1.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/python_testing/TC_VALCC_3_1.py b/src/python_testing/TC_VALCC_3_1.py index 0dea5231a9c787..3c9c40b9dd146d 100644 --- a/src/python_testing/TC_VALCC_3_1.py +++ b/src/python_testing/TC_VALCC_3_1.py @@ -29,7 +29,7 @@ import chip.clusters as Clusters from chip.clusters.Types import NullValue from chip.interaction_model import InteractionModelError, Status -from matter_testing_support import MatterBaseTest, TestStep, default_matter_test_main, has_cluster, run_if_endpoint_matches +from matter_testing_support import AttributeValue, ClusterAttributeChangeAccumulator, MatterBaseTest, TestStep, default_matter_test_main, has_cluster, run_if_endpoint_matches from mobly import asserts @@ -44,13 +44,14 @@ def desc_TC_VALCC_3_1(self) -> str: def steps_TC_VALCC_3_1(self) -> list[TestStep]: steps = [ TestStep(1, "Commissioning, already done", is_commissioning=True), - TestStep(2, "Send Open command"), - TestStep(3, "Read CurrentState and TargetState attribute until CurrentState is Open", - "Target state is Open while CurrentState is Transitioning"), + TestStep(2, "Set up a subscription to all attributes on the DUT"), + TestStep(2, "Send Open command", "DUT returns SUCCESS"), + TestStep(3, "Wait until TH receives and data report for TargetState set to NULL and an attribute report for CurrentState set to Open (ordering does not matter)", + "Expected attribute reports are received"), TestStep(4, "Read CurrentState and TargetState attribute", "CurrentState is Open, TargetState is NULL"), - TestStep(5, "Send Close command"), - TestStep(6, "Read CurrentState and TargetState attribute until CurrentState is Open", - "Target state is Closed while CurrentState is Transitioning"), + TestStep(5, "Send Close command", "DUT returns SUCCESS"), + TestStep(3, "Wait until TH receives and data report for TargetState set to NULL and an attribute report for CurrentState set to Closed (ordering does not matter)", + "Expected attribute reports are received"), TestStep(7, "Read CurrentState and TargetState attribute", "CurrentState is Closed, TargetState is NULL"), ] return steps @@ -63,6 +64,8 @@ async def test_TC_VALCC_3_1(self): self.step(1) cluster = Clusters.ValveConfigurationAndControl attributes = cluster.Attributes + attribute_subscription = ClusterAttributeChangeAccumulator(cluster) + await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) self.step(2) try: @@ -73,6 +76,13 @@ async def test_TC_VALCC_3_1(self): self.step(3) + # Wait until the current state is open and the target state is Null. + # Wait for the entire duration of the test because this valve may be slow. The test will time out before this does. That's fine. + timeout = self.matter_test_config.timeout if self.matter_test_config.timeout is not None else self.default_timeout + expected_final_state = [AttributeValue(endpoint_id=endpoint, attribute=attributes.TargetState, value=NullValue), AttributeValue( + endpoint_id=endpoint, attribute=attributes.CurrentState, value=cluster.Enums.ValveStateEnum.kOpen)] + attribute_subscription.await_all_final_values_reported(expected_final_values=expected_final_state, timeout_sec=timeout) + # Read target state first in case the current state goes to open between these calls. # The test re-reads target state after CurrentState goes to open. target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) From 847a8d78f8dee3a8048a58c4d7a254514a53298b Mon Sep 17 00:00:00 2001 From: Cecille Freeman Date: Mon, 7 Oct 2024 10:02:50 -0400 Subject: [PATCH 24/31] another fixup for 3.1 --- src/python_testing/TC_VALCC_3_1.py | 76 ++++++++++-------------------- 1 file changed, 24 insertions(+), 52 deletions(-) diff --git a/src/python_testing/TC_VALCC_3_1.py b/src/python_testing/TC_VALCC_3_1.py index 3c9c40b9dd146d..659a92f92e6107 100644 --- a/src/python_testing/TC_VALCC_3_1.py +++ b/src/python_testing/TC_VALCC_3_1.py @@ -45,14 +45,14 @@ def steps_TC_VALCC_3_1(self) -> list[TestStep]: steps = [ TestStep(1, "Commissioning, already done", is_commissioning=True), TestStep(2, "Set up a subscription to all attributes on the DUT"), - TestStep(2, "Send Open command", "DUT returns SUCCESS"), - TestStep(3, "Wait until TH receives and data report for TargetState set to NULL and an attribute report for CurrentState set to Open (ordering does not matter)", + TestStep(3, "Send Open command", "DUT returns SUCCESS"), + TestStep(4, "Wait until TH receives and data report for TargetState set to NULL and an attribute report for CurrentState set to Open (ordering does not matter)", "Expected attribute reports are received"), - TestStep(4, "Read CurrentState and TargetState attribute", "CurrentState is Open, TargetState is NULL"), - TestStep(5, "Send Close command", "DUT returns SUCCESS"), - TestStep(3, "Wait until TH receives and data report for TargetState set to NULL and an attribute report for CurrentState set to Closed (ordering does not matter)", + TestStep(5, "Read CurrentState and TargetState attribute", "CurrentState is Open, TargetState is NULL"), + TestStep(6, "Send Close command", "DUT returns SUCCESS"), + TestStep(7, "Wait until TH receives and data report for TargetState set to NULL and an attribute report for CurrentState set to Closed (ordering does not matter)", "Expected attribute reports are received"), - TestStep(7, "Read CurrentState and TargetState attribute", "CurrentState is Closed, TargetState is NULL"), + TestStep(8, "Read CurrentState and TargetState attribute", "CurrentState is Closed, TargetState is NULL"), ] return steps @@ -61,21 +61,18 @@ async def test_TC_VALCC_3_1(self): endpoint = self.matter_test_config.endpoint - self.step(1) + self.step(1) # commissioning - already done + + self.step(2) cluster = Clusters.ValveConfigurationAndControl attributes = cluster.Attributes attribute_subscription = ClusterAttributeChangeAccumulator(cluster) await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) - self.step(2) - try: - await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Open(), endpoint=endpoint) - except InteractionModelError as e: - asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") - pass - self.step(3) + await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Open(), endpoint=endpoint) + self.step(4) # Wait until the current state is open and the target state is Null. # Wait for the entire duration of the test because this valve may be slow. The test will time out before this does. That's fine. timeout = self.matter_test_config.timeout if self.matter_test_config.timeout is not None else self.default_timeout @@ -83,51 +80,26 @@ async def test_TC_VALCC_3_1(self): endpoint_id=endpoint, attribute=attributes.CurrentState, value=cluster.Enums.ValveStateEnum.kOpen)] attribute_subscription.await_all_final_values_reported(expected_final_values=expected_final_state, timeout_sec=timeout) - # Read target state first in case the current state goes to open between these calls. - # The test re-reads target state after CurrentState goes to open. - target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) - current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) - - while current_state_dut is cluster.Enums.ValveStateEnum.kTransitioning: - asserts.assert_equal(target_state_dut, cluster.Enums.ValveStateEnum.kOpen, "TargetState is incorrect") - time.sleep(1) - - target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) - current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) - asserts.assert_true(current_state_dut is not NullValue, "CurrentState is null") - asserts.assert_true(target_state_dut is not NullValue, "TargetState is null") - - self.step(4) - current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) - asserts.assert_equal(current_state_dut, cluster.Enums.ValveStateEnum.kOpen, "CurrentState is incorrect") - target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) - asserts.assert_true(target_state_dut is NullValue, "TargetState is not null") - self.step(5) - try: - await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Close(), endpoint=endpoint) - except InteractionModelError as e: - asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") - pass - - self.step(6) target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) + asserts.assert_equal(current_state_dut, cluster.Enums.ValveStateEnum.kOpen, "CurrentState is not open") + asserts.assert_equal(target_state_dut, NullValue, "TargetState is not null") - while current_state_dut is cluster.Enums.ValveStateEnum.kTransitioning: - asserts.assert_equal(target_state_dut, cluster.Enums.ValveStateEnum.kClosed, "TargetState is incorrect") - time.sleep(1) - - target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) - current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) - asserts.assert_true(current_state_dut is not NullValue, "CurrentState is null") - asserts.assert_true(target_state_dut is not NullValue, "TargetState is null") + self.step(6) + attribute_subscription.reset() + await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Close(), endpoint=endpoint) self.step(7) - current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) - asserts.assert_equal(current_state_dut, cluster.Enums.ValveStateEnum.kClosed, "CurrentState is incorrect") + expected_final_state = [AttributeValue(endpoint_id=endpoint, attribute=attributes.TargetState, value=NullValue), AttributeValue( + endpoint_id=endpoint, attribute=attributes.CurrentState, value=cluster.Enums.ValveStateEnum.kClosed)] + attribute_subscription.await_all_final_values_reported(expected_final_values=expected_final_state, timeout_sec=timeout) + + self.step(8) target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) - asserts.assert_true(target_state_dut is NullValue, "TargetState is not null") + current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) + asserts.assert_equal(current_state_dut, cluster.Enums.ValveStateEnum.kClosed, "CurrentState is not closed") + asserts.assert_equal(target_state_dut, NullValue, "TargetState is not null") if __name__ == "__main__": From 8d1491e08bb9966598086fe78e276ce427155bd0 Mon Sep 17 00:00:00 2001 From: cecille Date: Mon, 7 Oct 2024 12:41:32 -0400 Subject: [PATCH 25/31] Add close at start of test --- .../linux/ValveControlDelegate.cpp | 3 -- ...valve-configuration-and-control-server.cpp | 21 +++++++++--- src/python_testing/TC_VALCC_3_1.py | 34 ++++++++++++------- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/examples/all-clusters-app/linux/ValveControlDelegate.cpp b/examples/all-clusters-app/linux/ValveControlDelegate.cpp index f161ee65eae504..ed006eaa14baec 100644 --- a/examples/all-clusters-app/linux/ValveControlDelegate.cpp +++ b/examples/all-clusters-app/linux/ValveControlDelegate.cpp @@ -38,7 +38,6 @@ DataModel::Nullable ValveControlDelegate::HandleOpenValve(DataMod // In this demo application, the transition is considered instant, // so current level is set to the requested level and current state is set to kOpen. currentLevel = sLevel; - Attributes::CurrentState::Set(kValveEndpoint, ValveConfigurationAndControl::ValveStateEnum::kOpen); return DataModel::Nullable(currentLevel); } @@ -48,8 +47,6 @@ CHIP_ERROR ValveControlDelegate::HandleCloseValve() sLastOpenDuration = 0; sLevel = 0; ReturnErrorOnFailure(ValveConfigurationAndControl::UpdateCurrentLevel(kValveEndpoint, sLevel)); - ReturnErrorOnFailure( - ValveConfigurationAndControl::UpdateCurrentState(kValveEndpoint, ValveConfigurationAndControl::ValveStateEnum::kClosed)); ChipLogProgress(NotSpecified, "Valve closed"); return CHIP_NO_ERROR; } diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.cpp index ef5baea2de6ec7..7d6ab6a11a2541 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.cpp +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.cpp @@ -360,9 +360,8 @@ CHIP_ERROR SetValveLevel(EndpointId ep, DataModel::Nullable level, Data if (!isDelegateNull(delegate)) { DataModel::Nullable cLevel = delegate->HandleOpenValve(level); - if (HasFeature(ep, ValveConfigurationAndControl::Feature::kLevel)) - { - VerifyOrReturnError(Status::Success == CurrentLevel::Set(ep, cLevel), attribute_error); + if (!cLevel.IsNull()) { + UpdateCurrentLevel(ep, cLevel.Value()); } } // start countdown @@ -376,14 +375,26 @@ CHIP_ERROR UpdateCurrentLevel(EndpointId ep, Percent currentLevel) if (HasFeature(ep, ValveConfigurationAndControl::Feature::kLevel)) { VerifyOrReturnError(Status::Success == CurrentLevel::Set(ep, currentLevel), CHIP_IM_GLOBAL_STATUS(ConstraintError)); - return CHIP_NO_ERROR; } - return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); + DataModel::Nullable targetLevel = DataModel::NullNullable; + TargetLevel::Get(ep, targetLevel); + if (!targetLevel.IsNull() && currentLevel == targetLevel.Value()) { + targetLevel = DataModel::NullNullable; + TargetLevel::Set(ep, targetLevel); + UpdateCurrentState(ep, currentLevel == 0 ? ValveStateEnum::kClosed : ValveStateEnum::kOpen); + } + return CHIP_NO_ERROR; } CHIP_ERROR UpdateCurrentState(EndpointId ep, ValveConfigurationAndControl::ValveStateEnum currentState) { VerifyOrReturnError(Status::Success == CurrentState::Set(ep, currentState), CHIP_IM_GLOBAL_STATUS(ConstraintError)); + DataModel::Nullable targetState = DataModel::NullNullable; + TargetState::Get(ep, targetState); + if (currentState == targetState.ValueOr(ValveStateEnum::kUnknownEnumValue)) { + targetState = DataModel::NullNullable; + TargetState::Set(ep, targetState); + } emitValveStateChangedEvent(ep, currentState); return CHIP_NO_ERROR; } diff --git a/src/python_testing/TC_VALCC_3_1.py b/src/python_testing/TC_VALCC_3_1.py index 659a92f92e6107..3b6e6ae8d5ea63 100644 --- a/src/python_testing/TC_VALCC_3_1.py +++ b/src/python_testing/TC_VALCC_3_1.py @@ -45,14 +45,15 @@ def steps_TC_VALCC_3_1(self) -> list[TestStep]: steps = [ TestStep(1, "Commissioning, already done", is_commissioning=True), TestStep(2, "Set up a subscription to all attributes on the DUT"), - TestStep(3, "Send Open command", "DUT returns SUCCESS"), - TestStep(4, "Wait until TH receives and data report for TargetState set to NULL and an attribute report for CurrentState set to Open (ordering does not matter)", + TestStep(3, "Send a close command to the DUT and wait until the CurrentState is closed", "DUT returns SUCCESS"), + TestStep(4, "Send Open command", "DUT returns SUCCESS"), + TestStep(5, "Wait until TH receives and data report for TargetState set to NULL and an attribute report for CurrentState set to Open (ordering does not matter)", "Expected attribute reports are received"), - TestStep(5, "Read CurrentState and TargetState attribute", "CurrentState is Open, TargetState is NULL"), - TestStep(6, "Send Close command", "DUT returns SUCCESS"), - TestStep(7, "Wait until TH receives and data report for TargetState set to NULL and an attribute report for CurrentState set to Closed (ordering does not matter)", + TestStep(6, "Read CurrentState and TargetState attribute", "CurrentState is Open, TargetState is NULL"), + TestStep(7, "Send Close command", "DUT returns SUCCESS"), + TestStep(8, "Wait until TH receives and data report for TargetState set to NULL and an attribute report for CurrentState set to Closed (ordering does not matter)", "Expected attribute reports are received"), - TestStep(8, "Read CurrentState and TargetState attribute", "CurrentState is Closed, TargetState is NULL"), + TestStep(9, "Read CurrentState and TargetState attribute", "CurrentState is Closed, TargetState is NULL"), ] return steps @@ -70,9 +71,18 @@ async def test_TC_VALCC_3_1(self): await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) self.step(3) - await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Open(), endpoint=endpoint) + await self.send_single_cmd(cmd=cluster.Commands.Close(), endpoint=endpoint) + current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) + if current_state_dut != cluster.Enums.ValveStateEnum.kClosed: + current_state_closed = AttributeValue( + endpoint_id=endpoint, attribute=attributes.CurrentState, value=cluster.Enums.ValveStateEnum.kClosed) + attribute_subscription.await_all_final_values_reported(expected_final_values=[current_state_closed]) self.step(4) + attribute_subscription.reset() + await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Open(), endpoint=endpoint) + + self.step(5) # Wait until the current state is open and the target state is Null. # Wait for the entire duration of the test because this valve may be slow. The test will time out before this does. That's fine. timeout = self.matter_test_config.timeout if self.matter_test_config.timeout is not None else self.default_timeout @@ -80,22 +90,22 @@ async def test_TC_VALCC_3_1(self): endpoint_id=endpoint, attribute=attributes.CurrentState, value=cluster.Enums.ValveStateEnum.kOpen)] attribute_subscription.await_all_final_values_reported(expected_final_values=expected_final_state, timeout_sec=timeout) - self.step(5) + self.step(6) target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) asserts.assert_equal(current_state_dut, cluster.Enums.ValveStateEnum.kOpen, "CurrentState is not open") asserts.assert_equal(target_state_dut, NullValue, "TargetState is not null") - self.step(6) + self.step(7) attribute_subscription.reset() - await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Close(), endpoint=endpoint) + await self.send_single_cmd(cmd=cluster.Commands.Close(), endpoint=endpoint) - self.step(7) + self.step(8) expected_final_state = [AttributeValue(endpoint_id=endpoint, attribute=attributes.TargetState, value=NullValue), AttributeValue( endpoint_id=endpoint, attribute=attributes.CurrentState, value=cluster.Enums.ValveStateEnum.kClosed)] attribute_subscription.await_all_final_values_reported(expected_final_values=expected_final_state, timeout_sec=timeout) - self.step(8) + self.step(9) target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) asserts.assert_equal(current_state_dut, cluster.Enums.ValveStateEnum.kClosed, "CurrentState is not closed") From 31ed684634ce6b06cc2e0cde0a035765464dcb31 Mon Sep 17 00:00:00 2001 From: cecille Date: Wed, 9 Oct 2024 17:03:01 -0400 Subject: [PATCH 26/31] Fix tests to account for unconditional change to target and current at open --- .../TestValveConfigurationAndControl.cpp | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/app/tests/TestValveConfigurationAndControl.cpp b/src/app/tests/TestValveConfigurationAndControl.cpp index 8108403918298d..e8f5180aaf3368 100644 --- a/src/app/tests/TestValveConfigurationAndControl.cpp +++ b/src/app/tests/TestValveConfigurationAndControl.cpp @@ -1388,11 +1388,14 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestAttributeUpdates) EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); EXPECT_EQ(logic.GetRemainingDuration(valElapsedSNullable), CHIP_NO_ERROR); EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable); - // We should see the currentLevel and currentState marked as dirty, and the targets should not be + + // We should see the CurrentLevel and CurrentState marked as dirty because they are changed. + // TargetState and TargetLevel should ALSO be marked as dirty because the target state should transition through + // from target back to NULL per the spec (effect of receipt for open command). EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentLevel::Id)); EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentState::Id)); - EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetLevel::Id)); - EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetState::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetLevel::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetState::Id)); EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); @@ -1413,12 +1416,16 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestAttributeUpdates) EXPECT_EQ(valElapsedSNullable.ValueOr(0), openDuration.Value()); EXPECT_EQ(logic.GetCurrentLevel(valPercentNullable), CHIP_NO_ERROR); EXPECT_EQ(valPercentNullable.ValueOr(0), requestedLevel); - // We should see the following attributes marked as dirty: currentLevel, remainingDuration, openDuration - // The targetLevel and targetState should be null, and thus we should see no update. + // We should see the following attributes marked as dirty since they are different at the end + // of the command: currentLevel, remainingDuration, openDuration + // The TargetLevel and TargetState should be NULL at the end of the open call, but will still be + // marked dirty per the spec since they are set to the target values and and back to NULL. + // Similarly, TargetState is also set to transitioning during the call then back to open and should + // therefore be marked as dirty. EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentLevel::Id)); - EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentState::Id)); - EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetLevel::Id)); - EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetState::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentState::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetLevel::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::TargetState::Id)); EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); @@ -1509,8 +1516,10 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestAttributeUpdates) EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::make_optional(requestedLevel)), CHIP_NO_ERROR); EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); + // Current level should remain unchanged and therefore won't be marked as dirty, but the current state is unconditionally + // set to transitioning and then back to open per the spec and therefore should be marked as dirty. EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentLevel::Id)); - EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentState::Id)); + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentState::Id)); // TODO: Clarify, should we get a report if we use open to set the remaining duration down? I think so. // TODO: Add such tests here. I don't think the underlying layer handles that properly right now. @@ -1524,7 +1533,9 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestAttributeUpdates) EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::RemainingDuration::Id)); EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::OpenDuration::Id)); EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentLevel::Id)); - EXPECT_FALSE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentState::Id)); + // Current state will also be marked dirty since every open call sets the current state to transitioning + // unconditionally even if it is then set back to the original value. + EXPECT_TRUE(HasAttributeChanges(context.GetDirtyList(), Attributes::CurrentState::Id)); context.ClearDirtyList(); From 1a1d5c61e9a9fb684cbea618a9bebad330a1f6e5 Mon Sep 17 00:00:00 2001 From: Cecille Freeman Date: Tue, 15 Oct 2024 19:27:38 -0400 Subject: [PATCH 27/31] Add a mystery option to the controller --- examples/valve/controller/README.md | 4 +- examples/valve/controller/__init__.py | 62 +++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/examples/valve/controller/README.md b/examples/valve/controller/README.md index 3e7fe774da2cdb..270bf83cc04307 100644 --- a/examples/valve/controller/README.md +++ b/examples/valve/controller/README.md @@ -39,7 +39,7 @@ To compile the chip-repl, from the root of the chip tree: . scripts/activate.sh ./scripts/build_python.sh -i out/pyenv source out/pyenv/activate -out/pyenv/chip-repl -- +out/pyenv/bin/chip-repl ``` The chip-repl is a shell that lets you directly call python functions. It @@ -60,7 +60,7 @@ from chip import ChipDeviceCtrl await devCtrl.CommissionOnNetwork(nodeId=1, setupPinCode=20202021, filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=3840) ``` -### Interacting with teh valve app +### Interacting with the valve app To create a drinks machine controller: diff --git a/examples/valve/controller/__init__.py b/examples/valve/controller/__init__.py index 312465a9bc4757..2e415689beb7cd 100644 --- a/examples/valve/controller/__init__.py +++ b/examples/valve/controller/__init__.py @@ -1,9 +1,14 @@ +import random + from enum import StrEnum from chip import ChipDeviceCtrl from chip.clusters.Types import NullValue import chip.clusters as Clusters +MYSTERY_DRINK = "mystery mirrorball" + + class Bottle(StrEnum): kBourbon = "bourbon" kGin = "gin" @@ -21,6 +26,12 @@ def time(self): # One oz == approx 12s. return self.oz * 12 + def __eq__(self, other): + return self.oz == other.oz + + def __lt__(self, other): + return self.oz < other.oz + class DrinkMachine: def __init__(self, devCtrl: ChipDeviceCtrl, node_id: int): @@ -39,6 +50,11 @@ def __init__(self, devCtrl: ChipDeviceCtrl, node_id: int): self.add_recipe("old fashioned", {Bottle.kBourbon: Oz(2), Bottle.kSimpleSyrup: Oz(0.125)}) self.add_recipe("shot of bourbon", {Bottle.kBourbon: Oz(1.5)}) self.add_recipe("shot of gin", {Bottle.kGin: Oz(1.5)}) + self.add_recipe("gin sour", {Bottle.kGin: Oz(2), Bottle.kSourMix: Oz(1), Bottle.kSimpleSyrup: Oz(0.5)}) + self.add_recipe("manhattan", {Bottle.kBourbon: Oz(2), Bottle.kVermouth: Oz(1)}) + self.add_recipe("campari sour", {Bottle.kGin: Oz(1), Bottle.kCampari: Oz(1), + Bottle.kSourMix: Oz(0.75), Bottle.kSimpleSyrup: Oz(0.5)}) + self.add_recipe(MYSTERY_DRINK, {}) def set_bottle_names(self, bottles: dict[Bottle, int]) -> bool: ''' Bottle is a dict of bottle name to endpoint and should contain all 6 endpoints at once''' @@ -56,7 +72,50 @@ def add_recipe(self, name: str, ingredients: dict[Bottle, Oz]): # TODO: should store somewhere permanent - simplest is to write out to file. In the meanwhile, we have a few pre-populated self.recipes[name] = ingredients + async def _dispense_mystery(self): + num_ingredients = random.randint(1, 3) + bottles = set() + choice = random.choice(list(Bottle)) + bottles.add(choice) + while len(bottles) < num_ingredients: + # If this matches something in the bottle list already, it won't be added + choice = random.choice(list(Bottle)) + bottles.add(choice) + + # we're going to aim for a 2.5 Oz shot with min 0.5 shot sizes + goal = 2.5 + print(f"Creating your mystery beverage:") + ingredients = {} + total = 0 + for bottle in bottles: + remaining = num_ingredients - len(ingredients.keys()) + if remaining == 1: + oz = goal-total + else: + max_oz = goal - total - 0.5*(remaining-1) + # multiply by 2 because randrange likes ints + oz = random.randrange(1, max_oz*2, 1)/2 + total += oz + ingredients[bottle] = Oz(oz) + print(f"\t{oz} Oz of {bottle}") + + largest = max(ingredients, key=ingredients.get) + mystery_prefix = ['', 'giant', 'potent', 'disco-tastic', 'shiny', 'Dr.'] + mystery_suffix = ['blaster', 'concoction', 'blend', 'cocktail'] + mystery_extra_suffix = ['', 'jr', 'of wonder', 'esq'] + name = [] + name.append(random.choice(mystery_prefix)) + name.append(largest) + name.append(random.choice(mystery_suffix)) + name.append(random.choice(mystery_extra_suffix)) + print(f'The {" ".join(name).strip()}') + print('Please enjoy responsibly.') + await self._dispense_ingredients(ingredients) + async def dispense(self, recipe: str): + if recipe == MYSTERY_DRINK: + return await self._dispense_mystery() + # TODO: be a bit nicer on the comparison here. Strings as keys aren't great, but I want the flexibility to add non-standard recipes if recipe not in self.recipes.keys(): print(f"Unable to find the specified recipe. Available Recipes: {self.recipes.keys()}") @@ -69,6 +128,9 @@ async def dispense(self, recipe: str): return ingredients = self.recipes[recipe] + self._dispense_ingredients(ingredients) + + async def _dispense_ingredients(self, ingredients: dict[Bottle, Oz]): for bottle, amount in ingredients.items(): ep = self.bottles[bottle] time = amount.time() From 6e354ba4630127b5026e28c21f244a6b638345e9 Mon Sep 17 00:00:00 2001 From: cecille Date: Tue, 7 Jan 2025 16:01:25 -0500 Subject: [PATCH 28/31] Fix build file from bad merge --- src/app/tests/BUILD.gn | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index d50b719e0cf5fd..0685c795bce8e9 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -163,6 +163,15 @@ source_set("valve-configuration-and-control-test-srcs") { "${chip_root}/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.cpp", "${chip_root}/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-matter-context.h", ] + public_deps = [ + "${chip_root}/src/app:interaction-model", + "${chip_root}/src/app/common:cluster-objects", + "${chip_root}/src/lib/core", + "${chip_root}/src/lib/support", + "${chip_root}/src/platform", + ] +} + source_set("ecosystem-information-test-srcs") { sources = [ "${chip_root}/src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp", @@ -172,7 +181,6 @@ source_set("ecosystem-information-test-srcs") { "${chip_root}/src/app:interaction-model", "${chip_root}/src/app/common:cluster-objects", "${chip_root}/src/lib/core", - "${chip_root}/src/platform", ] } From b95be0abab385da29a31f2e4f81581ef33b22db0 Mon Sep 17 00:00:00 2001 From: cecille Date: Thu, 19 Dec 2024 18:13:44 -0500 Subject: [PATCH 29/31] python framework: Fix warning on asyncio not awaited Testing: TC_TestArrtAvail.py --- .../chip/testing/matter_testing.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py b/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py index ec43db186eae6d..043ca8922f77c3 100644 --- a/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py +++ b/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py @@ -1125,8 +1125,6 @@ def setup_class(self): self.current_step_index = 0 self.step_start_time = datetime.now(timezone.utc) self.step_skipped = False - self.global_wildcard = asyncio.wait_for(self.default_controller.Read(self.dut_node_id, [(Clusters.Descriptor), Attribute.AttributePath(None, None, GlobalAttributeIds.ATTRIBUTE_LIST_ID), Attribute.AttributePath( - None, None, GlobalAttributeIds.FEATURE_MAP_ID), Attribute.AttributePath(None, None, GlobalAttributeIds.ACCEPTED_COMMAND_LIST_ID)]), timeout=60) # self.stored_global_wildcard stores value of self.global_wildcard after first async call. # Because setup_class can be called before commissioning, this variable is lazy-initialized # where the read is deferred until the first guard function call that requires global attributes. @@ -1462,6 +1460,13 @@ def pics_guard(self, pics_condition: bool): self.mark_current_step_skipped() return pics_condition + async def _populate_wildcard(self): + """ Populates self.stored_global_wildcard if not already filled. """ + if self.stored_global_wildcard is None: + global_wildcard = asyncio.wait_for(self.default_controller.Read(self.dut_node_id, [(Clusters.Descriptor), Attribute.AttributePath(None, None, GlobalAttributeIds.ATTRIBUTE_LIST_ID), Attribute.AttributePath( + None, None, GlobalAttributeIds.FEATURE_MAP_ID), Attribute.AttributePath(None, None, GlobalAttributeIds.ACCEPTED_COMMAND_LIST_ID)]), timeout=60) + self.stored_global_wildcard = await global_wildcard + async def attribute_guard(self, endpoint: int, attribute: ClusterObjects.ClusterAttributeDescriptor): """Similar to pics_guard above, except checks a condition and if False marks the test step as skipped and returns False using attributes against attributes_list, otherwise returns True. @@ -1475,8 +1480,7 @@ async def attribute_guard(self, endpoint: int, attribute: ClusterObjects.Cluster if self.attribute_guard(condition2_needs_to_be_false_to_skip_step): # skip step 2 if condition not met """ - if self.stored_global_wildcard is None: - self.stored_global_wildcard = await self.global_wildcard + await self._populate_wildcard() attr_condition = _has_attribute(wildcard=self.stored_global_wildcard, endpoint=endpoint, attribute=attribute) if not attr_condition: self.mark_current_step_skipped() @@ -1495,8 +1499,7 @@ async def command_guard(self, endpoint: int, command: ClusterObjects.ClusterComm if self.command_guard(condition2_needs_to_be_false_to_skip_step): # skip step 2 if condition not met """ - if self.stored_global_wildcard is None: - self.stored_global_wildcard = await self.global_wildcard + await self._populate_wildcard() cmd_condition = _has_command(wildcard=self.stored_global_wildcard, endpoint=endpoint, command=command) if not cmd_condition: self.mark_current_step_skipped() @@ -1515,8 +1518,7 @@ async def feature_guard(self, endpoint: int, cluster: ClusterObjects.ClusterObje if self.feature_guard(condition2_needs_to_be_false_to_skip_step): # skip step 2 if condition not met """ - if self.stored_global_wildcard is None: - self.stored_global_wildcard = await self.global_wildcard + await self._populate_wildcard() feat_condition = _has_feature(wildcard=self.stored_global_wildcard, endpoint=endpoint, cluster=cluster, feature=feature_int) if not feat_condition: self.mark_current_step_skipped() From 82e67dea1992a4fee8e98f6a5387c2de41fb9040 Mon Sep 17 00:00:00 2001 From: cecille Date: Tue, 7 Jan 2025 16:45:08 -0500 Subject: [PATCH 30/31] Uncomment the attribute access interface registration - whoops --- .../valve-configuration-and-control-server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.cpp index 0f29b024c3ac1b..fd89ab135d384c 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.cpp +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.cpp @@ -528,5 +528,5 @@ bool emberAfValveConfigurationAndControlClusterCloseCallback( void MatterValveConfigurationAndControlPluginServerInitCallback() { - // AttributeAccessInterfaceRegistry::Instance().Register(&gAttrAccess); + AttributeAccessInterfaceRegistry::Instance().Register(&gAttrAccess); } From e3a9b433e3140e6fcf88c1832a46179978fadfb7 Mon Sep 17 00:00:00 2001 From: cecille Date: Wed, 8 Jan 2025 09:51:10 -0500 Subject: [PATCH 31/31] fix up some tests --- src/python_testing/TC_VALCC_2_1.py | 6 ++++++ src/python_testing/TC_VALCC_3_1.py | 6 ++++++ src/python_testing/TC_VALCC_3_2.py | 6 ++++++ src/python_testing/TC_VALCC_3_3.py | 10 ++++++++-- src/python_testing/TC_VALCC_3_4.py | 8 ++++++++ src/python_testing/TC_VALCC_4_1.py | 6 ++++++ src/python_testing/TC_VALCC_4_2.py | 6 ++++++ src/python_testing/TC_VALCC_4_3.py | 6 ++++++ src/python_testing/TC_VALCC_4_4.py | 6 ++++++ src/python_testing/TC_VALCC_4_5.py | 6 ++++++ 10 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/python_testing/TC_VALCC_2_1.py b/src/python_testing/TC_VALCC_2_1.py index ac2ee1d8768e05..a5199f052ee324 100644 --- a/src/python_testing/TC_VALCC_2_1.py +++ b/src/python_testing/TC_VALCC_2_1.py @@ -66,6 +66,12 @@ def steps_TC_VALCC_2_1(self) -> list[TestStep]: ] return steps + def pics_TC_VALCC_2_1(self) -> list[str]: + pics = [ + "VALCC.S", + ] + return pics + @run_if_endpoint_matches(has_cluster(Clusters.ValveConfigurationAndControl)) async def test_TC_VALCC_2_1(self): diff --git a/src/python_testing/TC_VALCC_3_1.py b/src/python_testing/TC_VALCC_3_1.py index c7b133f94b6582..c08206acfbae8d 100644 --- a/src/python_testing/TC_VALCC_3_1.py +++ b/src/python_testing/TC_VALCC_3_1.py @@ -62,6 +62,12 @@ def steps_TC_VALCC_3_1(self) -> list[TestStep]: ] return steps + def pics_TC_VALCC_3_1(self) -> list[str]: + pics = [ + "VALCC.S", + ] + return pics + @run_if_endpoint_matches(has_cluster(Clusters.ValveConfigurationAndControl)) async def test_TC_VALCC_3_1(self): diff --git a/src/python_testing/TC_VALCC_3_2.py b/src/python_testing/TC_VALCC_3_2.py index f225e9593e4efb..d5bfb803a408cb 100644 --- a/src/python_testing/TC_VALCC_3_2.py +++ b/src/python_testing/TC_VALCC_3_2.py @@ -68,6 +68,12 @@ def steps_TC_VALCC_3_2(self) -> list[TestStep]: ] return steps + def pics_TC_VALCC_3_2(self) -> list[str]: + pics = [ + "VALCC.S", + ] + return pics + @run_if_endpoint_matches(has_feature(Clusters.ValveConfigurationAndControl, Clusters.ValveConfigurationAndControl.Bitmaps.Feature.kLevel)) async def test_TC_VALCC_3_2(self): diff --git a/src/python_testing/TC_VALCC_3_3.py b/src/python_testing/TC_VALCC_3_3.py index 1258efd4fc398b..c1b0b9d17bb300 100644 --- a/src/python_testing/TC_VALCC_3_3.py +++ b/src/python_testing/TC_VALCC_3_3.py @@ -34,7 +34,7 @@ import chip.clusters as Clusters from chip.clusters.Types import NullValue -from chip.testing.matter_support import (AttributeValue, ClusterAttributeChangeAccumulator, MatterBaseTest, TestStep, +from chip.testing.matter_testing import (AttributeValue, ClusterAttributeChangeAccumulator, MatterBaseTest, TestStep, default_matter_test_main, has_attribute, run_if_endpoint_matches) from mobly import asserts @@ -69,6 +69,12 @@ def steps_TC_VALCC_3_3(self) -> list[TestStep]: ] return steps + def pics_TC_VALCC_3_3(self) -> list[str]: + pics = [ + "VALCC.S", + ] + return pics + @run_if_endpoint_matches(has_attribute(Clusters.ValveConfigurationAndControl.Attributes.DefaultOpenLevel)) async def test_TC_VALCC_3_3(self): @@ -78,7 +84,7 @@ async def test_TC_VALCC_3_3(self): attributes = Clusters.ValveConfigurationAndControl.Attributes self.step(2) - # Done as part of the test initialization + attribute_list = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.AttributeList) self.step(3) if attributes.DefaultOpenLevel.attribute_id not in attribute_list: diff --git a/src/python_testing/TC_VALCC_3_4.py b/src/python_testing/TC_VALCC_3_4.py index 5a8bb93ddbbc0a..e59a160ca5fc9e 100644 --- a/src/python_testing/TC_VALCC_3_4.py +++ b/src/python_testing/TC_VALCC_3_4.py @@ -59,6 +59,12 @@ def steps_TC_VALCC_3_4(self) -> list[TestStep]: ] return steps + def pics_TC_VALCC_3_4(self) -> list[str]: + pics = [ + "VALCC.S", + ] + return pics + @run_if_endpoint_matches(has_attribute(Clusters.ValveConfigurationAndControl.Attributes.LevelStep)) async def test_TC_VALCC_3_4(self): @@ -68,6 +74,8 @@ async def test_TC_VALCC_3_4(self): attributes = Clusters.ValveConfigurationAndControl.Attributes self.step(2) + attribute_list = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.AttributeList) + self.step(3) # Steps 2 and three are handled by the decorator diff --git a/src/python_testing/TC_VALCC_4_1.py b/src/python_testing/TC_VALCC_4_1.py index 83adb66878cfd1..a1179afe06d61c 100644 --- a/src/python_testing/TC_VALCC_4_1.py +++ b/src/python_testing/TC_VALCC_4_1.py @@ -63,6 +63,12 @@ def steps_TC_VALCC_4_1(self) -> list[TestStep]: ] return steps + def pics_TC_VALCC_4_1(self) -> list[str]: + pics = [ + "VALCC.S", + ] + return pics + @run_if_endpoint_matches(has_cluster(Clusters.ValveConfigurationAndControl)) async def test_TC_VALCC_4_1(self): diff --git a/src/python_testing/TC_VALCC_4_2.py b/src/python_testing/TC_VALCC_4_2.py index e5266502add7e4..35d75ef2d95da0 100644 --- a/src/python_testing/TC_VALCC_4_2.py +++ b/src/python_testing/TC_VALCC_4_2.py @@ -66,6 +66,12 @@ def steps_TC_VALCC_4_2(self) -> list[TestStep]: ] return steps + def pics_TC_VALCC_4_2(self) -> list[str]: + pics = [ + "VALCC.S", + ] + return pics + @run_if_endpoint_matches(has_cluster(Clusters.ValveConfigurationAndControl)) async def test_TC_VALCC_4_2(self): diff --git a/src/python_testing/TC_VALCC_4_3.py b/src/python_testing/TC_VALCC_4_3.py index bc92f163302398..ee19f48c2f3976 100644 --- a/src/python_testing/TC_VALCC_4_3.py +++ b/src/python_testing/TC_VALCC_4_3.py @@ -63,6 +63,12 @@ def steps_TC_VALCC_4_3(self) -> list[TestStep]: ] return steps + def pics_TC_VALCC_4_3(self) -> list[str]: + pics = [ + "VALCC.S", + ] + return pics + @run_if_endpoint_matches(has_feature(Clusters.ValveConfigurationAndControl, Clusters.ValveConfigurationAndControl.Bitmaps.Feature.kTimeSync)) async def test_TC_VALCC_4_3(self): diff --git a/src/python_testing/TC_VALCC_4_4.py b/src/python_testing/TC_VALCC_4_4.py index a247b420d16931..17189c351e1b4a 100644 --- a/src/python_testing/TC_VALCC_4_4.py +++ b/src/python_testing/TC_VALCC_4_4.py @@ -72,6 +72,12 @@ def steps_TC_VALCC_4_4(self) -> list[TestStep]: ] return steps + def pics_TC_VALCC_4_4(self) -> list[str]: + pics = [ + "VALCC.S", + ] + return pics + @run_if_endpoint_matches(has_feature(Clusters.ValveConfigurationAndControl, Clusters.ValveConfigurationAndControl.Bitmaps.Feature.kTimeSync)) async def test_TC_VALCC_4_4(self): diff --git a/src/python_testing/TC_VALCC_4_5.py b/src/python_testing/TC_VALCC_4_5.py index e358fa6a747a63..12743338cdf144 100644 --- a/src/python_testing/TC_VALCC_4_5.py +++ b/src/python_testing/TC_VALCC_4_5.py @@ -63,6 +63,12 @@ def steps_TC_VALCC_4_5(self) -> list[TestStep]: ] return steps + def pics_TC_VALCC_4_5(self) -> list[str]: + pics = [ + "VALCC.S", + ] + return pics + @run_if_endpoint_matches(has_cluster(Clusters.ValveConfigurationAndControl)) async def test_TC_VALCC_4_5(self):