3 * Copyright (c) 2020 Project CHIP Authors
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 * Copyright (c) 2020 Silicon Labs
22 * Licensed under the Apache License, Version 2.0 (the "License");
23 * you may not use this file except in compliance with the License.
24 * You may obtain a copy of the License at
26 * http://www.apache.org/licenses/LICENSE-2.0
28 * Unless required by applicable law or agreed to in writing, software
29 * distributed under the License is distributed on an "AS IS" BASIS,
30 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31 * See the License for the specific language governing permissions and
32 * limitations under the License.
34 /***************************************************************************/
37 * @brief This file contains the code to manipulate
38 *the Smart Energy attribute table. This handles
39 *external calls to read/write the table, as well as
41 *******************************************************************************
42 ******************************************************************************/
44 // this file contains all the common includes for clusters in the zcl-util
46 #include "attribute-storage.h"
48 // for pulling in defines dealing with EITHER server or client
50 #include "app/util/common.h"
51 #include "gen/callback.h"
53 #include <app/reporting/reporting.h>
57 //------------------------------------------------------------------------------
59 //------------------------------------------------------------------------------
60 // External Declarations
62 //------------------------------------------------------------------------------
63 // Forward Declarations
65 //------------------------------------------------------------------------------
68 EmberAfStatus emberAfWriteAttributeExternal(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask,
69 uint16_t manufacturerCode, uint8_t * dataPtr, EmberAfAttributeType dataType)
71 EmberAfAttributeWritePermission extWritePermission =
72 emberAfAllowNetworkWriteAttributeCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataPtr, dataType);
73 switch (extWritePermission)
75 case EMBER_ZCL_ATTRIBUTE_WRITE_PERMISSION_DENY_WRITE:
76 return EMBER_ZCL_STATUS_FAILURE;
77 case EMBER_ZCL_ATTRIBUTE_WRITE_PERMISSION_ALLOW_WRITE_NORMAL:
78 case EMBER_ZCL_ATTRIBUTE_WRITE_PERMISSION_ALLOW_WRITE_OF_READ_ONLY:
79 return emAfWriteAttribute(endpoint, cluster, attributeID, mask, manufacturerCode, dataPtr, dataType,
80 (extWritePermission == EMBER_ZCL_ATTRIBUTE_WRITE_PERMISSION_ALLOW_WRITE_OF_READ_ONLY), false);
82 return (EmberAfStatus) extWritePermission;
86 //@deprecated use emberAfWriteServerAttribute or emberAfWriteClientAttribute
87 EmberAfStatus emberAfWriteAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask,
88 uint8_t * dataPtr, EmberAfAttributeType dataType)
90 return emAfWriteAttribute(endpoint, cluster, attributeID, mask, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr, dataType,
91 true, // override read-only?
95 EmberAfStatus emberAfWriteClientAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr,
96 EmberAfAttributeType dataType)
98 return emAfWriteAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_CLIENT, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr,
100 true, // override read-only?
101 false); // just test?
104 EmberAfStatus emberAfWriteServerAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr,
105 EmberAfAttributeType dataType)
107 return emAfWriteAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_SERVER, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr,
109 true, // override read-only?
110 false); // just test?
113 EmberAfStatus emberAfWriteManufacturerSpecificClientAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID,
114 uint16_t manufacturerCode, uint8_t * dataPtr,
115 EmberAfAttributeType dataType)
117 return emAfWriteAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_CLIENT, manufacturerCode, dataPtr, dataType,
118 true, // override read-only?
119 false); // just test?
122 EmberAfStatus emberAfWriteManufacturerSpecificServerAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID,
123 uint16_t manufacturerCode, uint8_t * dataPtr,
124 EmberAfAttributeType dataType)
126 return emAfWriteAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_SERVER, manufacturerCode, dataPtr, dataType,
127 true, // override read-only?
128 false); // just test?
131 EmberAfStatus emberAfVerifyAttributeWrite(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask,
132 uint16_t manufacturerCode, uint8_t * dataPtr, EmberAfAttributeType dataType)
134 return emAfWriteAttribute(endpoint, cluster, attributeID, mask, manufacturerCode, dataPtr, dataType,
135 false, // override read-only?
139 EmberAfStatus emberAfReadAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask, uint8_t * dataPtr,
140 uint8_t readLength, EmberAfAttributeType * dataType)
142 return emAfReadAttribute(endpoint, cluster, attributeID, mask, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr, readLength, dataType);
145 EmberAfStatus emberAfReadServerAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr,
148 return emAfReadAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_SERVER, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr,
152 EmberAfStatus emberAfReadClientAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr,
155 return emAfReadAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_CLIENT, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr,
159 EmberAfStatus emberAfReadManufacturerSpecificServerAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID,
160 uint16_t manufacturerCode, uint8_t * dataPtr, uint8_t readLength)
162 return emAfReadAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_SERVER, manufacturerCode, dataPtr, readLength, NULL);
165 EmberAfStatus emberAfReadManufacturerSpecificClientAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID,
166 uint16_t manufacturerCode, uint8_t * dataPtr, uint8_t readLength)
168 return emAfReadAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_CLIENT, manufacturerCode, dataPtr, readLength, NULL);
171 bool emberAfReadSequentialAttributesAddToResponse(EndpointId endpoint, ClusterId clusterId, AttributeId startAttributeId,
172 uint8_t mask, uint16_t manufacturerCode, uint8_t maxAttributeIds,
173 bool includeAccessControl)
176 uint16_t discovered = 0;
177 uint16_t skipped = 0;
180 EmberAfCluster * cluster = emberAfFindClusterWithMfgCode(endpoint, clusterId, mask, manufacturerCode);
182 EmberAfAttributeSearchRecord record;
183 record.endpoint = endpoint;
184 record.clusterId = clusterId;
185 record.clusterMask = mask;
186 record.attributeId = startAttributeId;
187 record.manufacturerCode = manufacturerCode;
189 // If we don't have the cluster or it doesn't match the search, we're done.
190 if (cluster == NULL || !emAfMatchCluster(cluster, &record))
195 for (i = 0; i < cluster->attributeCount; i++)
197 EmberAfAttributeMetadata * metadata = &cluster->attributes[i];
199 // If the cluster is not manufacturer-specific, an attribute is considered
200 // only if its manufacturer code matches that of the command (which may be
202 if (!emberAfClusterIsManufacturerSpecific(cluster))
204 record.attributeId = metadata->attributeId;
205 if (!emAfMatchAttribute(cluster, metadata, &record))
211 if (metadata->attributeId < startAttributeId)
215 else if (discovered < maxAttributeIds)
217 emberAfPutInt16uInResp(metadata->attributeId);
218 emberAfPutInt8uInResp(metadata->attributeType);
219 if (includeAccessControl)
221 // bit 0 : Readable <-- All our attributes are readable
222 // bit 1 : Writable <-- The only thing we track in the attribute metadata mask
223 // bit 2 : Reportable <-- All our attributes are reportable
224 emberAfPutInt8uInResp((metadata->mask & ATTRIBUTE_MASK_WRITABLE) ? 0x07 : 0x05);
230 // MISRA requires ..else if.. to have terminating else.
235 // We are finished if there are no more attributes to find, which means the
236 // number of attributes discovered plus the number skipped equals the total
237 // attributes in the cluster. For manufacturer-specific clusters, the total
238 // includes all attributes in the cluster. For standard ZCL clusters, if the
239 // the manufacturer code is set, the total is the number of attributes that
240 // match the manufacturer code. Otherwise, the total is the number of
241 // standard ZCL attributes in the cluster.
242 return (discovered + skipped == total);
245 static void emberAfAttributeDecodeAndPrintCluster(ClusterId cluster, uint16_t mfgCode)
247 #if defined(EMBER_AF_PRINT_ENABLE) && defined(EMBER_AF_PRINT_ATTRIBUTES)
248 uint16_t index = emberAfFindClusterNameIndexWithMfgCode(cluster, mfgCode);
251 emberAfAttributesPrintln("(%p)", zclClusterNames[index].name);
253 emberAfAttributesFlush();
254 #endif // defined(EMBER_AF_PRINT_ENABLE) && defined(EMBER_AF_PRINT_ATTRIBUTES)
257 void emberAfPrintAttributeTable(void)
259 uint8_t data[ATTRIBUTE_LARGEST];
260 decltype(emberAfEndpointCount()) endpointIndex;
261 decltype(EmberAfEndpointType::clusterCount) clusterIndex;
262 uint16_t attributeIndex;
263 EmberAfStatus status;
265 for (endpointIndex = 0; endpointIndex < emberAfEndpointCount(); endpointIndex++)
267 EmberAfDefinedEndpoint * ep = &(emAfEndpoints[endpointIndex]);
268 emberAfAttributesPrintln("ENDPOINT %x", ep->endpoint);
269 emberAfAttributesPrintln("clus / side / attr / mfg /type(len)/ rw / storage / data (raw)");
270 emberAfAttributesFlush();
271 for (clusterIndex = 0; clusterIndex < ep->endpointType->clusterCount; clusterIndex++)
273 EmberAfCluster * cluster = &(ep->endpointType->cluster[clusterIndex]);
275 for (attributeIndex = 0; attributeIndex < cluster->attributeCount; attributeIndex++)
277 EmberAfAttributeMetadata * metaData = &(cluster->attributes[attributeIndex]);
279 // Depending on user config, this loop can take a very long time to
280 // run and watchdog reset will kick in. As a workaround, we'll
281 // manually reset the watchdog.
282 // halResetWatchdog();
284 emberAfAttributesPrint("%2x / %p / %2x / ", cluster->clusterId,
285 (emberAfAttributeIsClient(metaData) ? "clnt" : "srvr"), metaData->attributeId);
286 mfgCode = emAfGetManufacturerCodeForAttribute(cluster, metaData);
287 if (mfgCode == EMBER_AF_NULL_MANUFACTURER_CODE)
289 emberAfAttributesPrint("----");
293 emberAfAttributesPrint("%2x", mfgCode);
295 emberAfAttributesPrint(" / %x (%x) / %p / %p / ", metaData->attributeType, emberAfAttributeSize(metaData),
296 (emberAfAttributeIsReadOnly(metaData) ? "RO" : "RW"),
297 (emberAfAttributeIsTokenized(metaData)
299 : (emberAfAttributeIsExternal(metaData) ? "extern " : " RAM ")));
300 emberAfAttributesFlush();
301 status = emAfReadAttribute(ep->endpoint, cluster->clusterId, metaData->attributeId,
302 (emberAfAttributeIsClient(metaData) ? CLUSTER_MASK_CLIENT : CLUSTER_MASK_SERVER),
303 mfgCode, data, ATTRIBUTE_LARGEST, NULL);
304 if (status == EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE)
306 emberAfAttributesPrintln("Unsupported");
311 if (emberAfIsStringAttributeType(metaData->attributeType))
313 length = static_cast<uint16_t>(emberAfStringLength(data) + 1);
315 else if (emberAfIsLongStringAttributeType(metaData->attributeType))
317 length = static_cast<uint16_t>(emberAfLongStringLength(data) + 2);
321 length = emberAfAttributeSize(metaData);
324 emberAfAttributesPrintBuffer(data, length, true);
325 emberAfAttributesFlush();
326 emberAfAttributeDecodeAndPrintCluster(cluster->clusterId, mfgCode);
330 emberAfAttributesFlush();
334 // given a clusterId and an attribute to read, this crafts the response
335 // and places it in the response buffer. Response is one of two items:
336 // 1) unsupported: [attrId:2] [status:1]
337 // 2) supported: [attrId:2] [status:1] [type:1] [data:n]
339 void emberAfRetrieveAttributeAndCraftResponse(EndpointId endpoint, ClusterId clusterId, AttributeId attrId, uint8_t mask,
340 uint16_t manufacturerCode, uint16_t readLength)
342 EmberAfStatus status;
343 uint8_t data[ATTRIBUTE_LARGEST];
347 // account for at least one byte of data
353 emberAfAttributesPrintln("OTA READ: ep:%x cid:%2x attid:%2x msk:%x mfcode:%2x", endpoint, clusterId, attrId, mask,
356 // lookup the attribute in our table
357 status = emAfReadAttribute(endpoint, clusterId, attrId, mask, manufacturerCode, data, ATTRIBUTE_LARGEST, &dataType);
358 if (status == EMBER_ZCL_STATUS_SUCCESS)
360 dataLen = emberAfAttributeValueSize(dataType, data);
361 if ((readLength - 4) < dataLen)
362 { // Not enough space for attribute.
368 emberAfPutInt16uInResp(attrId);
369 emberAfPutInt8uInResp(status);
370 emberAfAttributesPrintln("READ: clus %2x, attr %2x failed %x", clusterId, attrId, status);
371 emberAfAttributesFlush();
375 // put attribute in least sig byte first
376 emberAfPutInt16uInResp(attrId);
378 // attribute is found, so copy in the status and the data type
379 emberAfPutInt8uInResp(EMBER_ZCL_STATUS_SUCCESS);
380 emberAfPutInt8uInResp(dataType);
382 if (dataLen < (EMBER_AF_RESPONSE_BUFFER_LEN - appResponseLength))
385 // strings go over the air as length byte and then in human
386 // readable format. These should not be flipped. Other attributes
387 // need to be flipped so they go little endian OTA
388 if (isThisDataTypeSentLittleEndianOTA(dataType))
391 for (i = 0; i < dataLen; i++)
393 appResponseData[appResponseLength + i] = data[dataLen - i - 1];
398 memmove(&(appResponseData[appResponseLength]), data, dataLen);
400 #else //(BIGENDIAN_CPU)
401 memmove(&(appResponseData[appResponseLength]), data, dataLen);
402 #endif //(BIGENDIAN_CPU)
403 // TODO: How do we know this does not overflow?
404 appResponseLength = static_cast<uint16_t>(appResponseLength + dataLen);
407 emberAfAttributesPrintln("READ: clus %2x, attr %2x, dataLen: %x, OK", clusterId, attrId, dataLen);
408 emberAfAttributesFlush();
411 // This function appends the attribute report fields for the given endpoint,
412 // cluster, and attribute to the buffer starting at the index. If there is
413 // insufficient space in the buffer or an error occurs, buffer and bufIndex will
414 // remain unchanged. Otherwise, bufIndex will be incremented appropriately and
415 // the fields will be written to the buffer.
416 EmberAfStatus emberAfAppendAttributeReportFields(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId, uint8_t mask,
417 uint8_t * buffer, uint8_t bufLen, uint8_t * bufIndex)
419 EmberAfStatus status;
420 EmberAfAttributeType type;
422 uint16_t bufLen16 = (uint16_t) bufLen;
423 uint8_t data[ATTRIBUTE_LARGEST];
425 status = emberAfReadAttribute(endpoint, clusterId, attributeId, mask, data, sizeof(data), &type);
426 if (status != EMBER_ZCL_STATUS_SUCCESS)
431 size = emberAfAttributeValueSize(type, data);
432 if (bufLen16 - *bufIndex < 3 || size > bufLen16 - (*bufIndex + 3))
434 status = EMBER_ZCL_STATUS_INSUFFICIENT_SPACE;
438 buffer[(*bufIndex)++] = EMBER_LOW_BYTE(attributeId);
439 buffer[(*bufIndex)++] = EMBER_HIGH_BYTE(attributeId);
440 buffer[(*bufIndex)++] = type;
442 if (isThisDataTypeSentLittleEndianOTA(type))
444 emberReverseMemCopy(buffer + *bufIndex, data, size);
448 memmove(buffer + *bufIndex, data, size);
451 memmove(buffer + *bufIndex, data, size);
453 *bufIndex = static_cast<uint8_t>(*bufIndex + size);
456 emberAfAttributesPrintln("REPORT: clus 0x%2x, attr 0x%2x: 0x%x", clusterId, attributeId, status);
457 emberAfAttributesFlush();
462 //------------------------------------------------------------------------------
463 // Internal Functions
465 // writes an attribute (identified by clusterID and attrID to the given value.
467 // - EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE: if attribute isnt supported by the device (the
468 // device is not found in the attribute table)
469 // - EMBER_ZCL_STATUS_INVALID_DATA_TYPE: if the data type passed in doesnt match the type
470 // stored in the attribute table
471 // - EMBER_ZCL_STATUS_READ_ONLY: if the attribute isnt writable
472 // - EMBER_ZCL_STATUS_INVALID_VALUE: if the value is set out of the allowable range for
474 // - EMBER_ZCL_STATUS_SUCCESS: if the attribute was found and successfully written
476 // if true is passed in for overrideReadOnlyAndDataType then the data type is
477 // not checked and the read-only flag is ignored. This mode is meant for
478 // testing or setting the initial value of the attribute on the device.
480 // if true is passed for justTest, then the type is not written but all
481 // checks are done to see if the type could be written
482 // reads the attribute specified, returns false if the attribute is not in
483 // the table or the data is too large, returns true and writes to dataPtr
484 // if the attribute is supported and the readLength specified is less than
485 // the length of the data.
486 EmberAfStatus emAfWriteAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask,
487 uint16_t manufacturerCode, uint8_t * data, EmberAfAttributeType dataType,
488 bool overrideReadOnlyAndDataType, bool justTest)
490 EmberAfAttributeMetadata * metadata = NULL;
491 EmberAfAttributeSearchRecord record;
492 record.endpoint = endpoint;
493 record.clusterId = cluster;
494 record.clusterMask = mask;
495 record.attributeId = attributeID;
496 record.manufacturerCode = manufacturerCode;
497 emAfReadOrWriteAttribute(&record, &metadata,
502 // if we dont support that attribute
503 if (metadata == NULL)
505 emberAfAttributesPrintln("%pep %x clus %2x attr %2x not supported", "WRITE ERR: ", endpoint, cluster, attributeID);
506 emberAfAttributesFlush();
507 return EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE;
510 // if the data type specified by the caller is incorrect
511 if (!(overrideReadOnlyAndDataType))
513 if (dataType != metadata->attributeType)
515 emberAfAttributesPrintln("%pinvalid data type", "WRITE ERR: ");
516 emberAfAttributesFlush();
517 return EMBER_ZCL_STATUS_INVALID_DATA_TYPE;
520 if (emberAfAttributeIsReadOnly(metadata))
522 emberAfAttributesPrintln("%pattr not writable", "WRITE ERR: ");
523 emberAfAttributesFlush();
524 return EMBER_ZCL_STATUS_READ_ONLY;
528 // if the value the attribute is being set to is out of range
529 // return EMBER_ZCL_STATUS_INVALID_VALUE
530 if ((metadata->mask & ATTRIBUTE_MASK_MIN_MAX) != 0U)
532 EmberAfDefaultAttributeValue minv = metadata->defaultValue.ptrToMinMaxValue->minValue;
533 EmberAfDefaultAttributeValue maxv = metadata->defaultValue.ptrToMinMaxValue->maxValue;
534 bool isAttributeSigned = emberAfIsTypeSigned(metadata->attributeType);
535 uint8_t dataLen = emberAfAttributeSize(metadata);
539 uint8_t * minI = (uint8_t *) &(minv.defaultValue);
540 uint8_t * maxI = (uint8_t *) &(maxv.defaultValue);
541 // On big endian cpu with length 1 only the second byte counts
548 #endif // BIGENDIAN_CPU
549 minR = emberAfCompareValues(minI, data, dataLen, isAttributeSigned);
550 maxR = emberAfCompareValues(maxI, data, dataLen, isAttributeSigned);
551 if ((minR == 1) || (maxR == -1))
553 return EMBER_ZCL_STATUS_INVALID_VALUE;
558 if ((emberAfCompareValues(minv.ptrToDefaultValue, data, dataLen, isAttributeSigned) == 1) ||
559 (emberAfCompareValues(maxv.ptrToDefaultValue, data, dataLen, isAttributeSigned) == -1))
561 return EMBER_ZCL_STATUS_INVALID_VALUE;
566 // write the data unless this is only a test
569 // Pre write attribute callback for all attribute changes,
570 // regardless of cluster.
571 EmberAfStatus status = emberAfPreAttributeChangeCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataType,
572 emberAfAttributeSize(metadata), data);
573 if (status != EMBER_ZCL_STATUS_SUCCESS)
578 // Pre-write attribute callback specific
579 // to the cluster that the attribute lives in.
580 status = emAfClusterPreAttributeChangedCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataType,
581 emberAfAttributeSize(metadata), data);
582 if (status != EMBER_ZCL_STATUS_SUCCESS)
587 // write the attribute
588 status = emAfReadOrWriteAttribute(&record,
591 0, // buffer size - unused
594 if (status != EMBER_ZCL_STATUS_SUCCESS)
599 // Save the attribute to token if needed
600 // Function itself will weed out tokens that are not tokenized.
601 emAfSaveAttributeToToken(data, endpoint, cluster, metadata);
603 emberAfReportingAttributeChangeCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataType, data);
605 // Post write attribute callback for all attributes changes, regardless
607 emberAfPostAttributeChangeCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataType,
608 emberAfAttributeSize(metadata), data);
610 // Post-write attribute callback specific
611 // to the cluster that the attribute lives in.
612 emAfClusterAttributeChangedCallback(endpoint, cluster, attributeID, mask, manufacturerCode);
616 // bug: 11618, we are not handling properly external attributes
617 // in this case... We need to do something. We don't really
618 // know if it will succeed.
619 emberAfAttributesPrintln("WRITE: no write, just a test");
620 emberAfAttributesFlush();
623 return EMBER_ZCL_STATUS_SUCCESS;
626 // If dataPtr is NULL, no data is copied to the caller.
627 // readLength should be 0 in that case.
629 EmberAfStatus emAfReadAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask,
630 uint16_t manufacturerCode, uint8_t * dataPtr, uint16_t readLength, EmberAfAttributeType * dataType)
632 EmberAfAttributeMetadata * metadata = NULL;
633 EmberAfAttributeSearchRecord record;
634 EmberAfStatus status;
635 record.endpoint = endpoint;
636 record.clusterId = cluster;
637 record.clusterMask = mask;
638 record.attributeId = attributeID;
639 record.manufacturerCode = manufacturerCode;
640 status = emAfReadOrWriteAttribute(&record, &metadata, dataPtr, readLength,
643 if (status == EMBER_ZCL_STATUS_SUCCESS)
645 // It worked! If the user asked for the type, set it before returning.
646 if (dataType != NULL)
648 (*dataType) = metadata->attributeType;
652 { // failed, print debug info
653 if (status == EMBER_ZCL_STATUS_INSUFFICIENT_SPACE)
655 emberAfAttributesPrintln("READ: attribute size too large for caller");
656 emberAfAttributesFlush();