Skip to content

Commit cbe7da5

Browse files
committed
Valve: Disco ball - argument parsing for open
1 parent 82624dc commit cbe7da5

File tree

3 files changed

+233
-1
lines changed

3 files changed

+233
-1
lines changed

src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.cpp

+71
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,77 @@ CHIP_ERROR ClusterLogic::ClearValveFault(const ValveFaultBitmap valveFault)
160160
return CHIP_ERROR_NOT_IMPLEMENTED;
161161
}
162162

163+
CHIP_ERROR ClusterLogic::GetRealTargetLevel(const std::optional<uint8_t> & targetLevel,
164+
DataModel::Nullable<uint8_t> & realTargetLevel)
165+
{
166+
if (!mConformance.HasFeature(Feature::kLevel))
167+
{
168+
if (targetLevel.has_value())
169+
{
170+
return CHIP_ERROR_INVALID_ARGUMENT;
171+
}
172+
realTargetLevel = DataModel::NullNullable;
173+
return CHIP_NO_ERROR;
174+
}
175+
// LVL is supported
176+
if (!targetLevel.has_value())
177+
{
178+
if (mConformance.supportsDefaultOpenLevel)
179+
{
180+
realTargetLevel = mState.defaultOpenLevel;
181+
return CHIP_NO_ERROR;
182+
}
183+
realTargetLevel.SetNonNull(100u);
184+
return CHIP_NO_ERROR;
185+
}
186+
// targetLevel has a value
187+
if (mConformance.supportsLevelStep)
188+
{
189+
if ((targetLevel.value() != 100u) && ((targetLevel.value() % mState.levelStep) != 0))
190+
{
191+
return CHIP_ERROR_INVALID_ARGUMENT;
192+
}
193+
}
194+
realTargetLevel.SetNonNull(targetLevel.value());
195+
return CHIP_NO_ERROR;
196+
}
197+
198+
CHIP_ERROR ClusterLogic::HandleOpenCommand(std::optional<DataModel::Nullable<uint32_t>> openDuration,
199+
std::optional<uint8_t> targetLevel)
200+
{
201+
// openDuration
202+
// - if this is omitted, fall back to defaultOpenDuration
203+
// - if this is NULL, remaining duration is NULL
204+
// - if this is a value, use that value
205+
// - if remaining duration is not null and TS is supported, set the autoCloseTime as appropriate
206+
// targetLevel
207+
// - if LVL is not supported
208+
// - if this is omitted, that's correct
209+
// - if this is supplied return error
210+
// - if LVL is supported
211+
// - if this value is not supplied, use defaultOpenLevel if supported, otherwise 100
212+
// - if this value is supplied, check against levelStep, error if not OK, otherwise set targetLevel
213+
VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE);
214+
215+
DataModel::Nullable<uint32_t> realOpenDuration;
216+
if (openDuration.has_value())
217+
{
218+
realOpenDuration = openDuration.value();
219+
}
220+
else
221+
{
222+
realOpenDuration = mState.defaultOpenDuration;
223+
}
224+
225+
DataModel::Nullable<uint8_t> realTargetLevel;
226+
ReturnErrorOnFailure(GetRealTargetLevel(targetLevel, realTargetLevel));
227+
228+
mClusterDriver.HandleOpenValve(realTargetLevel);
229+
mState.openDuration = realOpenDuration;
230+
mState.targetLevel = realTargetLevel;
231+
return CHIP_NO_ERROR;
232+
}
233+
163234
} // namespace ValveConfigurationAndControl
164235
} // namespace Clusters
165236
} // namespace app

src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-cluster-logic.h

+9
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,16 @@ class ClusterLogic
131131
// Current state
132132
// Current level
133133

134+
// Return CHIP_ERROR_INCORRECT_STATE if the class has not been initialized.
135+
// Return CHIP_ERROR_INVALID_ARGUMENT if the input values are out is out of range or the targetLevel is supplied when LVL is not
136+
// supported.
137+
// Calls delegate HandleOpen function after validating the parameters
138+
CHIP_ERROR HandleOpenCommand(std::optional<DataModel::Nullable<uint32_t>> openDuration, std::optional<uint8_t> targetLevel);
139+
134140
private:
141+
// Returns the target level to send to the delegate based on the targetLevel command field, the device conformance and the
142+
// defaults. Returns error if the supplied target level is invalid or not supported by the device conformance.
143+
CHIP_ERROR GetRealTargetLevel(const std::optional<uint8_t> & targetLevel, DataModel::Nullable<uint8_t> & realTargetLevel);
135144
bool mInitialized = false;
136145

