Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / app / clusters / ias-zone-client / ias-zone-client.cpp
1 /**
2  *
3  *    Copyright (c) 2020 Project CHIP Authors
4  *
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
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17
18 /**
19  *
20  *    Copyright (c) 2020 Silicon Labs
21  *
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
25  *
26  *        http://www.apache.org/licenses/LICENSE-2.0
27  *
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.
33  */
34 /****************************************************************************
35  * @file
36  * @brief  *
37  * Client Operation:
38  *   1. Look for ZDO device announce notification.
39  *   2. Perform ZDO match descriptor on device.
40  *   3. If supports IAS Zone Server, Add that server
41  *to our known list. Write CIE Address.
42  *   4. Read CIE address, verify it is ours.  This is
43  *done mostly because the test case requires it.
44  *   5. Read the IAS Zone Server attributes.
45  *     Record in table.
46  *   6. When we get an enroll request, give them our
47  *(only) zone ID.
48  *   7. When we get a notification, read their
49  *attributes.
50  *
51  * Improvements that could be made:
52  *   Add support for multiple endpoints on server.
53  *Most often this is a legacy security system
54  *retrofitted with a single ZigBee radio.  Therefore
55  *   each sensor is on a different endpoint.  Right
56  *now our client only handles a single endpoint per
57  *node.
58  *
59  *   Integration with Poll Control.  When the device
60  *boots we should configure its polling to make it
61  *possible to read/write its attributes.
62  *
63  *   Update the emberAfIasZoneClientKnownServers list
64  *when we know a server un-enrolls. Right now, we
65  *don't have any way to tell when we don't need to
66  *keep track of a server anymore, i.e., when it
67  *un-enrolls. Therefore, we could potentially keep
68  *adding servers to our known list, and run out of
69  *room to add more. Fortunately, we have two things
70  *working for us:
71  *     1. Servers will most likely stay around in a
72  *network. It is unlikely that an IAS Zone Client in
73  *production will have to handle 254 different
74  *servers.
75  *     2. If a server un-enrolls and then enrolls
76  *again, it will get the same Zone ID and have a spot
77  *in the list, since we store servers by long address.
78  *******************************************************************************
79  ******************************************************************************/
80
81 #include "ias-zone-client.h"
82 #include "af.h"
83
84 //-----------------------------------------------------------------------------
85 // Globals
86
87 IasZoneDevice emberAfIasZoneClientKnownServers[EMBER_AF_PLUGIN_IAS_ZONE_CLIENT_MAX_DEVICES];
88
89 typedef enum
90 {
91     IAS_ZONE_CLIENT_STATE_NONE,
92     IAS_ZONE_CLIENT_STATE_DISCOVER_ENDPOINT,
93     IAS_ZONE_CLIENT_STATE_SET_CIE_ADDRESS,
94     IAS_ZONE_CLIENT_STATE_READ_CIE_ADDRESS,
95     IAS_ZONE_CLIENT_STATE_READ_ATTRIBUTES,
96 } IasZoneClientState;
97
98 static IasZoneClientState iasZoneClientState = IAS_ZONE_CLIENT_STATE_NONE;
99 static uint8_t currentIndex                  = NO_INDEX;
100 static uint8_t myEndpoint                    = 0;
101
102 EmberEventControl emberAfPluginIasZoneClientStateMachineEventControl;
103
104 //-----------------------------------------------------------------------------
105 // Forward Declarations
106
107 void readIasZoneServerAttributes(EmberNodeId nodeId);
108 static void iasClientSaveCommand(void);
109 static void iasClientLoadCommand(void);
110
111 //-----------------------------------------------------------------------------
112 // Functions
113
114 void emberAfIasZoneClusterClientInitCallback(EndpointId endpoint)
115 {
116     emAfClearServers();
117     myEndpoint = endpoint;
118     iasClientLoadCommand();
119 }
120
121 void emAfClearServers(void)
122 {
123     MEMSET(emberAfIasZoneClientKnownServers, 0xFF, sizeof(IasZoneDevice) * EMBER_AF_PLUGIN_IAS_ZONE_CLIENT_MAX_DEVICES);
124 }
125
126 static void clearState(void)
127 {
128     currentIndex       = 0;
129     iasZoneClientState = IAS_ZONE_CLIENT_STATE_NONE;
130 }
131
132 static void setServerZoneStatus(uint8_t serverIndex, uint16_t zoneStatus)
133 {
134     emberAfIasZoneClientKnownServers[serverIndex].zoneStatus = zoneStatus;
135     iasClientSaveCommand();
136 }
137
138 static void setServerIeee(uint8_t serverIndex, uint8_t * ieeeAddress)
139 {
140     MEMCOPY(emberAfIasZoneClientKnownServers[serverIndex].ieeeAddress, ieeeAddress, EUI64_SIZE);
141     iasClientSaveCommand();
142 }
143
144 static void clearServerIeee(uint8_t serverIndex)
145 {
146     MEMSET(emberAfIasZoneClientKnownServers[serverIndex].ieeeAddress, 0xFF, sizeof(IasZoneDevice));
147     iasClientSaveCommand();
148 }
149
150 static void setServerNodeId(uint8_t serverIndex, EmberNodeId nodeId)
151 {
152     emberAfIasZoneClientKnownServers[serverIndex].nodeId = nodeId;
153 }
154
155 static void clearServerNodeId(uint8_t serverIndex)
156 {
157     emberAfIasZoneClientKnownServers[serverIndex].nodeId = EMBER_NULL_NODE_ID;
158 }
159
160 static void setServerZoneState(uint8_t serverIndex, uint8_t zoneState)
161 {
162     emberAfIasZoneClientKnownServers[serverIndex].zoneState = zoneState;
163     iasClientSaveCommand();
164 }
165
166 static void setServerEndpoint(uint8_t serverIndex, EndpointId endpoint)
167 {
168     emberAfIasZoneClientKnownServers[serverIndex].endpoint = endpoint;
169     iasClientSaveCommand();
170 }
171
172 static void setServerZoneType(uint8_t serverIndex, uint16_t zoneType)
173 {
174     emberAfIasZoneClientKnownServers[serverIndex].zoneType = zoneType;
175     iasClientSaveCommand();
176 }
177
178 static void setServerZoneId(uint8_t serverIndex, uint16_t zoneId)
179 {
180     emberAfIasZoneClientKnownServers[serverIndex].zoneId = zoneId;
181     iasClientSaveCommand();
182 }
183
184 static void setCurrentIndex(uint8_t serverIndex)
185 {
186     currentIndex = serverIndex;
187     iasClientSaveCommand();
188 }
189
190 static void setIasZoneClientState(uint8_t clientState)
191 {
192     iasZoneClientState = clientState;
193     iasClientSaveCommand();
194 }
195
196 static void iasClientSaveCommand(void)
197 {
198 #if defined(EZSP_HOST) && !defined(EMBER_TEST) && defined(UNIX_HOST)
199     FILE * fp;
200     uint16_t i, j;
201
202     // save zone server list
203     fp = fopen("iaszone.txt", "w");
204
205     for (i = 0; i < EMBER_AF_PLUGIN_IAS_ZONE_CLIENT_MAX_DEVICES; i++)
206     {
207         if (emberAfIasZoneClientKnownServers[i].zoneId != 0xFF)
208         {
209             fprintf(fp, "%x %x %x %x %x ", emberAfIasZoneClientKnownServers[i].zoneId,
210                     emberAfIasZoneClientKnownServers[i].zoneStatus, emberAfIasZoneClientKnownServers[i].zoneState,
211                     emberAfIasZoneClientKnownServers[i].endpoint, emberAfIasZoneClientKnownServers[i].zoneType);
212             for (j = 0; j < 8; j++)
213             {
214                 fprintf(fp, "%x ", emberAfIasZoneClientKnownServers[i].ieeeAddress[j]);
215             }
216         }
217     }
218     // Write something to mark the end of the file.
219     fprintf(fp, "ff");
220     int res = fclose(fp);
221     assert(res == 0);
222 #endif //#if defined(EZSP_HOST) && !defined(EMBER_TEST) && defined(UNIX_HOST)
223 }
224
225 static void iasClientLoadCommand(void)
226 {
227 #if defined(EZSP_HOST) && !defined(EMBER_TEST) && defined(UNIX_HOST)
228     FILE * fp;
229     uint16_t i, j;
230
231     unsigned int data1, data2, data3, data4, data5;
232
233     fp = fopen("iaszone.txt", "r");
234
235     if (!fp)
236     {
237         return;
238     }
239
240     for (i = 0; i < EMBER_AF_PLUGIN_IAS_ZONE_CLIENT_MAX_DEVICES; i++)
241     {
242         if (feof(fp))
243         {
244             break;
245         }
246         fscanf(fp, "%x ", &data1);
247         if (data1 == 0xff)
248         {
249             break;
250         }
251         fscanf(fp, "%x %x %x %x ", &data2, &data3, &data4, &data5);
252
253         emberAfIasZoneClientKnownServers[i].zoneId     = (uint8_t) data1;
254         emberAfIasZoneClientKnownServers[i].zoneStatus = (uint16_t) data2;
255         emberAfIasZoneClientKnownServers[i].zoneState  = (uint8_t) data3;
256         emberAfIasZoneClientKnownServers[i].endpoint   = (uint8_t) data4;
257         emberAfIasZoneClientKnownServers[i].zoneType   = (uint16_t) data5;
258
259         for (j = 0; j < 8; j++)
260         {
261             fscanf(fp, "%x ", &data1);
262             emberAfIasZoneClientKnownServers[i].ieeeAddress[j] = (uint8_t) data1;
263         }
264     }
265     int res = fclose(fp);
266     assert(res == 0);
267 #endif // #if defined(EZSP_HOST) && !defined(EMBER_TEST) && defined(UNIX_HOST)
268 }
269
270 static uint8_t findIasZoneServerByIeee(uint8_t * ieeeAddress)
271 {
272     uint8_t i;
273     for (i = 0; i < EMBER_AF_PLUGIN_IAS_ZONE_CLIENT_MAX_DEVICES; i++)
274     {
275         if (0 == memcmp(ieeeAddress, emberAfIasZoneClientKnownServers[i].ieeeAddress, EUI64_SIZE))
276         {
277             return i;
278         }
279     }
280     return NO_INDEX;
281 }
282
283 static uint8_t findIasZoneServerByNodeId(EmberNodeId nodeId)
284 {
285     uint8_t i;
286     for (i = 0; i < EMBER_AF_PLUGIN_IAS_ZONE_CLIENT_MAX_DEVICES; i++)
287     {
288         if (nodeId == emberAfIasZoneClientKnownServers[i].nodeId)
289         {
290             return i;
291         }
292     }
293
294     // If we didn't find the node ID in the table, see if the stack knows about
295     // it.
296     EmberEUI64 eui64;
297     if (emberLookupEui64ByNodeId(nodeId, eui64) == EMBER_SUCCESS)
298     {
299         i = findIasZoneServerByIeee(eui64);
300         if (i != NO_INDEX)
301         {
302             setServerNodeId(i, nodeId);
303         }
304     }
305
306     return i;
307 }
308
309 bool emberAfIasZoneClusterZoneStatusChangeNotificationCallback(uint16_t zoneStatus, uint8_t extendedStatus, uint8_t zoneId,
310                                                                uint16_t delay)
311 {
312     uint8_t serverIndex = findIasZoneServerByNodeId(emberAfCurrentCommand()->source);
313     uint8_t status      = EMBER_ZCL_STATUS_NOT_FOUND;
314     if (serverIndex != NO_INDEX)
315     {
316         status = EMBER_ZCL_STATUS_SUCCESS;
317         setServerZoneStatus(serverIndex, zoneStatus);
318
319         emberAfIasZoneClusterPrintln("Zone %d status change, 0x%2X from 0x%2X", zoneId, zoneStatus,
320                                      emberAfCurrentCommand()->source);
321
322         // The Test case calls for readding attributes after status change.
323         //   that is silly for the production device.
324         // readIasZoneServerAttributes(emberAfCurrentCommand()->source);
325     }
326     emberAfSendDefaultResponse(emberAfCurrentCommand(), status);
327     return true;
328 }
329
330 bool emberAfIasZoneClusterZoneEnrollRequestCallback(uint16_t zoneType, uint16_t manufacturerCode)
331 {
332     EmberAfIasEnrollResponseCode responseCode = EMBER_ZCL_IAS_ENROLL_RESPONSE_CODE_NO_ENROLL_PERMIT;
333     uint8_t zoneId                            = UNKNOWN_ZONE_ID;
334     uint8_t serverIndex                       = findIasZoneServerByNodeId(emberAfCurrentCommand()->source);
335     EmberStatus status;
336
337     if (serverIndex != NO_INDEX)
338     {
339         responseCode = EMBER_ZCL_IAS_ENROLL_RESPONSE_CODE_SUCCESS;
340         zoneId       = serverIndex;
341         setServerZoneId(serverIndex, zoneId);
342     }
343     emberAfFillExternalBuffer((ZCL_CLUSTER_SPECIFIC_COMMAND | ZCL_FRAME_CONTROL_CLIENT_TO_SERVER), ZCL_IAS_ZONE_CLUSTER_ID,
344                               ZCL_ZONE_ENROLL_RESPONSE_COMMAND_ID, "uu", responseCode, zoneId);
345     // Need to send this command with our source EUI because the server will
346     // check our EUI64 against his CIE Address to see if we're his CIE.
347     emberAfGetCommandApsFrame()->options |= EMBER_APS_OPTION_SOURCE_EUI64;
348     status = emberAfSendResponse();
349     emberAfCorePrintln("Sent enroll response with responseCode: 0x%X, zoneId: 0x%X, status: 0x%X", responseCode, zoneId, status);
350     return true;
351 }
352
353 void emberAfPluginIasZoneClientStateMachineEventHandler(void)
354 {
355     emberAfIasZoneClusterPrintln("IAS Zone Client Timeout waiting for message response.");
356     emberEventControlSetInactive(emberAfPluginIasZoneClientStateMachineEventControl);
357     clearState();
358 }
359
360 static uint8_t addServer(EmberNodeId nodeId, uint8_t * ieeeAddress)
361 {
362     uint8_t i = findIasZoneServerByIeee(ieeeAddress);
363     if (i != NO_INDEX)
364     {
365         return i;
366     }
367
368     for (i = 0; i < EMBER_AF_PLUGIN_IAS_ZONE_CLIENT_MAX_DEVICES; i++)
369     {
370         const uint8_t unsetEui64[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
371         if (0 == memcmp(emberAfIasZoneClientKnownServers[i].ieeeAddress, unsetEui64, EUI64_SIZE))
372         {
373             setServerIeee(i, ieeeAddress);
374             setServerNodeId(i, nodeId);
375             setServerEndpoint(i, UNKNOWN_ENDPOINT);
376             return i;
377         }
378     }
379     return NO_INDEX;
380 }
381
382 static void removeServer(uint8_t * ieeeAddress)
383 {
384     uint8_t index = findIasZoneServerByIeee(ieeeAddress);
385     clearServerIeee(index);
386     clearServerNodeId(index);
387 }
388
389 static EmberStatus sendCommand(EmberNodeId destAddress)
390 {
391     emberAfSetCommandEndpoints(myEndpoint, emberAfIasZoneClientKnownServers[currentIndex].endpoint);
392     EmberStatus status = emberAfSendCommandUnicast(EMBER_OUTGOING_DIRECT, destAddress);
393     emberAfIasZoneClusterPrintln("Sent IAS Zone Client Command to 0x%2X (%d -> %d) status: 0x%X", destAddress, myEndpoint,
394                                  emberAfIasZoneClientKnownServers[currentIndex].endpoint, status);
395     if (status != EMBER_SUCCESS)
396     {
397         clearState();
398     }
399     return status;
400 }
401
402 static void setCieAddress(EmberNodeId destAddress)
403 {
404     uint8_t writeAttributes[] = {
405         EMBER_LOW_BYTE(ZCL_IAS_CIE_ADDRESS_ATTRIBUTE_ID),
406         EMBER_HIGH_BYTE(ZCL_IAS_CIE_ADDRESS_ATTRIBUTE_ID),
407         ZCL_IEEE_ADDRESS_ATTRIBUTE_TYPE,
408         0,
409         0,
410         0,
411         0,
412         0,
413         0,
414         0,
415         0, // ieee (filled in later)
416     };
417     emberAfGetEui64(&writeAttributes[3]);
418     emberAfFillExternalBuffer((ZCL_GLOBAL_COMMAND | ZCL_FRAME_CONTROL_CLIENT_TO_SERVER), ZCL_IAS_ZONE_CLUSTER_ID,
419                               ZCL_WRITE_ATTRIBUTES_COMMAND_ID, "b", writeAttributes, sizeof(writeAttributes));
420     emberAfIasZoneClusterPrintln("Writing CIE Address to IAS Zone Server");
421     if (EMBER_SUCCESS == sendCommand(destAddress))
422     {
423         setIasZoneClientState(IAS_ZONE_CLIENT_STATE_SET_CIE_ADDRESS);
424     }
425 }
426
427 static void iasZoneClientServiceDiscoveryCallback(const EmberAfServiceDiscoveryResult * result)
428 {
429     if (result->status == EMBER_AF_UNICAST_SERVICE_DISCOVERY_COMPLETE_WITH_RESPONSE &&
430         result->zdoRequestClusterId == MATCH_DESCRIPTORS_REQUEST)
431     {
432         const EmberAfEndpointList * endpointList = (const EmberAfEndpointList *) result->responseData;
433         if (endpointList->count > 0)
434         {
435             setServerEndpoint(currentIndex, endpointList->list[0]);
436             emberAfIasZoneClusterPrintln("Device 0x%2X supports IAS Zone Server", result->matchAddress);
437             setCieAddress(result->matchAddress);
438             return;
439         }
440     }
441     clearState();
442 }
443
444 static void checkForIasZoneServer(EmberNodeId emberNodeId, uint8_t * ieeeAddress)
445 {
446     uint8_t endpointIndex = emberAfIndexFromEndpoint(myEndpoint);
447     uint8_t serverIndex   = addServer(emberNodeId, ieeeAddress);
448
449     if (serverIndex == NO_INDEX)
450     {
451         emberAfIasZoneClusterPrintln("Error: Could not add IAS Zone server.");
452         return;
453     }
454
455     setCurrentIndex(serverIndex);
456
457     if (emberAfIasZoneClientKnownServers[serverIndex].endpoint != UNKNOWN_ENDPOINT)
458     {
459         // If a remote endpoint that you have already seen announces itself,
460         // write your IEEE in them just in case they left and are rejoining. --agkeesle
461         // Bug: EMAPPFWKV2-1078
462         setCieAddress(emberNodeId);
463         emberAfIasZoneClusterPrintln("Node 0x%2X already known to IAS client", emberNodeId);
464         return;
465     }
466
467     EmberStatus status = emberAfFindDevicesByCluster(emberNodeId, ZCL_IAS_ZONE_CLUSTER_ID,
468                                                      true, // server cluster?
469                                                      iasZoneClientServiceDiscoveryCallback);
470
471     if (status != EMBER_SUCCESS)
472     {
473         emberAfIasZoneClusterPrintln("Error: Failed to initiate service discovery for IAS Zone Server 0x%2X", emberNodeId);
474         clearState();
475     }
476 }
477
478 void emberAfPluginIasZoneClientZdoMessageReceivedCallback(EmberNodeId emberNodeId, EmberApsFrame * apsFrame, uint8_t * message,
479                                                           uint16_t length)
480 {
481     emberAfIasZoneClusterPrintln("Incoming ZDO, Cluster: 0x%2X", apsFrame->clusterId);
482     if (apsFrame->clusterId == END_DEVICE_ANNOUNCE)
483     {
484         checkForIasZoneServer(emberNodeId, &(message[3]));
485     }
486 }
487
488 void readIasZoneServerAttributes(EmberNodeId nodeId)
489 {
490     uint8_t iasZoneAttributeIds[] = {
491         EMBER_LOW_BYTE(ZCL_ZONE_STATE_ATTRIBUTE_ID),  EMBER_HIGH_BYTE(ZCL_ZONE_STATE_ATTRIBUTE_ID),
492
493         EMBER_LOW_BYTE(ZCL_ZONE_TYPE_ATTRIBUTE_ID),   EMBER_HIGH_BYTE(ZCL_ZONE_TYPE_ATTRIBUTE_ID),
494
495         EMBER_LOW_BYTE(ZCL_ZONE_STATUS_ATTRIBUTE_ID), EMBER_HIGH_BYTE(ZCL_ZONE_STATUS_ATTRIBUTE_ID),
496     };
497     emberAfFillExternalBuffer((ZCL_GLOBAL_COMMAND | ZCL_FRAME_CONTROL_CLIENT_TO_SERVER), ZCL_IAS_ZONE_CLUSTER_ID,
498                               ZCL_READ_ATTRIBUTES_COMMAND_ID, "b", iasZoneAttributeIds, sizeof(iasZoneAttributeIds));
499     if (EMBER_SUCCESS == sendCommand(nodeId))
500     {
501         setIasZoneClientState(IAS_ZONE_CLIENT_STATE_READ_ATTRIBUTES);
502     }
503 }
504
505 void readIasZoneServerCieAddress(EmberNodeId nodeId)
506 {
507     uint8_t iasZoneAttributeIds[] = {
508         EMBER_LOW_BYTE(ZCL_IAS_CIE_ADDRESS_ATTRIBUTE_ID),
509         EMBER_HIGH_BYTE(ZCL_IAS_CIE_ADDRESS_ATTRIBUTE_ID),
510     };
511     emberAfFillExternalBuffer((ZCL_GLOBAL_COMMAND | ZCL_FRAME_CONTROL_CLIENT_TO_SERVER), ZCL_IAS_ZONE_CLUSTER_ID,
512                               ZCL_READ_ATTRIBUTES_COMMAND_ID, "b", iasZoneAttributeIds, sizeof(iasZoneAttributeIds));
513     if (EMBER_SUCCESS == sendCommand(nodeId))
514     {
515         setIasZoneClientState(IAS_ZONE_CLIENT_STATE_READ_CIE_ADDRESS);
516     }
517 }
518
519 void emberAfPluginIasZoneClientWriteAttributesResponseCallback(ClusterId clusterId, uint8_t * buffer, uint16_t bufLen)
520 {
521     if (clusterId == ZCL_IAS_ZONE_CLUSTER_ID && iasZoneClientState == IAS_ZONE_CLIENT_STATE_SET_CIE_ADDRESS &&
522         buffer[0] == EMBER_ZCL_STATUS_SUCCESS)
523     {
524         readIasZoneServerCieAddress(emberAfCurrentCommand()->source);
525         return;
526     }
527     return;
528 }
529
530 void emberAfPluginIasZoneClientReadAttributesResponseCallback(ClusterId clusterId, uint8_t * buffer, uint16_t bufLen)
531 {
532     uint8_t zoneStatus, zoneType, zoneState;
533     if (clusterId == ZCL_IAS_ZONE_CLUSTER_ID &&
534         (iasZoneClientState == IAS_ZONE_CLIENT_STATE_READ_ATTRIBUTES ||
535          iasZoneClientState == IAS_ZONE_CLIENT_STATE_READ_CIE_ADDRESS))
536     {
537         uint16_t i = 0;
538         while ((i + 3) <= bufLen)
539         { // 3 to insure we can read at least the attribute ID
540           // and the status
541             AttributeId attributeId = buffer[i] + (buffer[i + 1] << 8);
542             uint8_t status          = buffer[i + 2];
543             i += 3;
544             // emberAfIasZoneClusterPrintln("Parsing Attribute 0x%2X, Status: 0x%X", attributeId, status);
545             if (status == EMBER_ZCL_STATUS_SUCCESS)
546             {
547                 if ((i + 1) > bufLen)
548                 {
549                     // Too short, dump the message.
550                     return;
551                 }
552                 i++; // skip the type of the attribute.  We already know what it should be.
553                 switch (attributeId)
554                 {
555                 case ZCL_ZONE_STATUS_ATTRIBUTE_ID:
556                     if ((i + 2) > bufLen)
557                     {
558                         // Too short, dump the message.
559                         return;
560                     }
561                     zoneStatus = (buffer[i] + (buffer[i + 1] << 8));
562                     setServerZoneStatus(currentIndex, zoneStatus);
563                     i += 2;
564                     break;
565                 case ZCL_ZONE_TYPE_ATTRIBUTE_ID:
566                     if ((i + 2) > bufLen)
567                     {
568                         // Too short, dump the message.
569                         return;
570                     }
571                     zoneType = (buffer[i] + (buffer[i + 1] << 8));
572                     setServerZoneType(currentIndex, zoneType);
573                     i += 2;
574                     break;
575                 case ZCL_ZONE_STATE_ATTRIBUTE_ID:
576                     if ((i + 1) > bufLen)
577                     {
578                         // Too short, dump the message
579                         return;
580                     }
581                     zoneState = buffer[i];
582                     setServerZoneState(currentIndex, zoneState);
583                     i++;
584                     break;
585                 case ZCL_IAS_CIE_ADDRESS_ATTRIBUTE_ID: {
586                     uint8_t myIeee[EUI64_SIZE];
587                     emberAfGetEui64(myIeee);
588                     if ((i + 8) > bufLen)
589                     {
590                         // Too short, dump the message
591                     }
592                     else if (0 != memcmp(&(buffer[i]), myIeee, EUI64_SIZE))
593                     {
594                         emberAfIasZoneClusterPrintln("CIE Address not set to mine, removing IAS zone server.");
595                         removeServer(&(buffer[i]));
596                         clearState();
597                     }
598                     else
599                     {
600                         readIasZoneServerAttributes(emberAfCurrentCommand()->source);
601                     }
602                     return;
603                 }
604                 }
605             }
606         }
607         emberAfIasZoneClusterPrintln("Retrieved IAS Zone Server attributes from 0x%2X", emberAfCurrentCommand()->source);
608         clearState();
609     }
610 }
611
612 void emberAfPluginIasZoneClientZdoCallback(EmberNodeId emberNodeId, EmberApsFrame * apsFrame, uint8_t * message, uint16_t length) {}
613
614 void emberAfPluginIasZoneClientWriteAttributesResponseCallback(ClusterId clusterId, uint8_t * buffer, uint16_t bufLen) {}
615
616 void emberAfPluginIasZoneClientReadAttributesResponseCallback(ClusterId clusterId, uint8_t * buffer, uint16_t bufLen) {}