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 function that processes
39 *******************************************************************************
40 ******************************************************************************/
44 #include <app/clusters/ias-zone-client/ias-zone-client.h>
45 #include <app/reporting/reporting.h>
46 #include <app/util/common.h>
48 #include "gen/attribute-id.h"
49 #include "gen/attribute-type.h"
50 #include "gen/callback.h"
51 #include "gen/cluster-id.h"
52 #include "gen/command-id.h"
54 #ifdef EMBER_AF_PLUGIN_COMMS_HUB_FUNCTION_SUB_GHZ
55 #include "app/framework/plugin/comms-hub-function-sub-ghz/comms-hub-function-sub-ghz.h"
58 #include <support/CodeUtils.h>
62 // flag to keep track of the fact that we just sent a read attr for time and
63 // we should set our time to the result of the read attr response.
64 bool emAfSyncingTime = false;
66 #ifdef EMBER_AF_GBCS_COMPATIBLE
67 // Some GBCS use cases (e.g. GCS15e, GCS21f) require that ReadAttributesResponse
68 // should be send back with Disable Default Response flag set. The only pattern
69 // is that the decision is based on the cluster and attribute IDs requested.
70 // To reduce the possibility of false positives, we disable default response
71 // only for responses containing at least the specified minimum of attributes.
72 #define MIN_MATCHING_ATTR_IDS_TO_DISABLE_DEFAULT_RESPONSE 3
75 #define DISC_ATTR_RSP_MAX_ATTRIBUTES \
76 (((EMBER_AF_MAXIMUM_APS_PAYLOAD_LENGTH - EMBER_AF_ZCL_MANUFACTURER_SPECIFIC_OVERHEAD /* max ZCL header size */ \
77 - 1) /* discovery is complete boolean */ \
78 / 3) /* size of one discover attributes response entry */ \
79 % UINT8_MAX) /* make count fit in an 8 bit integer */
80 #define DISC_ATTR_EXT_RSP_MAX_ATTRIBUTES \
81 (((EMBER_AF_MAXIMUM_APS_PAYLOAD_LENGTH - EMBER_AF_ZCL_MANUFACTURER_SPECIFIC_OVERHEAD /* max ZCL header size */ \
82 - 1) /* discovery is complete boolean */ \
83 / 4) /* size of one discover attributes extended response entry */ \
84 % UINT8_MAX) /* make count fit in an 8 bit integer */
86 #if defined(EMBER_AF_SUPPORT_COMMAND_DISCOVERY)
87 static void printDiscoverCommandsResponse(bool generated, ClusterId clusterId, bool discoveryComplete, uint8_t * buffer,
91 emberAfServiceDiscoveryPrint("Discover Commands response (complete: %c), %p IDs: ", (discoveryComplete ? 'y' : 'n'),
92 (generated ? "Generated" : "Received"));
93 for (i = 0; i < length; i++)
95 emberAfServiceDiscoveryPrint("0x%X ", buffer[i]);
97 emberAfServiceDiscoveryPrintln("");
101 bool emAfProcessGlobalCommand(EmberAfClusterCommand * cmd)
104 uint8_t frameControl;
105 // This is a little clumsy but easier to read and port
106 // from earlier implementation.
107 ClusterId clusterId = cmd->apsFrame->clusterId;
108 CommandId zclCmd = cmd->commandId;
109 uint8_t * message = cmd->buffer;
110 uint16_t msgLen = cmd->bufLen;
111 uint16_t msgIndex = cmd->payloadStartIndex;
112 uint8_t clientServerMask = (cmd->direction == ZCL_DIRECTION_CLIENT_TO_SERVER ? CLUSTER_MASK_SERVER : CLUSTER_MASK_CLIENT);
114 // If we are disabled then we can only respond to read or write commands
115 // or identify cluster (see device enabled attr of basic cluster)
116 if (!emberAfIsDeviceEnabled(cmd->apsFrame->destinationEndpoint) && zclCmd != ZCL_READ_ATTRIBUTES_COMMAND_ID &&
117 zclCmd != ZCL_WRITE_ATTRIBUTES_COMMAND_ID && zclCmd != ZCL_WRITE_ATTRIBUTES_UNDIVIDED_COMMAND_ID &&
118 zclCmd != ZCL_WRITE_ATTRIBUTES_NO_RESPONSE_COMMAND_ID && clusterId != ZCL_IDENTIFY_CLUSTER_ID)
120 emberAfCorePrintln("disabled");
121 emberAfDebugPrintln("%pd, dropping global cmd:%x", "disable", zclCmd);
122 emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_FAILURE);
126 // If a manufacturer-specific command arrives using our special internal "not
127 // manufacturer specific" code, we need to reject it outright without letting
128 // it pass through to the rest of the code. The internal read and write APIs
129 // would interpret it as a standard attribute or cluster and return incorrect
131 if (cmd->mfgSpecific && cmd->mfgCode == EMBER_AF_NULL_MANUFACTURER_CODE)
136 // Clear out the response buffer by setting its length to zero
137 appResponseLength = 0;
139 // Make the ZCL header for the response
140 // note: cmd byte is set below
141 frameControl = static_cast<uint8_t>(ZCL_GLOBAL_COMMAND |
142 (cmd->direction == ZCL_DIRECTION_CLIENT_TO_SERVER
143 ? ZCL_FRAME_CONTROL_SERVER_TO_CLIENT | EMBER_AF_DEFAULT_RESPONSE_POLICY_RESPONSES
144 : ZCL_FRAME_CONTROL_CLIENT_TO_SERVER | EMBER_AF_DEFAULT_RESPONSE_POLICY_RESPONSES));
145 if (cmd->mfgSpecific)
147 frameControl |= ZCL_MANUFACTURER_SPECIFIC_MASK;
149 emberAfPutInt8uInResp(frameControl);
150 if (cmd->mfgSpecific)
152 emberAfPutInt16uInResp(cmd->mfgCode);
154 emberAfPutInt8uInResp(cmd->seqNum);
158 // The format of the read attributes cmd is:
160 // The format of the read attributes response is:
161 // ([attr ID:2] [status:1] [data type:0/1] [data:0/N]) * N
162 case ZCL_READ_ATTRIBUTES_COMMAND_ID: {
163 emberAfAttributesPrintln("%p: clus %2x", "READ_ATTR", clusterId);
164 // Set the cmd byte - this is byte 3 index 2, but since we have
165 // already incremented past the 3 byte ZCL header (our index is at 3),
166 // this gets written to "-1" since 3 - 1 = 2.
167 emberAfPutInt8uInResp(ZCL_READ_ATTRIBUTES_RESPONSE_COMMAND_ID);
169 // This message contains N 2-byte attr IDs after the 3 byte ZCL header,
170 // for each one we need to look it up and make a response
171 while (msgIndex + 2 <= msgLen)
173 // Get the attribute ID and store it in the response buffer
174 // least significant byte is first OTA
175 attrId = emberAfGetInt16u(message, msgIndex, msgLen);
177 #ifdef EMBER_AF_GBCS_COMPATIBLE
178 // GBCS explicitly lists some commands that need to be sent with "disable
179 // default response" flag set, including some ReadAttributes responses.
180 // We make it conditional on GBCS so it does not affect standard SE apps.
186 } noDefaultResponseSet[] = {
187 { ZCL_PRICE_CLUSTER_ID, ZCL_THRESHOLD_MULTIPLIER_ATTRIBUTE_ID },
188 { ZCL_PRICE_CLUSTER_ID, ZCL_THRESHOLD_DIVISOR_ATTRIBUTE_ID },
189 { ZCL_PRICE_CLUSTER_ID, ZCL_STANDING_CHARGE_ATTRIBUTE_ID },
190 { ZCL_PRICE_CLUSTER_ID, ZCL_TARIFF_UNIT_OF_MEASURE_ATTRIBUTE_ID },
191 { ZCL_SIMPLE_METERING_CLUSTER_ID, ZCL_UNIT_OF_MEASURE_ATTRIBUTE_ID },
192 { ZCL_SIMPLE_METERING_CLUSTER_ID, ZCL_MULTIPLIER_ATTRIBUTE_ID },
193 { ZCL_SIMPLE_METERING_CLUSTER_ID, ZCL_DIVISOR_ATTRIBUTE_ID },
196 uint8_t foundMatchingAttrIdsCount = 0;
198 for (i = 0; i < sizeof noDefaultResponseSet / sizeof noDefaultResponseSet[0]; ++i)
200 if (noDefaultResponseSet[i].clusterId == clusterId && noDefaultResponseSet[i].attrId == attrId)
202 if (++foundMatchingAttrIdsCount >= MIN_MATCHING_ATTR_IDS_TO_DISABLE_DEFAULT_RESPONSE)
204 emberAfSetDisableDefaultResponse(EMBER_AF_DISABLE_DEFAULT_RESPONSE_ONE_SHOT);
211 #ifdef EMBER_AF_PLUGIN_COMMS_HUB_FUNCTION_SUB_GHZ
212 // This plugin sets channel change notification flags and needs to know
213 // when those flags have been read.
214 if (clientServerMask == CLUSTER_MASK_SERVER)
216 emAfCommsHubFunctionSubGhzReadAttributeNotification(cmd->source, clusterId, attrId);
221 // This function reads the attribute and creates the correct response
222 // in the response buffer
223 emberAfRetrieveAttributeAndCraftResponse(cmd->apsFrame->destinationEndpoint, clusterId, attrId, clientServerMask,
225 static_cast<uint16_t>(EMBER_AF_RESPONSE_BUFFER_LEN - appResponseLength));
227 msgIndex = static_cast<uint16_t>(msgIndex + 2);
231 emberAfSendResponse();
234 // Write undivided means all attributes must be written in order to write
235 // any of them. So first do a check. If the check fails, send back a fail
236 // response. If it works, fall through to the normal write attr code.
237 // write attr responses are the same for undivided and normal writes.
238 case ZCL_WRITE_ATTRIBUTES_UNDIVIDED_COMMAND_ID: {
239 uint8_t numFailures = 0;
242 EmberAfStatus status;
244 emberAfPutInt8uInResp(ZCL_WRITE_ATTRIBUTES_RESPONSE_COMMAND_ID);
246 // Go through the message until there are no more attrID/type/data
247 while (msgIndex < msgLen - 3)
249 attrId = emberAfGetInt16u(message, msgIndex, msgLen);
250 dataType = emberAfGetInt8u(message, msgIndex + 2, msgLen);
252 dataSize = emberAfAttributeValueSize(dataType, message + msgIndex + 3);
254 // Check to see if there are dataSize bytes left in the message if it is a string
255 if (emberAfIsThisDataTypeAStringType(dataType) && (dataSize < msgLen - (msgIndex + 3)))
257 // This command is malformed
258 status = EMBER_ZCL_STATUS_MALFORMED_COMMAND;
262 status = emberAfVerifyAttributeWrite(cmd->apsFrame->destinationEndpoint, clusterId, attrId, clientServerMask,
263 cmd->mfgCode, &(message[msgIndex + 3]), dataType);
266 if (status != EMBER_ZCL_STATUS_SUCCESS)
269 // Write to the response buffer - status and then attrID
270 emberAfPutInt8uInResp(status);
271 emberAfPutInt16uInResp(attrId);
273 emberAfAttributesPrintln("WRITE: clus %2x attr %2x ", clusterId, attrId);
274 emberAfAttributesPrintln("FAIL %x", status);
276 if (status == EMBER_ZCL_STATUS_MALFORMED_COMMAND)
278 // this attribute is malformed, terminate attribute processing.
283 // Increment past the attribute id (two bytes), the type (one byte), and
284 // the data (N bytes, including the length byte for strings).
285 msgIndex = static_cast<uint16_t>(msgIndex + 3 + dataSize);
287 // If there are any failures, send the response and exit
290 emberAfSendResponse();
294 // Reset message back to start
295 msgIndex = cmd->payloadStartIndex;
296 appResponseLength = (cmd->mfgSpecific ? 4 : 2);
299 // DO NOT BREAK from this case
301 // the format of the write attributes cmd is:
302 // ([attr ID:2] [data type:1] [data:N]) * N
303 // the format of the write attributes response is:
304 // ([status 1] [attr ID 2]) * n
305 // ONLY errors are reported unless all are successful then a single success
306 // is sent. write attr no response is handled by just executing the same
307 // code but not setting the flag that sends the response at the end.
308 case ZCL_WRITE_ATTRIBUTES_NO_RESPONSE_COMMAND_ID:
309 case ZCL_WRITE_ATTRIBUTES_COMMAND_ID: {
310 uint8_t numFailures = 0;
311 uint8_t numSuccess = 0;
315 uint8_t writeData[ATTRIBUTE_LARGEST];
316 #endif //(BIGENDIAN_CPU)
317 EmberAfStatus status;
319 // set the cmd byte - this is byte 3 index 2, but since we have
320 // already incremented past the 3 byte ZCL header (our index is at 3),
321 // this gets written to "-1" since 3 - 1 = 2.
322 emberAfPutInt8uInResp(ZCL_WRITE_ATTRIBUTES_RESPONSE_COMMAND_ID);
324 // go through the message until there are no more attrID/type/data
325 while (msgLen > msgIndex + 3)
327 attrId = emberAfGetInt16u(message, msgIndex, msgLen);
328 dataType = emberAfGetInt8u(message, msgIndex + 2, msgLen);
330 dataSize = emberAfAttributeValueSize(dataType, message + msgIndex + 3);
332 // the data is sent little endian over-the-air, it needs to be
333 // inserted into the table big endian for the EM250 and little
334 // endian for the EZSP hosts. This means for the EM250 the data
335 // needs to be reversed before sending to writeAttributes
337 if (dataSize <= msgLen - (msgIndex + 3) && dataSize <= ATTRIBUTE_LARGEST)
339 // strings go over the air as length byte and then in human
340 // readable format. These should not be flipped.
341 if (emberAfIsThisDataTypeAStringType(dataType))
343 memmove(writeData, message + msgIndex + 3, dataSize);
347 // the data is sent little endian over-the-air, it needs to be
348 // inserted into the table big endian
350 for (i = 0; i < dataSize; i++)
352 writeData[i] = message[msgIndex + 3 + dataSize - i - 1];
355 #else //(BIGENDIAN_CPU)
356 if (dataSize <= msgLen - (msgIndex + 3))
358 #endif //(BIGENDIAN_CPU)
360 status = emberAfWriteAttributeExternal(cmd->apsFrame->destinationEndpoint, clusterId, attrId, clientServerMask,
364 #else //(BIGENDIAN_CPU)
365 &(message[msgIndex + 3]),
366 #endif //(BIGENDIAN_CPU)
368 emberAfAttributesPrint("WRITE: clus %2x attr %2x ", clusterId, attrId);
369 if (status == EMBER_ZCL_STATUS_SUCCESS)
372 emberAfAttributesPrintln("OK");
377 // write to the response buffer - status and then attrID
378 emberAfPutInt8uInResp(status);
379 emberAfPutInt16uInResp(attrId);
380 emberAfAttributesPrintln("FAIL %x", status);
384 // Increment past the attribute id (two bytes), the type (one byte), and
385 // the data (N bytes, including the length byte for strings).
386 msgIndex = static_cast<uint16_t>(msgIndex + 3 + dataSize);
391 status = EMBER_ZCL_STATUS_INVALID_VALUE;
392 // write to the response buffer - status and then attrID
393 emberAfPutInt8uInResp(status);
394 emberAfPutInt16uInResp(attrId);
395 emberAfAttributesPrintln("FAIL %x", status);
396 // size exceeds buffer, terminate loop
401 // always send a response unless the cmd requested no response
402 if (zclCmd == ZCL_WRITE_ATTRIBUTES_NO_RESPONSE_COMMAND_ID)
407 if (numFailures == 0)
409 // if no failures and no success this means the packet was too short
410 // print an error message but still return true as we consumed the
414 emberAfAttributesPrintln("WRITE: too short");
415 emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_MALFORMED_COMMAND);
418 // if no failures and at least one success, write a success status
419 // that means everything worked
422 emberAfPutInt8uInResp(EMBER_ZCL_STATUS_SUCCESS);
425 emberAfSendResponse();
429 // the format of discover is: [start attr ID:2] [max attr IDs:1]
430 // the format of the response is: [done:1] ([attrID:2] [type:1]) * N
431 case ZCL_DISCOVER_ATTRIBUTES_COMMAND_ID:
432 case ZCL_DISCOVER_ATTRIBUTES_EXTENDED_COMMAND_ID: {
433 AttributeId startingAttributeId;
434 uint8_t numberAttributes;
437 emberAfAttributesPrintln("%p%p: clus %2x", "DISC_ATTR",
438 (zclCmd == ZCL_DISCOVER_ATTRIBUTES_EXTENDED_COMMAND_ID ? "_EXT" : ""), clusterId);
440 // set the cmd byte - this is byte 3 index 2, but since we have
441 // already incremented past the 3 byte ZCL header (our index is at 3),
442 // this gets written to "-1" since 3 - 1 = 2.
443 emberAfPutInt8uInResp((zclCmd == ZCL_DISCOVER_ATTRIBUTES_COMMAND_ID
444 ? ZCL_DISCOVER_ATTRIBUTES_RESPONSE_COMMAND_ID
445 : ZCL_DISCOVER_ATTRIBUTES_EXTENDED_RESPONSE_COMMAND_ID));
447 // get the attrId to start on and the max count
448 startingAttributeId = emberAfGetInt16u(message, msgIndex, msgLen);
449 numberAttributes = emberAfGetInt8u(message, msgIndex + 2, msgLen);
451 // BUGZID: EMAPPFWKV2-828, EMAPPFWKV2-1401
452 if (zclCmd == ZCL_DISCOVER_ATTRIBUTES_COMMAND_ID && numberAttributes > DISC_ATTR_RSP_MAX_ATTRIBUTES)
454 numberAttributes = DISC_ATTR_RSP_MAX_ATTRIBUTES;
456 else if (zclCmd == ZCL_DISCOVER_ATTRIBUTES_EXTENDED_COMMAND_ID && numberAttributes > DISC_ATTR_EXT_RSP_MAX_ATTRIBUTES)
458 numberAttributes = DISC_ATTR_EXT_RSP_MAX_ATTRIBUTES;
462 // MISRA requires ..else if.. to have terminating else.
465 // The response has a one-byte field indicating whether discovery is
466 // complete. We can't populate that field until we've finished going
467 // through all the attributes, so save a placeholder, write a temporary
468 // value for now (so that the offset moves forward), and write the real
469 // value when we're done.
470 complete = &(appResponseData[appResponseLength]);
471 emberAfPutInt8uInResp(false);
472 *complete = emberAfReadSequentialAttributesAddToResponse(cmd->apsFrame->destinationEndpoint, clusterId, startingAttributeId,
473 clientServerMask, cmd->mfgCode, numberAttributes,
474 (zclCmd == ZCL_DISCOVER_ATTRIBUTES_EXTENDED_COMMAND_ID));
475 emberAfSendResponse();
479 case ZCL_CONFIGURE_REPORTING_COMMAND_ID:
480 if (emberAfConfigureReportingCommandCallback(cmd))
486 case ZCL_READ_REPORTING_CONFIGURATION_COMMAND_ID:
487 if (emberAfReadReportingConfigurationCommandCallback(cmd))
493 // ([attribute id:2] [status:1] [type:0/1] [value:0/V])+
494 case ZCL_READ_ATTRIBUTES_RESPONSE_COMMAND_ID:
495 // The "timesync" command in the CLI sends a Read Attributes command for the
496 // Time attribute on another device and then sets a flag. If that flag is
497 // set and a Read Attributes Response command for the time comes in, we set
498 // the time to the value in the message.
499 if (clusterId == ZCL_TIME_CLUSTER_ID)
501 if (emAfSyncingTime && !cmd->mfgSpecific && msgLen - msgIndex == 8 // attr:2 status:1 type:1 data:4
502 && (emberAfGetInt16u(message, msgIndex, msgLen) == ZCL_TIME_ATTRIBUTE_ID) &&
503 (emberAfGetInt8u(message, msgIndex + 2, msgLen) == EMBER_ZCL_STATUS_SUCCESS) &&
504 (emberAfGetInt8u(message, msgIndex + 3, msgLen) == ZCL_UTC_TIME_ATTRIBUTE_TYPE))
506 // emberAfSetTime(emberAfGetInt32u(message, msgIndex + 4, msgLen));
507 // emberAfDebugPrintln("time sync ok, time: %4x", emberAfGetCurrentTime());
508 emAfSyncingTime = false;
510 #ifdef EMBER_AF_PLUGIN_SMART_ENERGY_REGISTRATION_TIME_SOURCE_REQUIRED
511 emAfPluginSmartEnergyRegistrationReadAttributesResponseCallback(message + msgIndex, msgLen - msgIndex);
512 #endif // EMBER_AF_PLUGIN_SMART_ENERGY_REGISTRATION_TIME_SOURCE_REQUIRED
513 #ifdef EMBER_AF_PLUGIN_WWAH_SERVER_SILABS
514 emAfPluginSlWwahReadAttributesResponseCallback(clusterId, message, msgLen);
518 #ifdef EMBER_AF_PLUGIN_TRUST_CENTER_KEEPALIVE
519 if (clusterId == ZCL_KEEPALIVE_CLUSTER_ID && !cmd->mfgSpecific)
521 emAfPluginTrustCenterKeepaliveReadAttributesResponseCallback(message + msgIndex, msgLen - msgIndex);
523 #endif // EMBER_AF_PLUGIN_TRUST_CENTER_KEEPALIVE
525 #if defined(EMBER_AF_PLUGIN_KEY_ESTABLISHMENT)
526 if (clusterId == ZCL_KEY_ESTABLISHMENT_CLUSTER_ID && !cmd->mfgSpecific &&
527 msgLen - msgIndex == 6 // attr:2 status:1 type:1 data:2
528 && (emberAfGetInt16u(message, msgIndex, msgLen) == ZCL_KEY_ESTABLISHMENT_SUITE_CLIENT_ATTRIBUTE_ID) &&
529 (emberAfGetInt8u(message, msgIndex + 2, msgLen) == EMBER_ZCL_STATUS_SUCCESS) &&
530 ((emberAfGetInt8u(message, msgIndex + 3, msgLen) == ZCL_ENUM16_ATTRIBUTE_TYPE) ||
531 (emberAfGetInt8u(message, msgIndex + 3, msgLen) == ZCL_BITMAP16_ATTRIBUTE_TYPE)))
533 uint16_t suite = emberAfGetInt16u(message, msgIndex + 4, msgLen);
534 emberAfPluginKeyEstablishmentReadAttributesCallback(suite);
538 #if defined(EMBER_AF_PLUGIN_TEST_HARNESS)
539 emberAfPluginTestHarnessReadAttributesResponseCallback(clusterId, message + msgIndex, msgLen - msgIndex);
542 #if defined(EMBER_AF_PLUGIN_IAS_ZONE_CLIENT)
543 emberAfPluginIasZoneClientReadAttributesResponseCallback(clusterId, message + msgIndex,
544 static_cast<uint16_t>(msgLen - msgIndex));
547 #if defined(EMBER_AF_PLUGIN_SIMPLE_METERING_SERVER)
548 emberAfPluginSimpleMeteringClusterReadAttributesResponseCallback(clusterId, message + msgIndex,
549 static_cast<uint16_t>(msgLen - msgIndex));
552 if (!emberAfReadAttributesResponseCallback(clusterId, message + msgIndex, static_cast<uint16_t>(msgLen - msgIndex)))
554 emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS);
558 // ([status:1] [attribute id:2])+
559 case ZCL_WRITE_ATTRIBUTES_RESPONSE_COMMAND_ID:
561 #if defined(EMBER_AF_PLUGIN_TEST_HARNESS)
562 emberAfPluginTestHarnessWriteAttributesResponseCallback(clusterId, message + msgIndex,
563 static_cast<uint16_t>(msgLen - msgIndex));
566 #if defined(EMBER_AF_PLUGIN_IAS_ZONE_CLIENT)
567 emberAfPluginIasZoneClientWriteAttributesResponseCallback(clusterId, message + msgIndex,
568 static_cast<uint16_t>(msgLen - msgIndex));
571 if (!emberAfWriteAttributesResponseCallback(clusterId, message + msgIndex, static_cast<uint16_t>(msgLen - msgIndex)))
573 emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS);
577 // ([status:1] [direction:1] [attribute id:2])+
578 case ZCL_CONFIGURE_REPORTING_RESPONSE_COMMAND_ID:
579 if (!emberAfConfigureReportingResponseCallback(clusterId, message + msgIndex, static_cast<uint16_t>(msgLen - msgIndex)))
581 emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS);
585 // ([status:1] [direction:1] [attribute id:2] [type:0/1] ...
586 // ... [min interval:0/2] [max interval:0/2] [reportable change:0/V] ...
587 // ... [timeout:0/2])+
588 case ZCL_READ_REPORTING_CONFIGURATION_RESPONSE_COMMAND_ID:
589 if (!emberAfReadReportingConfigurationResponseCallback(clusterId, message + msgIndex,
590 static_cast<uint16_t>(msgLen - msgIndex)))
592 emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS);
596 // ([attribute id:2] [type:1] [data:V])+
597 case ZCL_REPORT_ATTRIBUTES_COMMAND_ID:
598 if (!emberAfReportAttributesCallback(clusterId, message + msgIndex, static_cast<uint16_t>(msgLen - msgIndex)))
600 emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS);
604 // [command id:1] [status:1]
605 case ZCL_DEFAULT_RESPONSE_COMMAND_ID: {
606 EmberAfStatus status;
608 commandId = emberAfGetInt8u(message, msgIndex, msgLen);
610 status = (EmberAfStatus) emberAfGetInt8u(message, msgIndex, msgLen);
612 emberAfClusterDefaultResponseWithMfgCodeCallback(cmd->apsFrame->destinationEndpoint, clusterId, commandId, status,
613 clientServerMask, cmd->mfgCode);
614 emberAfDefaultResponseCallback(clusterId, commandId, status);
618 // [discovery complete:1] ([attribute id:2] [type:1])*
619 case ZCL_DISCOVER_ATTRIBUTES_RESPONSE_COMMAND_ID:
620 case ZCL_DISCOVER_ATTRIBUTES_EXTENDED_RESPONSE_COMMAND_ID: {
621 bool discoveryComplete = emberAfGetInt8u(message, msgIndex, msgLen);
623 if (!emberAfDiscoverAttributesResponseCallback(clusterId, discoveryComplete, message + msgIndex,
624 static_cast<uint16_t>(msgLen - msgIndex),
625 (zclCmd == ZCL_DISCOVER_ATTRIBUTES_EXTENDED_RESPONSE_COMMAND_ID)))
627 emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS);
632 #ifdef EMBER_AF_SUPPORT_COMMAND_DISCOVERY
633 // Command discovery takes a bit of flash because we need to add structs
634 // for commands into the generated hader. Hence it's all configurable.
635 case ZCL_DISCOVER_COMMANDS_RECEIVED_COMMAND_ID:
636 case ZCL_DISCOVER_COMMANDS_GENERATED_COMMAND_ID: {
637 uint8_t startCommandIdentifier = emberAfGetInt8u(message, msgIndex, msgLen);
638 uint8_t maximumCommandIdentifiers = emberAfGetInt8u(message, msgIndex + 1, msgLen);
642 // Ok. This is the command that matters.
643 if (zclCmd == ZCL_DISCOVER_COMMANDS_RECEIVED_COMMAND_ID)
645 emberAfPutInt8uInResp(ZCL_DISCOVER_COMMANDS_RECEIVED_RESPONSE_COMMAND_ID);
650 emberAfPutInt8uInResp(ZCL_DISCOVER_COMMANDS_GENERATED_RESPONSE_COMMAND_ID);
653 savedIndex = appResponseLength;
654 flag = emberAfExtractCommandIds(flag, cmd, clusterId, appResponseData + appResponseLength + 1,
655 static_cast<uint16_t>(EMBER_AF_RESPONSE_BUFFER_LEN - appResponseLength - 1),
656 &appResponseLength, startCommandIdentifier, maximumCommandIdentifiers);
657 appResponseData[savedIndex] = (flag ? 1 : 0);
659 emberAfSendResponse();
662 case ZCL_DISCOVER_COMMANDS_RECEIVED_RESPONSE_COMMAND_ID: {
663 bool discoveryComplete = emberAfGetInt8u(message, msgIndex, msgLen);
665 if (msgIndex <= msgLen)
667 printDiscoverCommandsResponse(false, // is ZCL command generated?
668 clusterId, discoveryComplete, message + msgIndex,
669 static_cast<uint16_t>(msgLen - msgIndex));
670 if (!emberAfDiscoverCommandsReceivedResponseCallback(clusterId, cmd->mfgCode, discoveryComplete, message + msgIndex,
671 static_cast<uint16_t>(msgLen - msgIndex)))
673 emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS);
682 case ZCL_DISCOVER_COMMANDS_GENERATED_RESPONSE_COMMAND_ID: {
683 bool discoveryComplete = emberAfGetInt8u(message, msgIndex, msgLen);
685 if (msgIndex <= msgLen)
687 printDiscoverCommandsResponse(true, // is ZCL command generated?
688 clusterId, discoveryComplete, message + msgIndex,
689 static_cast<uint16_t>(msgLen - msgIndex));
690 if (!emberAfDiscoverCommandsGeneratedResponseCallback(clusterId, cmd->mfgCode, discoveryComplete, message + msgIndex,
691 static_cast<uint16_t>(msgLen - msgIndex)))
693 emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS);
705 // MISRA requires default case.
710 emberAfSendDefaultResponse(
711 cmd, (cmd->mfgSpecific ? EMBER_ZCL_STATUS_UNSUP_MANUF_GENERAL_COMMAND : EMBER_ZCL_STATUS_UNSUP_GENERAL_COMMAND));