137146
Delegate & mClusterDriver;

src/app/tests/TestValveConfigurationAndControl.cpp

+153-1
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,14 @@ class TestDelegate : public Delegate
4040
{
4141
public:
4242
TestDelegate() {}
43-
DataModel::Nullable<chip::Percent> HandleOpenValve(DataModel::Nullable<chip::Percent> level) override
43+
DataModel::Nullable<Percent> HandleOpenValve(DataModel::Nullable<Percent> level) override
4444
{
45+
lastRequestedLevel = level;
4546
return DataModel::NullNullable;
4647
}
4748
CHIP_ERROR HandleCloseValve() override { return CHIP_NO_ERROR; }
4849
void HandleRemainingDurationTick(uint32_t duration) override {}
50+
DataModel::Nullable<Percent> lastRequestedLevel;
4951
};
5052

5153
TEST_F(TestValveConfigurationAndControlClusterLogic, TestConformanceValid)
@@ -504,6 +506,156 @@ TEST_F(TestValveConfigurationAndControlClusterLogic, TestSetDefaultOpenLevel)
504506
EXPECT_EQ(logic_no_level.SetDefaultOpenLevel(testVal), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE);
505507
}
506508

509+
TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenDuration)
510+
{
511+
TestDelegate delegate;
512+
TestPersistentStorageDelegate storageDelegate;
513+
EndpointId endpoint = 0;
514+
MatterContext context = MatterContext(endpoint, storageDelegate);
515+
ClusterLogic logic = ClusterLogic(delegate, context);
516+
517+
ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel) | to_underlying(Feature::kTimeSync),
518+
.supportsDefaultOpenLevel = true,
519+
.supportsValveFault = true,
520+
.supportsLevelStep = true };
521+
EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR);
522+
523+
DataModel::Nullable<ElapsedS> valElapsedSNullable;
524+
525+
EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR);
526+
EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable);
527+
528+
EXPECT_EQ(logic.GetDefaultOpenDuration(valElapsedSNullable), CHIP_NO_ERROR);
529+
EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable);
530+
531+
// Fall back to default
532+
EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR);
533+
EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR);
534+
EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable);
535+
536+
DataModel::Nullable<ElapsedS> defaultOpenDuration;
537+
defaultOpenDuration.SetNonNull(12u);
538+
EXPECT_EQ(logic.SetDefaultOpenDuration(defaultOpenDuration), CHIP_NO_ERROR);
539+
EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR);
540+
EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR);
541+
EXPECT_EQ(valElapsedSNullable, defaultOpenDuration);
542+
543+
// Set from command parameters
544+
DataModel::Nullable<ElapsedS> openDuration;
545+
openDuration.SetNull();
546+
EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::nullopt), CHIP_NO_ERROR);
547+
EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR);
548+
EXPECT_EQ(valElapsedSNullable, DataModel::NullNullable);
549+
550+
openDuration.SetNonNull(12u);
551+
EXPECT_EQ(logic.HandleOpenCommand(std::make_optional(openDuration), std::nullopt), CHIP_NO_ERROR);
552+
EXPECT_EQ(logic.GetOpenDuration(valElapsedSNullable), CHIP_NO_ERROR);
553+
EXPECT_EQ(valElapsedSNullable.ValueOr(0), 12u);
554+
}
555+
556+
TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenTargetLevelFeatureUnsupported)
557+
{
558+
TestDelegate delegate;
559+
TestPersistentStorageDelegate storageDelegate;
560+
EndpointId endpoint = 0;
561+
MatterContext context = MatterContext(endpoint, storageDelegate);
562+
ClusterLogic logic = ClusterLogic(delegate, context);
563+
564+
ClusterConformance conformance = {
565+
.featureMap = 0, .supportsDefaultOpenLevel = false, .supportsValveFault = false, .supportsLevelStep = false
566+
};
567+
EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR);
568+
569+
// Set the last value to something non-null first so we can ensure the delegate was called correctly.
570+
delegate.lastRequestedLevel.SetNonNull(0);
571+
EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR);
572+
// This configuration doesn't support level, so the delegate should be called with a NullNullable
573+
EXPECT_EQ(delegate.lastRequestedLevel, DataModel::NullNullable);
574+
575+
// Should get an error when this is called with target level set since the feature is unsupported.
576+
EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::make_optional(50u)), CHIP_ERROR_INVALID_ARGUMENT);
577+
}
578+
579+
TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenTargetLevelNotSuppliedNoDefaultSupported)
580+
{
581+
TestDelegate delegate;
582+
TestPersistentStorageDelegate storageDelegate;
583+
EndpointId endpoint = 0;
584+
MatterContext context = MatterContext(endpoint, storageDelegate);
585+
ClusterLogic logic = ClusterLogic(delegate, context);
586+
587+
ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel),
588+
.supportsDefaultOpenLevel = false,
589+
.supportsValveFault = false,
590+
.supportsLevelStep = false };
591+
EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR);
592+
// Set the last value to something non-null first so we can ensure the delegate was called correctly.
593+
delegate.lastRequestedLevel.SetNonNull(0);
594+
EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR);
595+
EXPECT_EQ(delegate.lastRequestedLevel.ValueOr(0), 100u);
596+
}
597+
598+
TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenTargetLevelNotSuppliedDefaultSupported)
599+
{
600+
TestDelegate delegate;
601+
TestPersistentStorageDelegate storageDelegate;
602+
EndpointId endpoint = 0;
603+
MatterContext context = MatterContext(endpoint, storageDelegate);
604+
ClusterLogic logic = ClusterLogic(delegate, context);
605+
606+
ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel),
607+
.supportsDefaultOpenLevel = true,
608+
.supportsValveFault = false,
609+
.supportsLevelStep = false };
610+
EXPECT_EQ(logic.Init(conformance), CHIP_NO_ERROR);
611+
// Set the last value to something non-null first so we can ensure the delegate was called correctly.
612+
delegate.lastRequestedLevel.SetNonNull(0);
613+
EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR);
614+
EXPECT_EQ(delegate.lastRequestedLevel.ValueOr(0), 100u);
615+
616+
EXPECT_EQ(logic.SetDefaultOpenLevel(50u), CHIP_NO_ERROR);
617+
EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::nullopt), CHIP_NO_ERROR);
618+
EXPECT_EQ(delegate.lastRequestedLevel.ValueOr(0), 50u);
619+
}
620+
621+
TEST_F(TestValveConfigurationAndControlClusterLogic, TestHandleOpenTargetLevelSupplied)
622+
{
623+
TestDelegate delegate;
624+
TestPersistentStorageDelegate storageDelegate;
625+
EndpointId endpoint = 0;
626+
MatterContext context = MatterContext(endpoint, storageDelegate);
627+
ClusterLogic logic = ClusterLogic(delegate, context);
628+
629+
ClusterConformance conformance = { .featureMap = to_underlying(Feature::kLevel),
630+
.supportsDefaultOpenLevel = true,
631+
.supportsValveFault = false,
632+
.supportsLevelStep = true };
633+
ClusterState state = ClusterState();
634+
state.levelStep = 33;
635+
EXPECT_EQ(logic.Init(conformance, state), CHIP_NO_ERROR);
636+
// Set the last value to something non-null first so we can ensure the delegate was called correctly.
637+
delegate.lastRequestedLevel.SetNonNull(0);
638+
639+
// 33, 66, 99 and 100 should all work, nothing else should
640+
EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::make_optional(33u)), CHIP_NO_ERROR);
641+
EXPECT_EQ(delegate.lastRequestedLevel.ValueOr(0), 33u);
642+
643+
EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::make_optional(66u)), CHIP_NO_ERROR);
644+
EXPECT_EQ(delegate.lastRequestedLevel.ValueOr(0), 66u);
645+
646+
EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::make_optional(99u)), CHIP_NO_ERROR);
647+
EXPECT_EQ(delegate.lastRequestedLevel.ValueOr(0), 99u);
648+
649+
EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::make_optional(100u)), CHIP_NO_ERROR);
650+
EXPECT_EQ(delegate.lastRequestedLevel.ValueOr(0), 100u);
651+
652+
EXPECT_EQ(logic.HandleOpenCommand(std::nullopt, std::make_optional(32u)), CHIP_ERROR_INVALID_ARGUMENT);
653+
// Ensure this wasn't called again.
654+
EXPECT_EQ(delegate.lastRequestedLevel.ValueOr(0), 100u);
655+
}
656+
657+
// TODO: Add tests (and support) for checking default open level against the level step
658+
507659
} // namespace ValveConfigurationAndControl
508660
} // namespace Clusters
509661
} // namespace app

0 commit comments

Comments
 (0)