Skip to content

Commit 0e76616

Browse files
lpbeliveau-silabsbzbarsky-apple
andauthoredAug 31, 2024
[Scenes] Missing Tests Cases (project-chip#32632)
* Implemented missing yaml tests cases and added EFS validation in SceneHandlerImpl * Apply suggestions from code review Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Cleanup up comments and useless static cast --------- Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
1 parent efa78b9 commit 0e76616

File tree

8 files changed

+1067
-55
lines changed

8 files changed

+1067
-55
lines changed
 

‎src/app/clusters/scenes-server/SceneHandlerImpl.cpp

+242-1
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,249 @@
1616
*/
1717

1818
#include <app/clusters/scenes-server/SceneHandlerImpl.h>
19+
#include <app/util/ember-io-storage.h>
20+
#include <app/util/endpoint-config-api.h>
21+
#include <app/util/odd-sized-integers.h>
1922

2023
namespace chip {
2124
namespace scenes {
2225

26+
namespace {
27+
28+
template <int ByteSize, bool IsSigned>
29+
using OddSizedInteger = app::OddSizedInteger<ByteSize, IsSigned>;
30+
using ConcreteAttributePath = app::ConcreteAttributePath;
31+
using AttributeValuePairType = app::Clusters::ScenesManagement::Structs::AttributeValuePairStruct::Type;
32+
33+
/// ConvertDefaultValueToWorkingValue
34+
/// @brief Helper function to convert a byte array to a value of the given type.
35+
/// @param EmberAfDefaultAttributeValue & defaultValue
36+
/// @return Value converted to the given working type
37+
template <typename Type>
38+
typename app::NumericAttributeTraits<Type>::WorkingType
39+
ConvertDefaultValueToWorkingValue(const EmberAfDefaultAttributeValue & defaultValue)
40+
{
41+
if constexpr (sizeof(typename app::NumericAttributeTraits<Type>::WorkingType) <= 2)
42+
{
43+
return static_cast<typename app::NumericAttributeTraits<Type>::WorkingType>(defaultValue.defaultValue);
44+
}
45+
46+
typename app::NumericAttributeTraits<Type>::StorageType sValue;
47+
memcpy(&sValue, defaultValue.ptrToDefaultValue, sizeof(sValue));
48+
return app::NumericAttributeTraits<Type>::StorageToWorking(sValue);
49+
}
50+
51+
/// IsExactlyOneValuePopulated
52+
/// @brief Helper function to verify that exactly one value is populated in a given AttributeValuePairType
53+
/// @param AttributeValuePairType & type AttributeValuePairType to verify
54+
/// @return bool true if only one value is populated, false otherwise
55+
bool IsExactlyOneValuePopulated(const AttributeValuePairType & type)
56+
{
57+
int count = 0;
58+
if (type.valueUnsigned8.HasValue())
59+
count++;
60+
if (type.valueSigned8.HasValue())
61+
count++;
62+
if (type.valueUnsigned16.HasValue())
63+
count++;
64+
if (type.valueSigned16.HasValue())
65+
count++;
66+
if (type.valueUnsigned32.HasValue())
67+
count++;
68+
if (type.valueSigned32.HasValue())
69+
count++;
70+
if (type.valueUnsigned64.HasValue())
71+
count++;
72+
if (type.valueSigned64.HasValue())
73+
count++;
74+
return count == 1;
75+
}
76+
77+
/// CapAttributeValue
78+
/// Cap the attribute value based on the attribute's min and max if they are defined,
79+
/// or based on the attribute's size if they are not.
80+
/// @param[in] aVPair AttributeValuePairType
81+
/// @param[in] metadata EmberAfAttributeMetadata
82+
///
83+
template <typename Type>
84+
void CapAttributeValue(typename app::NumericAttributeTraits<Type>::WorkingType & value, const EmberAfAttributeMetadata * metadata)
85+
{
86+
using IntType = app::NumericAttributeTraits<Type>;
87+
using WorkingType = typename IntType::WorkingType;
88+
89+
WorkingType maxValue;
90+
WorkingType minValue;
91+
uint16_t bitWidth = static_cast<uint16_t>(emberAfAttributeSize(metadata) * 8);
92+
93+
// TODO Use min/max values from Type to obtain min/max instead of relying on metadata. See:
94+
// https://github.com/project-chip/connectedhomeip/issues/35328
95+
96+
// Min/Max Value caps for the OddSize integers
97+
if (metadata->IsSignedIntegerAttribute())
98+
{
99+
// We use emberAfAttributeSize for cases like INT24S, INT40S, INT48S, INT56S where numeric_limits<WorkingType>::max()
100+
// wouldn't work
101+
maxValue = static_cast<WorkingType>((1ULL << (bitWidth - 1)) - 1);
102+
minValue = static_cast<WorkingType>(-(1ULL << (bitWidth - 1)));
103+
}
104+
else
105+
{
106+
// We use emberAfAttributeSize for cases like INT24U, INT40U, INT48U, INT56U where numeric_limits<WorkingType>::max()
107+
// wouldn't work
108+
if (ZCL_INT64U_ATTRIBUTE_TYPE == app::Compatibility::Internal::AttributeBaseType(metadata->attributeType))
109+
{
110+
maxValue = static_cast<WorkingType>(UINT64_MAX); // Bit shift of 64 is undefined so we use UINT64_MAX
111+
}
112+
else
113+
{
114+
maxValue = static_cast<WorkingType>((1ULL << bitWidth) - 1);
115+
}
116+
minValue = static_cast<WorkingType>(0);
117+
}
118+
119+
// Ensure that the metadata's signedness matches the working type's signedness
120+
VerifyOrDie(metadata->IsSignedIntegerAttribute() == std::is_signed<WorkingType>::value);
121+
122+
if (metadata->IsBoolean())
123+
{
124+
if (metadata->IsNullable() && (value != 1 && value != 0))
125+
{
126+
// If the attribute is nullable, the value can be set to NULL
127+
app::NumericAttributeTraits<WorkingType>::SetNull(value);
128+
}
129+
else
130+
{
131+
// Caping the value to 1 in case values greater than 1 are set
132+
value = value ? 1 : 0;
133+
}
134+
return;
135+
}
136+
137+
// Check metadata for min and max values
138+
if (metadata->HasMinMax())
139+
{
140+
const EmberAfAttributeMinMaxValue * minMaxValue = metadata->defaultValue.ptrToMinMaxValue;
141+
minValue = ConvertDefaultValueToWorkingValue<Type>(minMaxValue->minValue);
142+
maxValue = ConvertDefaultValueToWorkingValue<Type>(minMaxValue->maxValue);
143+
}
144+
145+
// If the attribute is nullable, the min and max values calculated for types will not be valid, however this does not
146+
// change the behavior here as the value will already be NULL if it is out of range. E.g. a nullable INT8U has a minValue of
147+
// -127. The code above determin minValue = -128, so an input value of -128 would not enter the condition block below, but would
148+
// be considered NULL nonetheless.
149+
if (metadata->IsNullable() && (minValue > value || maxValue < value))
150+
{
151+
// If the attribute is nullable, the value can be set to NULL
152+
app::NumericAttributeTraits<WorkingType>::SetNull(value);
153+
return;
154+
}
155+
156+
if (minValue > value)
157+
{
158+
value = minValue;
159+
}
160+
else if (maxValue < value)
161+
{
162+
value = maxValue;
163+
}
164+
}
165+
166+
/// @brief Validate the attribute exists for a given cluster
167+
/// @param[in] endpoint Endpoint ID
168+
/// @param[in] clusterID Cluster ID
169+
/// @param[in] aVPair AttributeValuePairType, will be mutated to cap the value if it is out of range
170+
/// @return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute) if the attribute does not exist for a given cluster or is not scenable
171+
/// @note This will allways fail for global list attributes. If we do want to make them scenable someday, we will need to
172+
/// use a different validation method.
173+
// TODO: Add check for "S" quality to determine if the attribute is scenable once suported :
174+
// https://github.com/project-chip/connectedhomeip/issues/24177
175+
CHIP_ERROR ValidateAttributePath(EndpointId endpoint, ClusterId cluster, AttributeValuePairType & aVPair)
176+
{
177+
const EmberAfAttributeMetadata * metadata = emberAfLocateAttributeMetadata(endpoint, cluster, aVPair.attributeID);
178+
179+
if (nullptr == metadata)
180+
{
181+
return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute);
182+
}
183+
184+
// There should never be more than one populated value in an ExtensionFieldSet
185+
VerifyOrReturnError(IsExactlyOneValuePopulated(aVPair), CHIP_ERROR_INVALID_ARGUMENT);
186+
187+
switch (app::Compatibility::Internal::AttributeBaseType(metadata->attributeType))
188+
{
189+
case ZCL_BOOLEAN_ATTRIBUTE_TYPE:
190+
case ZCL_INT8U_ATTRIBUTE_TYPE:
191+
VerifyOrReturnError(aVPair.valueUnsigned8.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
192+
CapAttributeValue<uint8_t>(aVPair.valueUnsigned8.Value(), metadata);
193+
break;
194+
case ZCL_INT16U_ATTRIBUTE_TYPE:
195+
VerifyOrReturnError(aVPair.valueUnsigned16.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
196+
CapAttributeValue<uint16_t>(aVPair.valueUnsigned16.Value(), metadata);
197+
break;
198+
case ZCL_INT24U_ATTRIBUTE_TYPE:
199+
VerifyOrReturnError(aVPair.valueUnsigned32.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
200+
CapAttributeValue<OddSizedInteger<3, false>>(aVPair.valueUnsigned32.Value(), metadata);
201+
break;
202+
case ZCL_INT32U_ATTRIBUTE_TYPE:
203+
VerifyOrReturnError(aVPair.valueUnsigned32.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
204+
CapAttributeValue<uint32_t>(aVPair.valueUnsigned32.Value(), metadata);
205+
break;
206+
case ZCL_INT40U_ATTRIBUTE_TYPE:
207+
VerifyOrReturnError(aVPair.valueUnsigned64.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
208+
CapAttributeValue<OddSizedInteger<5, false>>(aVPair.valueUnsigned64.Value(), metadata);
209+
break;
210+
case ZCL_INT48U_ATTRIBUTE_TYPE:
211+
VerifyOrReturnError(aVPair.valueUnsigned64.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
212+
CapAttributeValue<OddSizedInteger<6, false>>(aVPair.valueUnsigned64.Value(), metadata);
213+
break;
214+
case ZCL_INT56U_ATTRIBUTE_TYPE:
215+
VerifyOrReturnError(aVPair.valueUnsigned64.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
216+
CapAttributeValue<OddSizedInteger<7, false>>(aVPair.valueUnsigned64.Value(), metadata);
217+
break;
218+
case ZCL_INT64U_ATTRIBUTE_TYPE:
219+
VerifyOrReturnError(aVPair.valueUnsigned64.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
220+
CapAttributeValue<uint64_t>(aVPair.valueUnsigned64.Value(), metadata);
221+
break;
222+
case ZCL_INT8S_ATTRIBUTE_TYPE:
223+
VerifyOrReturnError(aVPair.valueSigned8.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
224+
CapAttributeValue<int8_t>(aVPair.valueSigned8.Value(), metadata);
225+
break;
226+
case ZCL_INT16S_ATTRIBUTE_TYPE:
227+
VerifyOrReturnError(aVPair.valueSigned16.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
228+
CapAttributeValue<int16_t>(aVPair.valueSigned16.Value(), metadata);
229+
break;
230+
case ZCL_INT24S_ATTRIBUTE_TYPE:
231+
VerifyOrReturnError(aVPair.valueSigned32.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
232+
CapAttributeValue<OddSizedInteger<3, true>>(aVPair.valueSigned32.Value(), metadata);
233+
break;
234+
case ZCL_INT32S_ATTRIBUTE_TYPE:
235+
VerifyOrReturnError(aVPair.valueSigned32.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
236+
CapAttributeValue<int32_t>(aVPair.valueSigned32.Value(), metadata);
237+
break;
238+
case ZCL_INT40S_ATTRIBUTE_TYPE:
239+
VerifyOrReturnError(aVPair.valueSigned64.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
240+
CapAttributeValue<OddSizedInteger<5, true>>(aVPair.valueSigned64.Value(), metadata);
241+
break;
242+
case ZCL_INT48S_ATTRIBUTE_TYPE:
243+
VerifyOrReturnError(aVPair.valueSigned64.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
244+
CapAttributeValue<OddSizedInteger<6, true>>(aVPair.valueSigned64.Value(), metadata);
245+
break;
246+
case ZCL_INT56S_ATTRIBUTE_TYPE:
247+
VerifyOrReturnError(aVPair.valueSigned64.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
248+
CapAttributeValue<OddSizedInteger<7, true>>(aVPair.valueSigned64.Value(), metadata);
249+
break;
250+
case ZCL_INT64S_ATTRIBUTE_TYPE:
251+
VerifyOrReturnError(aVPair.valueSigned64.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
252+
CapAttributeValue<int64_t>(aVPair.valueSigned64.Value(), metadata);
253+
break;
254+
default:
255+
return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute);
256+
}
257+
258+
return CHIP_NO_ERROR;
259+
}
260+
} // namespace
261+
23262
CHIP_ERROR
24263
DefaultSceneHandlerImpl::EncodeAttributeValueList(const List<AttributeValuePairType> & aVlist, MutableByteSpan & serializedBytes)
25264
{
@@ -58,7 +297,9 @@ DefaultSceneHandlerImpl::SerializeAdd(EndpointId endpoint, const ExtensionFieldS
58297
auto pair_iterator = extensionFieldSet.attributeValueList.begin();
59298
while (pair_iterator.Next())
60299
{
61-
aVPairs[pairCount] = pair_iterator.GetValue();
300+
AttributeValuePairType currentPair = pair_iterator.GetValue();
301+
ReturnErrorOnFailure(ValidateAttributePath(endpoint, extensionFieldSet.clusterID, currentPair));
302+
aVPairs[pairCount] = currentPair;
62303
pairCount++;
63304
}
64305
ReturnErrorOnFailure(pair_iterator.GetStatus());

‎src/app/clusters/scenes-server/scenes-server.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ CHIP_ERROR AddResponseOnError(CommandHandlerInterface::HandlerContext & ctx, Res
7474
{
7575
resp.status = to_underlying(Protocols::InteractionModel::Status::ResourceExhausted);
7676
}
77+
else if (CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute) == err)
78+
{
79+
// TODO: Confirm if we need to add UnsupportedAttribute status as a return for Scene Commands
80+
resp.status = to_underlying(Protocols::InteractionModel::Status::InvalidCommand);
81+
}
7782
else
7883
{
7984
resp.status = to_underlying(StatusIB(err).mStatus);

‎src/app/tests/TestSceneTable.cpp

+637-45
Large diffs are not rendered by default.

‎src/app/tests/suites/certification/Test_TC_S_2_2.yaml

+86
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,20 @@ tests:
451451
response:
452452
error: CONSTRAINT_ERROR
453453

454+
- label:
455+
"Step 4f: TH sends a RecallScene command to DUT with the GroupID field
456+
set to GI (Where GI is a group currently absent from the group table)
457+
and the SceneID field set to 0x01."
458+
command: "RecallScene"
459+
arguments:
460+
values:
461+
- name: "GroupID"
462+
value: GI
463+
- name: "SceneID"
464+
value: 0x01
465+
response:
466+
error: INVALID_COMMAND
467+
454468
- label:
455469
"Step 5a: TH sends a ViewScene command to DUT with the GroupID field
456470
set to G1 and the SceneID field set to 0x01."
@@ -843,6 +857,78 @@ tests:
843857
- name: "SceneID"
844858
value: 0xFF
845859

860+
- label:
861+
"Step 8g: TH sends a AddScene command to DUT with the GroupID field
862+
set to G1, the SceneID field set to 0x01, the TransitionTime field set
863+
to 1000 (1s) and extension field sets holding an invalid
864+
ExtensionField (Unscenable attribute ID for given cluster). This
865+
should fail and return a status of 0x85 (INVALID_COMMAND)."
866+
## TODO: Change test to test for an existing bu non scenable attribute ID once scenability check is possible, see issue: https://github.com/project-chip/connectedhomeip/issues/24177
867+
PICS: S.S.C00.Rsp && PICS_SDK_CI_ONLY
868+
command: "AddScene"
869+
arguments:
870+
values:
871+
- name: "GroupID"
872+
value: G1
873+
- name: "SceneID"
874+
value: 0x01
875+
- name: "TransitionTime"
876+
value: 1000
877+
- name: "SceneName"
878+
value: "Scene1"
879+
- name: "ExtensionFieldSets"
880+
value:
881+
[
882+
{
883+
ClusterID: 0x0006,
884+
AttributeValueList:
885+
[{ AttributeID: 0x4011, ValueUnsigned8: 0x01 }],
886+
},
887+
]
888+
response:
889+
values:
890+
- name: "Status"
891+
value: 0x85
892+
- name: "GroupID"
893+
value: G1
894+
- name: "SceneID"
895+
value: 0x01
896+
897+
- label:
898+
"Step 8g: TH sends a AddScene command to DUT with the GroupID field
899+
set to G1, the SceneID field set to 0x01, the TransitionTime field set
900+
to 1000 (1s) and extension field sets holding an invalid
901+
ExtensionField (Unscenable attribute ID for given cluster). This
902+
should fail and return a status of 0x85 (INVALID_COMMAND)."
903+
## TODO: Change test to test for an existing bu non scenable attribute ID once scenability check is possible, see issue: https://github.com/project-chip/connectedhomeip/issues/24177
904+
verification: |
905+
./chip-tool scenesmanagement add-scene 0x0001 0x01 1000 "scene name" '[{"clusterID": "0x006", "attributeValueList":[{"attributeID": "0x4001", "attributeValue": "0x01"}]}]' 1 1
906+
907+
Verify DUT sends a AddSceneResponse command to TH with the Status field set to 0x85 (INVALID_COMMAND), the GroupID field set to G1 and the SceneID field set to 0x01 on the TH(Chip-tool)
908+
Log and below is the sample log provided for the raspi platform:
909+
910+
[1706763610.675038][4232:4234] CHIP:DMG: },
911+
[1706763610.675108][4232:4234] CHIP:DMG: Received Command Response Data, Endpoint=1 Cluster=0x0000_0062 Command=0x0000_0000
912+
[1706763610.675134][4232:4234] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0062 Command 0x0000_0000
913+
[1706763610.675187][4232:4234] CHIP:TOO: AddSceneResponse: {
914+
[1706763610.675215][4232:4234] CHIP:TOO: status: 133
915+
[1706763610.675229][4232:4234] CHIP:TOO: groupID: 1
916+
[1706763610.675244][4232:4234] CHIP:TOO: sceneID: 1
917+
[1706763610.675258][4232:4234] CHIP:TOO: }
918+
cluster: "LogCommands"
919+
command: "UserPrompt"
920+
PICS: PICS_SKIP_SAMPLE_APP
921+
arguments:
922+
values:
923+
- name: "message"
924+
value:
925+
"Please execute the add scene command with an invalid
926+
extensionfieldsets due to a non sceneable attribute not in
927+
an extensionfieldset on DUT and enter 'y' if the command
928+
returned a status of 0x85 (INVALID_COMMAND)"
929+
- name: "expectedValue"
930+
value: "y"
931+
846932
- label:
847933
"Step 9a: TH sends a RemoveScene command to DUT with the GroupID field
848934
set to G1 and the SceneID field set to 0x01."

‎src/app/tests/suites/certification/Test_TC_S_2_3.yaml

+75
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,81 @@ tests:
676676
- name: "SceneID"
677677
value: 0x03
678678

679+
- label:
680+
"Step 6f: TH sends a StoreScene command to to group G1 with the
681+
GroupID field set to G1 and the SceneID field set to 0x03."
682+
PICS: S.S.C04.Rsp
683+
command: "StoreScene"
684+
groupId: G1
685+
arguments:
686+
values:
687+
- name: "GroupID"
688+
value: G1
689+
- name: "SceneID"
690+
value: 0x03
691+
692+
- label:
693+
"Step 6g: TH sends a ViewScene command to DUT with the GroupID field
694+
set to G1 and the SceneID field set to 0x03."
695+
PICS: S.S.C01.Rsp
696+
command: "ViewScene"
697+
arguments:
698+
values:
699+
- name: "GroupID"
700+
value: G1
701+
- name: "SceneID"
702+
value: 0x03
703+
response:
704+
values:
705+
- name: "Status"
706+
value: 0x00
707+
- name: "GroupID"
708+
value: G1
709+
- name: "SceneID"
710+
value: 0x03
711+
712+
- label:
713+
"Step 6h: TH sends a RemoveScene command to group G1 with the GroupID
714+
field set to G1 and the SceneID field set to 0x03."
715+
PICS: S.S.C02.Rsp
716+
command: "RemoveScene"
717+
groupId: G1
718+
arguments:
719+
values:
720+
- name: "GroupID"
721+
value: G1
722+
- name: "SceneID"
723+
value: 0x03
724+
725+
- label: "Wait 1+ s to give CI time to process the RemoveScene command."
726+
PICS: PICS_SDK_CI_ONLY
727+
cluster: "DelayCommands"
728+
command: "WaitForMs"
729+
arguments:
730+
values:
731+
- name: "ms"
732+
value: 1250
733+
734+
- label:
735+
"Step 6i: TH sends a ViewScene command to DUT with the GroupID field
736+
set to G1 and the SceneID field set to 0x03."
737+
PICS: S.S.C01.Rsp
738+
command: "ViewScene"
739+
arguments:
740+
values:
741+
- name: "GroupID"
742+
value: G1
743+
- name: "SceneID"
744+
value: 0x03
745+
response:
746+
values:
747+
- name: "Status"
748+
value: 0x8b
749+
- name: "GroupID"
750+
value: G1
751+
- name: "SceneID"
752+
value: 0x03
753+
679754
- label:
680755
"Step 7a: TH sends a CopyScene command to DUT with the mode field set
681756
to 0x00, the group identifier from field set to G1, the scene

‎src/app/util/attribute-metadata.h

+20-1
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717

1818
#pragma once
1919

20+
#include <app-common/zap-generated/attribute-type.h>
2021
#include <app/util/basic-types.h>
21-
2222
#include <cstdint>
2323

2424
/**
@@ -158,6 +158,25 @@ struct EmberAfAttributeMetadata
158158
*/
159159
EmberAfAttributeMask mask;
160160

161+
/**
162+
* Check wether this attribute is a boolean based on its type according to the spec.
163+
*/
164+
bool IsBoolean() const { return attributeType == ZCL_BOOLEAN_ATTRIBUTE_TYPE; }
165+
166+
/**
167+
* Check wether this attribute is signed based on its type according to the spec.
168+
*/
169+
bool IsSignedIntegerAttribute() const
170+
{
171+
return (attributeType >= ZCL_INT8S_ATTRIBUTE_TYPE && attributeType <= ZCL_INT64S_ATTRIBUTE_TYPE) ||
172+
attributeType == ZCL_TEMPERATURE_ATTRIBUTE_TYPE;
173+
}
174+
175+
/**
176+
* Check whether this attribute has a define min and max.
177+
*/
178+
bool HasMinMax() const { return mask & ATTRIBUTE_MASK_MIN_MAX; }
179+
161180
/**
162181
* Check whether this attribute is nullable.
163182
*/

‎src/app/util/attribute-table.cpp

+1-8
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,6 @@ using namespace chip;
4141
using namespace chip::app;
4242

4343
namespace {
44-
// Zigbee spec says types between signed 8 bit and signed 64 bit
45-
bool emberAfIsTypeSigned(EmberAfAttributeType dataType)
46-
{
47-
return (dataType >= ZCL_INT8S_ATTRIBUTE_TYPE && dataType <= ZCL_INT64S_ATTRIBUTE_TYPE) ||
48-
dataType == ZCL_TEMPERATURE_ATTRIBUTE_TYPE;
49-
}
50-
5144
/**
5245
* @brief Simple integer comparison function.
5346
* Compares two values of a known length as integers.
@@ -386,7 +379,7 @@ Status emAfWriteAttribute(EndpointId endpoint, ClusterId cluster, AttributeId at
386379
maxBytes = maxv.ptrToDefaultValue;
387380
}
388381

389-
bool isAttributeSigned = emberAfIsTypeSigned(metadata->attributeType);
382+
bool isAttributeSigned = metadata->IsSignedIntegerAttribute();
390383
bool isOutOfRange = emberAfCompareValues(minBytes, data, dataLen, isAttributeSigned) == 1 ||
391384
emberAfCompareValues(maxBytes, data, dataLen, isAttributeSigned) == -1;
392385

‎src/app/util/mock/MockNodeConfig.h

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ constexpr EmberAfAttributeMetadata DefaultAttributeMetadata(chip::AttributeId id
5353
struct MockAttributeConfig
5454
{
5555
MockAttributeConfig(AttributeId aId) : id(aId), attributeMetaData(internal::DefaultAttributeMetadata(aId)) {}
56+
MockAttributeConfig(AttributeId aId, EmberAfAttributeMetadata metadata) : id(aId), attributeMetaData(metadata) {}
5657
MockAttributeConfig(AttributeId aId, EmberAfAttributeType type,
5758
EmberAfAttributeMask mask = ATTRIBUTE_MASK_WRITABLE | ATTRIBUTE_MASK_NULLABLE) :
5859
id(aId),

0 commit comments

Comments
 (0)
Please sign in to comment.