blob: 2ea8bc6fd51e1239c45404cb00109dc7e4fec5fd [file] [log] [blame]
/**
*
* 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.
*/
/**
*
* Copyright (c) 2020 Silicon Labs
*
* 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
* @brief This file contains the code to manipulate
*the Smart Energy attribute table. This handles
*external calls to read/write the table, as well as
*internal ones.
*******************************************************************************
******************************************************************************/
// this file contains all the common includes for clusters in the zcl-util
#include <app/util/attribute-storage.h>
// for pulling in defines dealing with EITHER server or client
#include "app/util/common.h"
#include <app-common/zap-generated/callback.h>
#include <app/util/af-main.h>
#include <app/reporting/reporting.h>
using namespace chip;
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// External Declarations
//------------------------------------------------------------------------------
// Forward Declarations
//------------------------------------------------------------------------------
// Globals
EmberAfStatus emberAfWriteAttributeExternal(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask,
uint16_t manufacturerCode, uint8_t * dataPtr, EmberAfAttributeType dataType)
{
EmberAfAttributeWritePermission extWritePermission =
emberAfAllowNetworkWriteAttributeCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataPtr, dataType);
switch (extWritePermission)
{
case EMBER_ZCL_ATTRIBUTE_WRITE_PERMISSION_DENY_WRITE:
return EMBER_ZCL_STATUS_FAILURE;
case EMBER_ZCL_ATTRIBUTE_WRITE_PERMISSION_ALLOW_WRITE_NORMAL:
case EMBER_ZCL_ATTRIBUTE_WRITE_PERMISSION_ALLOW_WRITE_OF_READ_ONLY:
return emAfWriteAttribute(endpoint, cluster, attributeID, mask, manufacturerCode, dataPtr, dataType,
(extWritePermission == EMBER_ZCL_ATTRIBUTE_WRITE_PERMISSION_ALLOW_WRITE_OF_READ_ONLY), false);
default:
return (EmberAfStatus) extWritePermission;
}
}
//@deprecated use emberAfWriteServerAttribute or emberAfWriteClientAttribute
EmberAfStatus emberAfWriteAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask,
uint8_t * dataPtr, EmberAfAttributeType dataType)
{
return emAfWriteAttribute(endpoint, cluster, attributeID, mask, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr, dataType,
true, // override read-only?
false); // just test?
}
EmberAfStatus emberAfWriteClientAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr,
EmberAfAttributeType dataType)
{
return emAfWriteAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_CLIENT, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr,
dataType,
true, // override read-only?
false); // just test?
}
EmberAfStatus emberAfWriteServerAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr,
EmberAfAttributeType dataType)
{
return emAfWriteAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_SERVER, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr,
dataType,
true, // override read-only?
false); // just test?
}
EmberAfStatus emberAfWriteManufacturerSpecificClientAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID,
uint16_t manufacturerCode, uint8_t * dataPtr,
EmberAfAttributeType dataType)
{
return emAfWriteAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_CLIENT, manufacturerCode, dataPtr, dataType,
true, // override read-only?
false); // just test?
}
EmberAfStatus emberAfWriteManufacturerSpecificServerAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID,
uint16_t manufacturerCode, uint8_t * dataPtr,
EmberAfAttributeType dataType)
{
return emAfWriteAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_SERVER, manufacturerCode, dataPtr, dataType,
true, // override read-only?
false); // just test?
}
EmberAfStatus emberAfVerifyAttributeWrite(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask,
uint16_t manufacturerCode, uint8_t * dataPtr, EmberAfAttributeType dataType)
{
return emAfWriteAttribute(endpoint, cluster, attributeID, mask, manufacturerCode, dataPtr, dataType,
false, // override read-only?
true); // just test?
}
EmberAfStatus emberAfReadAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask, uint8_t * dataPtr,
uint16_t readLength, EmberAfAttributeType * dataType)
{
return emAfReadAttribute(endpoint, cluster, attributeID, mask, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr, readLength, dataType);
}
EmberAfStatus emberAfReadServerAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr,
uint16_t readLength)
{
return emAfReadAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_SERVER, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr,
readLength, NULL);
}
EmberAfStatus emberAfReadClientAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr,
uint16_t readLength)
{
return emAfReadAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_CLIENT, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr,
readLength, NULL);
}
EmberAfStatus emberAfReadManufacturerSpecificServerAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID,
uint16_t manufacturerCode, uint8_t * dataPtr, uint16_t readLength)
{
return emAfReadAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_SERVER, manufacturerCode, dataPtr, readLength, NULL);
}
EmberAfStatus emberAfReadManufacturerSpecificClientAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID,
uint16_t manufacturerCode, uint8_t * dataPtr, uint16_t readLength)
{
return emAfReadAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_CLIENT, manufacturerCode, dataPtr, readLength, NULL);
}
bool emberAfReadSequentialAttributesAddToResponse(EndpointId endpoint, ClusterId clusterId, AttributeId startAttributeId,
uint8_t mask, uint16_t manufacturerCode, uint8_t maxAttributeIds,
bool includeAccessControl)
{
uint16_t i;
uint16_t discovered = 0;
uint16_t skipped = 0;
uint16_t total = 0;
EmberAfCluster * cluster = emberAfFindClusterWithMfgCode(endpoint, clusterId, mask, manufacturerCode);
EmberAfAttributeSearchRecord record;
record.endpoint = endpoint;
record.clusterId = clusterId;
record.clusterMask = mask;
record.attributeId = startAttributeId;
record.manufacturerCode = manufacturerCode;
// If we don't have the cluster or it doesn't match the search, we're done.
if (cluster == NULL || !emAfMatchCluster(cluster, &record))
{
return true;
}
for (i = 0; i < cluster->attributeCount; i++)
{
EmberAfAttributeMetadata * metadata = &cluster->attributes[i];
// If the cluster is not manufacturer-specific, an attribute is considered
// only if its manufacturer code matches that of the command (which may be
// unset).
if (!emberAfClusterIsManufacturerSpecific(cluster))
{
record.attributeId = metadata->attributeId;
if (!emAfMatchAttribute(cluster, metadata, &record))
{
continue;
}
}
if (metadata->attributeId < startAttributeId)
{
skipped++;
}
else if (discovered < maxAttributeIds)
{
emberAfPutInt32uInResp(metadata->attributeId);
emberAfPutInt8uInResp(metadata->attributeType);
if (includeAccessControl)
{
// bit 0 : Readable <-- All our attributes are readable
// bit 1 : Writable <-- The only thing we track in the attribute metadata mask
// bit 2 : Reportable <-- All our attributes are reportable
emberAfPutInt8uInResp((metadata->mask & ATTRIBUTE_MASK_WRITABLE) ? 0x07 : 0x05);
}
discovered++;
}
else
{
// MISRA requires ..else if.. to have terminating else.
}
total++;
}
// We are finished if there are no more attributes to find, which means the
// number of attributes discovered plus the number skipped equals the total
// attributes in the cluster. For manufacturer-specific clusters, the total
// includes all attributes in the cluster. For standard ZCL clusters, if the
// the manufacturer code is set, the total is the number of attributes that
// match the manufacturer code. Otherwise, the total is the number of
// standard ZCL attributes in the cluster.
return (discovered + skipped == total);
}
static void emberAfAttributeDecodeAndPrintCluster(ClusterId cluster, uint16_t mfgCode)
{
#if defined(EMBER_AF_PRINT_ENABLE) && defined(EMBER_AF_PRINT_ATTRIBUTES)
uint16_t index = emberAfFindClusterNameIndexWithMfgCode(cluster, mfgCode);
if (index != 0xFFFF)
{
emberAfAttributesPrintln("(%p)", zclClusterNames[index].name);
}
emberAfAttributesFlush();
#endif // defined(EMBER_AF_PRINT_ENABLE) && defined(EMBER_AF_PRINT_ATTRIBUTES)
}
void emberAfPrintAttributeTable(void)
{
uint8_t data[ATTRIBUTE_LARGEST];
decltype(emberAfEndpointCount()) endpointIndex;
decltype(EmberAfEndpointType::clusterCount) clusterIndex;
uint16_t attributeIndex;
EmberAfStatus status;
uint16_t mfgCode;
for (endpointIndex = 0; endpointIndex < emberAfEndpointCount(); endpointIndex++)
{
EmberAfDefinedEndpoint * ep = &(emAfEndpoints[endpointIndex]);
emberAfAttributesPrintln("ENDPOINT %x", ep->endpoint);
emberAfAttributesPrintln("clus / side / attr / mfg /type(len)/ rw / storage / data (raw)");
emberAfAttributesFlush();
for (clusterIndex = 0; clusterIndex < ep->endpointType->clusterCount; clusterIndex++)
{
EmberAfCluster * cluster = &(ep->endpointType->cluster[clusterIndex]);
for (attributeIndex = 0; attributeIndex < cluster->attributeCount; attributeIndex++)
{
EmberAfAttributeMetadata * metaData = &(cluster->attributes[attributeIndex]);
// Depending on user config, this loop can take a very long time to
// run and watchdog reset will kick in. As a workaround, we'll
// manually reset the watchdog.
// halResetWatchdog();
emberAfAttributesPrint("%2x / %p / %2x / ", cluster->clusterId,
(emberAfAttributeIsClient(metaData) ? "clnt" : "srvr"), metaData->attributeId);
mfgCode = emAfGetManufacturerCodeForAttribute(cluster, metaData);
if (mfgCode == EMBER_AF_NULL_MANUFACTURER_CODE)
{
emberAfAttributesPrint("----");
}
else
{
emberAfAttributesPrint("%2x", mfgCode);
}
emberAfAttributesPrint(" / %x (%x) / %p / %p / ", metaData->attributeType, emberAfAttributeSize(metaData),
(emberAfAttributeIsReadOnly(metaData) ? "RO" : "RW"),
(emberAfAttributeIsTokenized(metaData)
? " token "
: (emberAfAttributeIsExternal(metaData) ? "extern " : " RAM ")));
emberAfAttributesFlush();
status = emAfReadAttribute(ep->endpoint, cluster->clusterId, metaData->attributeId,
(emberAfAttributeIsClient(metaData) ? CLUSTER_MASK_CLIENT : CLUSTER_MASK_SERVER),
mfgCode, data, ATTRIBUTE_LARGEST, NULL);
if (status == EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE)
{
emberAfAttributesPrintln("Unsupported");
}
else
{
uint16_t length;
if (emberAfIsStringAttributeType(metaData->attributeType))
{
length = static_cast<uint16_t>(emberAfStringLength(data) + 1);
}
else if (emberAfIsLongStringAttributeType(metaData->attributeType))
{
length = static_cast<uint16_t>(emberAfLongStringLength(data) + 2);
}
else
{
length = emberAfAttributeSize(metaData);
}
UNUSED_VAR(length);
emberAfAttributesPrintBuffer(data, length, true);
emberAfAttributesFlush();
emberAfAttributeDecodeAndPrintCluster(cluster->clusterId, mfgCode);
}
}
}
emberAfAttributesFlush();
}
}
// given a clusterId and an attribute to read, this crafts the response
// and places it in the response buffer. Response is one of two items:
// 1) unsupported: [attrId:2] [status:1]
// 2) supported: [attrId:2] [status:1] [type:1] [data:n]
//
void emberAfRetrieveAttributeAndCraftResponse(EndpointId endpoint, ClusterId clusterId, AttributeId attrId, uint8_t mask,
uint16_t manufacturerCode, uint16_t readLength)
{
EmberAfStatus status;
uint8_t data[ATTRIBUTE_LARGEST];
uint8_t dataType;
uint16_t dataLen;
// account for at least one byte of data
if (readLength < 5)
{
return;
}
emberAfAttributesPrintln("OTA READ: ep:%x cid:%2x attid:%2x msk:%x mfcode:%2x", endpoint, clusterId, attrId, mask,
manufacturerCode);
// lookup the attribute in our table
status = emAfReadAttribute(endpoint, clusterId, attrId, mask, manufacturerCode, data, ATTRIBUTE_LARGEST, &dataType);
if (status == EMBER_ZCL_STATUS_SUCCESS)
{
dataLen = emberAfAttributeValueSize(clusterId, attrId, dataType, data);
if ((readLength - 4) < dataLen)
{ // Not enough space for attribute.
return;
}
}
else
{
emberAfPutInt32uInResp(attrId);
emberAfPutStatusInResp(status);
emberAfAttributesPrintln("READ: clus %2x, attr %2x failed %x", clusterId, attrId, status);
emberAfAttributesFlush();
return;
}
// put attribute in least sig byte first
emberAfPutInt32uInResp(attrId);
// attribute is found, so copy in the status and the data type
emberAfPutInt8uInResp(EMBER_ZCL_STATUS_SUCCESS);
emberAfPutInt8uInResp(dataType);
if (dataLen < (EMBER_AF_RESPONSE_BUFFER_LEN - appResponseLength))
{
#if (BIGENDIAN_CPU)
// strings go over the air as length byte and then in human
// readable format. These should not be flipped. Other attributes
// need to be flipped so they go little endian OTA
if (isThisDataTypeSentLittleEndianOTA(dataType))
{
uint8_t i;
for (i = 0; i < dataLen; i++)
{
appResponseData[appResponseLength + i] = data[dataLen - i - 1];
}
}
else
{
memmove(&(appResponseData[appResponseLength]), data, dataLen);
}
#else //(BIGENDIAN_CPU)
memmove(&(appResponseData[appResponseLength]), data, dataLen);
#endif //(BIGENDIAN_CPU)
// TODO: How do we know this does not overflow?
appResponseLength = static_cast<uint16_t>(appResponseLength + dataLen);
}
emberAfAttributesPrintln("READ: clus %2x, attr %2x, dataLen: %x, OK", clusterId, attrId, dataLen);
emberAfAttributesFlush();
}
// This function appends the attribute report fields for the given endpoint,
// cluster, and attribute to the buffer starting at the index. If there is
// insufficient space in the buffer or an error occurs, buffer and bufIndex will
// remain unchanged. Otherwise, bufIndex will be incremented appropriately and
// the fields will be written to the buffer.
EmberAfStatus emberAfAppendAttributeReportFields(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId, uint8_t mask,
uint8_t * buffer, uint8_t bufLen, uint8_t * bufIndex)
{
EmberAfStatus status;
EmberAfAttributeType type;
uint16_t size;
uint16_t bufLen16 = (uint16_t) bufLen;
uint8_t data[ATTRIBUTE_LARGEST];
status = emberAfReadAttribute(endpoint, clusterId, attributeId, mask, data, sizeof(data), &type);
if (status != EMBER_ZCL_STATUS_SUCCESS)
{
goto kickout;
}
size = emberAfAttributeValueSize(clusterId, attributeId, type, data);
if (bufLen16 - *bufIndex < 3 || size > bufLen16 - (*bufIndex + 3))
{
status = EMBER_ZCL_STATUS_INSUFFICIENT_SPACE;
goto kickout;
}
buffer[(*bufIndex)++] = EMBER_LOW_BYTE(attributeId);
buffer[(*bufIndex)++] = EMBER_HIGH_BYTE(attributeId);
buffer[(*bufIndex)++] = type;
#if (BIGENDIAN_CPU)
if (isThisDataTypeSentLittleEndianOTA(type))
{
emberReverseMemCopy(buffer + *bufIndex, data, size);
}
else
{
memmove(buffer + *bufIndex, data, size);
}
#else
memmove(buffer + *bufIndex, data, size);
#endif
*bufIndex = static_cast<uint8_t>(*bufIndex + size);
kickout:
emberAfAttributesPrintln("REPORT: clus 0x%2x, attr 0x%2x: 0x%x", clusterId, attributeId, status);
emberAfAttributesFlush();
return status;
}
//------------------------------------------------------------------------------
// Internal Functions
// writes an attribute (identified by clusterID and attrID to the given value.
// this returns:
// - EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE: if attribute isnt supported by the device (the
// device is not found in the attribute table)
// - EMBER_ZCL_STATUS_INVALID_DATA_TYPE: if the data type passed in doesnt match the type
// stored in the attribute table
// - EMBER_ZCL_STATUS_READ_ONLY: if the attribute isnt writable
// - EMBER_ZCL_STATUS_INVALID_VALUE: if the value is set out of the allowable range for
// the attribute
// - EMBER_ZCL_STATUS_SUCCESS: if the attribute was found and successfully written
//
// if true is passed in for overrideReadOnlyAndDataType then the data type is
// not checked and the read-only flag is ignored. This mode is meant for
// testing or setting the initial value of the attribute on the device.
//
// if true is passed for justTest, then the type is not written but all
// checks are done to see if the type could be written
// reads the attribute specified, returns false if the attribute is not in
// the table or the data is too large, returns true and writes to dataPtr
// if the attribute is supported and the readLength specified is less than
// the length of the data.
EmberAfStatus emAfWriteAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask,
uint16_t manufacturerCode, uint8_t * data, EmberAfAttributeType dataType,
bool overrideReadOnlyAndDataType, bool justTest)
{
EmberAfAttributeMetadata * metadata = NULL;
EmberAfAttributeSearchRecord record;
record.endpoint = endpoint;
record.clusterId = cluster;
record.clusterMask = mask;
record.attributeId = attributeID;
record.manufacturerCode = manufacturerCode;
emAfReadOrWriteAttribute(&record, &metadata,
NULL, // buffer
0, // buffer size
false); // write?
// if we dont support that attribute
if (metadata == NULL)
{
emberAfAttributesPrintln("%pep %x clus %2x attr %2x not supported", "WRITE ERR: ", endpoint, cluster, attributeID);
emberAfAttributesFlush();
return EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE;
}
// if the data type specified by the caller is incorrect
if (!(overrideReadOnlyAndDataType))
{
if (dataType != metadata->attributeType)
{
emberAfAttributesPrintln("%pinvalid data type", "WRITE ERR: ");
emberAfAttributesFlush();
return EMBER_ZCL_STATUS_INVALID_DATA_TYPE;
}
if (emberAfAttributeIsReadOnly(metadata))
{
emberAfAttributesPrintln("%pattr not writable", "WRITE ERR: ");
emberAfAttributesFlush();
return EMBER_ZCL_STATUS_READ_ONLY;
}
}
// if the value the attribute is being set to is out of range
// return EMBER_ZCL_STATUS_INVALID_VALUE
if ((metadata->mask & ATTRIBUTE_MASK_MIN_MAX) != 0U)
{
EmberAfDefaultAttributeValue minv = metadata->defaultValue.ptrToMinMaxValue->minValue;
EmberAfDefaultAttributeValue maxv = metadata->defaultValue.ptrToMinMaxValue->maxValue;
bool isAttributeSigned = emberAfIsTypeSigned(metadata->attributeType);
uint16_t dataLen = emberAfAttributeSize(metadata);
if (dataLen <= 2)
{
int8_t minR, maxR;
uint8_t * minI = (uint8_t *) &(minv.defaultValue);
uint8_t * maxI = (uint8_t *) &(maxv.defaultValue);
// On big endian cpu with length 1 only the second byte counts
#if (BIGENDIAN_CPU)
if (dataLen == 1)
{
minI++;
maxI++;
}
#endif // BIGENDIAN_CPU
minR = emberAfCompareValues(minI, data, dataLen, isAttributeSigned);
maxR = emberAfCompareValues(maxI, data, dataLen, isAttributeSigned);
if ((minR == 1) || (maxR == -1))
{
return EMBER_ZCL_STATUS_INVALID_VALUE;
}
}
else
{
if ((emberAfCompareValues(minv.ptrToDefaultValue, data, dataLen, isAttributeSigned) == 1) ||
(emberAfCompareValues(maxv.ptrToDefaultValue, data, dataLen, isAttributeSigned) == -1))
{
return EMBER_ZCL_STATUS_INVALID_VALUE;
}
}
}
// write the data unless this is only a test
if (!justTest)
{
// Pre write attribute callback for all attribute changes,
// regardless of cluster.
EmberAfStatus status = emberAfPreAttributeChangeCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataType,
emberAfAttributeSize(metadata), data);
if (status != EMBER_ZCL_STATUS_SUCCESS)
{
return status;
}
// Pre-write attribute callback specific
// to the cluster that the attribute lives in.
status = emAfClusterPreAttributeChangedCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataType,
emberAfAttributeSize(metadata), data);
if (status != EMBER_ZCL_STATUS_SUCCESS)
{
return status;
}
// write the attribute
status = emAfReadOrWriteAttribute(&record,
NULL, // metadata
data,
0, // buffer size - unused
true); // write?
if (status != EMBER_ZCL_STATUS_SUCCESS)
{
return status;
}
// Save the attribute to token if needed
// Function itself will weed out tokens that are not tokenized.
emAfSaveAttributeToToken(data, endpoint, cluster, metadata);
InteractionModelReportingAttributeChangeCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataType, data);
// Post write attribute callback for all attributes changes, regardless
// of cluster.
emberAfPostAttributeChangeCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataType,
emberAfAttributeSize(metadata), data);
// Post-write attribute callback specific
// to the cluster that the attribute lives in.
emAfClusterAttributeChangedCallback(endpoint, cluster, attributeID, mask, manufacturerCode);
}
else
{
// bug: 11618, we are not handling properly external attributes
// in this case... We need to do something. We don't really
// know if it will succeed.
emberAfAttributesPrintln("WRITE: no write, just a test");
emberAfAttributesFlush();
}
return EMBER_ZCL_STATUS_SUCCESS;
}
// If dataPtr is NULL, no data is copied to the caller.
// readLength should be 0 in that case.
EmberAfStatus emAfReadAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask,
uint16_t manufacturerCode, uint8_t * dataPtr, uint16_t readLength, EmberAfAttributeType * dataType)
{
EmberAfAttributeMetadata * metadata = NULL;
EmberAfAttributeSearchRecord record;
EmberAfStatus status;
record.endpoint = endpoint;
record.clusterId = cluster;
record.clusterMask = mask;
record.attributeId = attributeID;
record.manufacturerCode = manufacturerCode;
status = emAfReadOrWriteAttribute(&record, &metadata, dataPtr, readLength,
false); // write?
if (status == EMBER_ZCL_STATUS_SUCCESS)
{
// It worked! If the user asked for the type, set it before returning.
if (dataType != NULL)
{
(*dataType) = metadata->attributeType;
}
}
else
{ // failed, print debug info
if (status == EMBER_ZCL_STATUS_INSUFFICIENT_SPACE)
{
emberAfAttributesPrintln("READ: attribute size too large for caller");
emberAfAttributesFlush();
}
}
return status;
}