Imported Upstream version 1.0.0
[platform/upstream/iotivity.git] / plugins / zigbee_wrapper / src / zigbee_wrapper.c
1 //******************************************************************
2 //
3 // Copyright 2015 Intel Mobile Communications GmbH All Rights Reserved.
4 //
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 //      http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 //
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
20
21 /**
22  * @file
23  *
24  * This file contains defined interface for the Zigbee Radio.
25  */
26
27  #ifdef __cplusplus
28 #include <cfloat>
29 #else
30 #include <float.h>
31 #endif // __cplusplus
32
33 #include <stdbool.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <inttypes.h> // To convert "int64_t" to string.
37 #include <math.h>
38 #include <errno.h>
39
40 #include "zigbee_wrapper.h"
41 #include "telegesis_wrapper.h"
42 #include "pluginlist.h"
43
44 #include "ocpayload.h"
45 #include "oic_malloc.h"
46 #include "oic_string.h"
47 #include "logger.h"
48
49 #define HexPrepend "0x"
50
51 #define TAG "zigbeeWrapper"
52
53 // TODO: These should eventually go into an XML/JSON/Mapping thing
54 #define MAX_ATTRIBUTES                       10
55 #define ZB_TEMPERATURE_CLUSTER               "0402"
56 #define ZB_TEMPERATURE_ATTRIBUTE_ID          "0000"
57 #define ZB_CURRENT_LEVEL_ATTRIBUTE_READONLY  "0000"
58 #define ZB_ON_LEVEL_ATTRIBUTE                "0011"
59 #define ZB_LEVEL_CONTROL_CLUSTER             "0008"
60 #define ZB_IAS_ZONE_CLUSTER                  "0500"
61 #define ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID      "0002"
62 #define ZB_INDICATOR_CLUSTER                 "0003"
63 #define ZB_INDICATOR_ATTRIBUTE_ID            "0000"
64 #define ZB_ON_OFF_CLUSTER                    "0006"
65 #define ZB_ON_OFF_ATTRIBUTE_ID               "0000"
66 #define ZB_IAS_ZONE_TYPE_ATTRIBUTE_ID        "0001"
67
68 #define IAS_ZONE_TYPE_MOTION_SENSOR          "000d"
69 #define IAS_ZONE_TYPE_CONTACT_SENSOR         "0015"
70 #define IAS_ZONE_TYPE_WATER_SENSOR           "002a"
71
72 #define ZB_DATA_TYPE_NULL                    "00"
73 #define ZB_DATA_TYPE_1_BYTE                  "08"
74 #define ZB_DATA_TYPE_2_BYTE                  "09"
75 #define ZB_DATA_TYPE_3_BYTE                  "0a"
76 #define ZB_DATA_TYPE_4_BYTE                  "0b"
77 #define ZB_DATA_TYPE_5_BYTE                  "0c"
78 #define ZB_DATA_TYPE_6_BYTE                  "0d"
79 #define ZB_DATA_TYPE_7_BYTE                  "0e"
80 #define ZB_DATA_TYPE_8_BYTE                  "0f"
81 #define ZB_DATA_TYPE_BOOL                    "10"
82 #define ZB_DATA_TYPE_SIGNED_INT_16           "29"
83 #define ZB_DATA_TYPE_UNSIGNED_INT_8          "20"
84 #define ZB_DATA_TYPE_UNSIGNED_INT_16         "21"
85
86 #define MAX_STRLEN_INT (10)
87 // DBL_MANT_DIG = Max # of digits after decimal after the leading zeros.
88 // DBL_MIN_EXP = Max # of leading zeros of the mantissa.
89 // Magic number '3' represents a '-' (negative sign), '0' (a possible zero), and '.' (a period).
90 //       "-0." from a number like "-0.999999991245", the "-0." adds 3 unaccounted characters.
91 #define MAX_STRLEN_DOUBLE (3 + DBL_MANT_DIG - DBL_MIN_EXP)
92 #define MAX_STRLEN_BOOL (1)
93
94 #define DEFAULT_TRANS_TIME "0000"
95 #define DEFAULT_MOVETOLEVEL_MODE "0"
96
97 static const char* OIC_TEMPERATURE_SENSOR = "oic.r.temperature";
98 static const char* OIC_DIMMABLE_LIGHT = "oic.r.light.dimming";
99 static const char* OIC_CONTACT_SENSOR = "oic.r.sensor.contact";
100 static const char* OIC_MOTION_SENSOR = "oic.r.sensor.motion";
101 static const char* OIC_WATER_SENSOR = "oic.r.sensor.water";
102 static const char* OIC_BINARY_SWITCH = "oic.r.switch.binary";
103
104 static const char* OIC_TEMPERATURE_ATTRIBUTE = "temperature";
105 static const char* OIC_DIMMING_ATTRIBUTE = "dimmingSetting";
106 static const char* OIC_CONTACT_ATTRIBUTE = "value";
107 static const char* OIC_ON_OFF_ATTRIBUTE = "value";
108
109 PIPlugin_Zigbee ** gPlugin = NULL;
110
111 typedef enum
112 {
113     ZB_NULL,   // No Data
114     ZB_8_BIT,  // 1 byte
115     ZB_16_BIT, // 2 bytes
116     ZB_24_BIT, // 3 bytes
117     ZB_32_BIT, // 4 bytes
118     ZB_40_BIT, // 5 bytes
119     ZB_48_BIT, // 6 bytes
120     ZB_56_BIT, // 7 bytes
121     ZB_64_BIT, // 8 bytes
122     ZB_BOOL,   // boolean
123     ZB_8_BITMAP,
124     ZB_16_BITMAP,
125     ZB_24_BITMAP,
126     ZB_32_BITMAP,
127     ZB_40_BITMAP,
128     ZB_48_BITMAP,
129     ZB_56_BITMAP,
130     ZB_64_BITMAP,
131     ZB_16_SINT,
132     ZB_8_UINT,
133     ZB_16_UINT
134 } ZigBeeAttributeDataType;
135
136 char * getZBDataTypeString(ZigBeeAttributeDataType attrType);
137 OCEntityHandlerResult ProcessEHRequest(PIPluginBase * plugin, OCEntityHandlerRequest *ehRequest,
138         OCRepPayload **payload);
139
140 typedef enum
141 {
142     OIC_ATTR_NULL,
143     OIC_ATTR_INT,
144     OIC_ATTR_DOUBLE,
145     OIC_ATTR_BOOL,
146     OIC_ATTR_STRING
147 } OICAttributeType;
148
149 typedef struct
150 {
151     char                *oicAttribute;
152     char                *zigBeeAttribute;
153     OICAttributeType    oicType;
154     ZigBeeAttributeDataType zigbeeType;
155     union
156     {
157         int64_t i;
158         double d;
159         bool b;
160         char* str;
161     } val;
162
163 } OICZigBeeAttributePair;
164
165 typedef enum
166 {
167     CIE_RON_OFF         = 1 << 1,
168     CIE_MOVE_TO_LEVEL   = 1 << 2
169
170 } CIECommandMask;
171
172 typedef struct
173 {
174     uint32_t count;
175     CIECommandMask CIEMask;
176     OICZigBeeAttributePair list[MAX_ATTRIBUTES];
177 } AttributeList;
178
179 const char* ZigBeeClusterIDToOICResourceType(const char * clusterID);
180
181 OCStackResult getZigBeeAttributesForOICResource(const char * OICResourceType,
182                                                     AttributeList *attributeList);
183
184 bool getZigBeeAttributesIfValid(const char * OICResourceType,
185                                     AttributeList *attributeList,
186                                     OCRepPayload *payload);
187
188 const char * getResourceTypeForIASZoneType(TWDevice *device)
189 {
190     if(!device)
191     {
192         return NULL;
193     }
194     char *IASZoneType = NULL;
195     const char *resourceType = NULL;
196     uint8_t length = 0;
197
198     OCStackResult ret = TWGetAttribute(
199         NULL,
200         device->nodeId,
201         device->endpointOfInterest->endpointId,
202         ZB_IAS_ZONE_CLUSTER,
203         ZB_IAS_ZONE_TYPE_ATTRIBUTE_ID,
204         &IASZoneType,
205         &length
206     );
207
208     if (ret != OC_STACK_OK || !IASZoneType)
209     {
210         OC_LOG_V (ERROR, TAG, "Error %u getting IAS Zone Type", ret);
211         return NULL;
212     }
213
214     if (strcmp (IASZoneType, IAS_ZONE_TYPE_CONTACT_SENSOR) == 0)
215     {
216         resourceType = OIC_CONTACT_SENSOR;
217     }
218     else if (strcmp (IASZoneType, IAS_ZONE_TYPE_MOTION_SENSOR) == 0)
219     {
220         resourceType = OIC_MOTION_SENSOR;
221     }
222     else if (strcmp (IASZoneType, IAS_ZONE_TYPE_WATER_SENSOR) == 0)
223     {
224         resourceType = OIC_WATER_SENSOR;
225     }
226     else
227     {
228         OC_LOG_V (ERROR, TAG, "Unsupported Zone Type %s", IASZoneType);
229         resourceType = NULL;
230     }
231
232     OICFree(IASZoneType);
233
234     return resourceType;
235 }
236
237 OCStackResult buildURI(char ** output,
238                        const char * prefix,
239                        const char * nodeId,
240                        const char * endpointId,
241                        const char * clusterId)
242 {
243     if(!output || !prefix || !nodeId || !endpointId || !clusterId)
244     {
245         return OC_STACK_INVALID_PARAM;
246     }
247     const char LEN_SEPARATOR[] = "/";
248     size_t lenSeparatorSize = sizeof(LEN_SEPARATOR) - 1;
249     size_t newUriSize = strlen(prefix) + lenSeparatorSize +
250                         strlen(nodeId) + lenSeparatorSize +
251                         strlen(endpointId) + lenSeparatorSize +
252                         strlen(clusterId)
253                         + 1; // NULL Terminator
254     *output = (char *) OICCalloc(1, newUriSize);
255
256     if (!*output)
257     {
258         OC_LOG (ERROR, TAG, "Out of memory");
259         return OC_STACK_NO_MEMORY;
260     }
261
262     char * temp = OICStrcpy(*output, newUriSize, prefix);
263     if(temp != *output)
264     {
265         goto exit;
266     }
267     temp = OICStrcat(*output, newUriSize, LEN_SEPARATOR);
268     if(temp != *output)
269     {
270         goto exit;
271     }
272     temp = OICStrcat(*output, newUriSize, nodeId);
273     if(temp != *output)
274     {
275         goto exit;
276     }
277     temp = OICStrcat(*output, newUriSize, LEN_SEPARATOR);
278     if(temp != *output)
279     {
280         goto exit;
281     }
282     temp = OICStrcat(*output, newUriSize, endpointId);
283     if(temp != *output)
284     {
285         goto exit;
286     }
287     temp = OICStrcat(*output, newUriSize, LEN_SEPARATOR);
288     if(temp != *output)
289     {
290         goto exit;
291     }
292     temp = OICStrcat(*output, newUriSize, clusterId);
293     if(temp != *output)
294     {
295         goto exit;
296     }
297
298     return OC_STACK_OK;
299
300 exit:
301     OICFree(*output);
302     *output = NULL;
303     return OC_STACK_NO_MEMORY;
304 }
305
306 void foundZigbeeCallback(TWDevice *device)
307 {
308     if(!device)
309     {
310         OC_LOG(ERROR, TAG, "foundZigbeeCallback: Invalid parameter.");
311         return;
312     }
313     int count = device->endpointOfInterest->clusterList->count;
314     for(int i=0; i < count; i++)
315     {
316         PIResource_Zigbee *piResource = (PIResource_Zigbee *) OICMalloc(sizeof(*piResource));
317         if (!piResource)
318         {
319             OC_LOG (ERROR, TAG, "Out of memory");
320             return;
321         }
322         piResource->header.plugin = (PIPluginBase *)gPlugin;
323
324         OCStackResult result = buildURI(&piResource->header.piResource.uri,
325                                 PI_ZIGBEE_PREFIX,
326                                 device->nodeId,
327                                 device->endpointOfInterest->endpointId,
328                                 device->endpointOfInterest->clusterList->clusterIds[i].clusterId);
329
330         if(result != OC_STACK_OK)
331         {
332             OICFree(piResource);
333             return;
334         }
335
336         char * foundClusterID =
337             device->endpointOfInterest->clusterList->clusterIds[i].clusterId;
338
339         if (strcmp (foundClusterID, ZB_IAS_ZONE_CLUSTER) == 0)
340         {
341             piResource->header.piResource.resourceTypeName
342                 = getResourceTypeForIASZoneType (device);
343
344             OCStackResult ret = TWListenForStatusUpdates (device->nodeId,
345                                       device->endpointOfInterest->endpointId);
346
347             if (ret != OC_STACK_OK)
348             {
349                 // Just log it and move on if this fails?
350                 // or not create this resource at all?
351                 OC_LOG (ERROR, TAG, "Command to listen for status updates failed");
352             }
353         }
354         else
355         {
356             piResource->header.piResource.resourceTypeName =
357                     (char *) ZigBeeClusterIDToOICResourceType(foundClusterID);
358         }
359
360         if(piResource->header.piResource.resourceTypeName == NULL)
361         {
362             OC_LOG_V (ERROR, TAG, "unsupported clusterId : %s",
363                 device->endpointOfInterest->clusterList->clusterIds[i].clusterId);
364             OICFree(piResource->header.piResource.uri);
365             OICFree(piResource);
366             continue;
367         }
368         piResource->header.piResource.resourceInterfaceName =
369                             OC_RSRVD_INTERFACE_DEFAULT;
370
371         piResource->header.piResource.callbackParam = NULL;
372         piResource->header.piResource.resourceProperties = 0;
373         piResource->eui = OICStrdup(device->eui);
374         piResource->nodeId = OICStrdup(device->nodeId);
375         piResource->endpointId = OICStrdup(device->endpointOfInterest->endpointId);
376         piResource->clusterId =
377             OICStrdup(device->endpointOfInterest->clusterList->clusterIds[i].clusterId);
378         (*gPlugin)->header.NewResourceFoundCB(&(*gPlugin)->header, &piResource->header);
379     }
380 }
381
382 void zigbeeZoneStatusUpdate(TWUpdate * update)
383 {
384     if(!update)
385     {
386         return;
387     }
388
389     char * uri = NULL;
390     OCStackResult result = buildURI(&uri,
391                                     PI_ZIGBEE_PREFIX,
392                                     update->nodeId,
393                                     update->endpoint,
394                                     ZB_IAS_ZONE_CLUSTER);
395     if(result != OC_STACK_OK || !uri)
396     {
397         OC_LOG_V(ERROR, TAG, "Failed to build URI with result: %d", result);
398         return;
399     }
400
401     (*gPlugin)->header.ObserveNotificationUpdate((PIPluginBase *)*gPlugin, uri);
402     OICFree(uri);
403 }
404
405 OCStackResult ZigbeeInit(const char * comPort, PIPlugin_Zigbee ** plugin,
406                          PINewResourceFound newResourceCB,
407                          PIObserveNotificationUpdate observeNotificationUpdate)
408 {
409     if(!plugin)
410     {
411         return OC_STACK_INVALID_PARAM;
412     }
413     *plugin = (PIPlugin_Zigbee *) OICMalloc(sizeof(PIPlugin_Zigbee) + sizeof(PIPluginBase));
414     if(!*plugin)
415     {
416         return OC_STACK_NO_MEMORY;
417     }
418     ((*plugin)->header).type = PLUGIN_ZIGBEE;
419     ((*plugin)->header).comPort = comPort;
420     ((*plugin)->header).NewResourceFoundCB = newResourceCB;
421     ((*plugin)->header).ObserveNotificationUpdate = observeNotificationUpdate;
422     ((*plugin)->header).next = NULL;
423     ((*plugin)->header).resourceList = NULL;
424     ((*plugin)->header).processEHRequest = ProcessEHRequest;
425
426     gPlugin = plugin;
427     OCStackResult result = TWInitialize(comPort);
428     if(result != OC_STACK_OK)
429     {
430         return result;
431     }
432
433     return TWSetStatusUpdateCallback(zigbeeZoneStatusUpdate);
434 }
435
436 OCStackResult ZigbeeDiscover(PIPlugin_Zigbee * plugin)
437 {
438     OCStackResult result = OC_STACK_ERROR;
439     (void)plugin;
440     TWSetDiscoveryCallback(foundZigbeeCallback);
441     result = TWDiscover(NULL);
442     OC_LOG_V (DEBUG, TAG, "ZigbeeDiscover : Status = %d\n", result);
443
444     return result;
445 }
446
447 OCStackResult ZigbeeStop(PIPlugin_Zigbee * plugin)
448 {
449     free(plugin);
450     return TWUninitialize();
451 }
452
453 OCStackResult ZigbeeProcess(PIPlugin_Zigbee * plugin)
454 {
455     (void)plugin;
456     return TWProcess();
457 }
458
459 // Function returns an OIC Smart Home resource Type
460 // from the cluster ID. If the cluster is not supported, null is
461 // returned.
462 // NOTE: The returned string is NOT malloc'ed.
463 const char* ZigBeeClusterIDToOICResourceType(const char * clusterID) //Discovery/CreateResource
464 {
465     if (strcmp(clusterID, ZB_TEMPERATURE_CLUSTER) == 0)
466     {
467         return OIC_TEMPERATURE_SENSOR;
468     }
469     else if (strcmp(clusterID, ZB_LEVEL_CONTROL_CLUSTER) == 0)
470     {
471         return OIC_DIMMABLE_LIGHT;
472     }
473     else if (strcmp(clusterID, ZB_IAS_ZONE_CLUSTER) == 0)
474     {
475         return OIC_CONTACT_SENSOR;
476     }
477     else if (strcmp(clusterID, ZB_ON_OFF_CLUSTER) == 0)
478     {
479         return OIC_BINARY_SWITCH;
480     }
481     else
482     {
483         return NULL;
484     }
485 }
486
487 const char* OICResourceToZigBeeClusterID(char *oicResourceType)
488 {
489     if (strcmp(oicResourceType, OIC_TEMPERATURE_SENSOR) == 0)
490     {
491         return ZB_TEMPERATURE_CLUSTER;
492     }
493     else if (strcmp(oicResourceType, OIC_DIMMABLE_LIGHT) == 0)
494     {
495         return ZB_LEVEL_CONTROL_CLUSTER;
496     }
497     else if (strcmp(oicResourceType, OIC_CONTACT_SENSOR) == 0)
498     {
499         return ZB_IAS_ZONE_CLUSTER;
500     }
501     else if (strcmp(oicResourceType, OIC_BINARY_SWITCH) == 0)
502     {
503         return ZB_ON_OFF_CLUSTER;
504     }
505     else if (strcmp(oicResourceType, OIC_BINARY_SWITCH) == 0)
506     {
507         return ZB_INDICATOR_CLUSTER;
508     }
509     else
510     {
511         return NULL;
512     }
513 }
514
515 OCStackResult getZigBeeAttributesForOICResource(const char * OICResourceType,
516                                                     AttributeList *attributeList) // GET
517 {
518     if (strcmp (OICResourceType, OIC_TEMPERATURE_SENSOR) == 0)
519     {
520         attributeList->count = 1;
521         attributeList->list[0].oicAttribute = OICStrdup(OIC_TEMPERATURE_ATTRIBUTE);
522         attributeList->list[0].zigBeeAttribute = ZB_TEMPERATURE_ATTRIBUTE_ID;
523         attributeList->list[0].oicType = OIC_ATTR_DOUBLE;
524         attributeList->list[0].zigbeeType = ZB_16_SINT;
525         return OC_STACK_OK;
526     }
527     else if (strcmp (OICResourceType, OIC_DIMMABLE_LIGHT) == 0)
528     {
529         attributeList->count = 1;
530         attributeList->list[0].oicAttribute = OICStrdup(OIC_DIMMING_ATTRIBUTE);
531         attributeList->list[0].zigBeeAttribute = ZB_CURRENT_LEVEL_ATTRIBUTE_READONLY;
532         attributeList->list[0].oicType = OIC_ATTR_INT;
533         attributeList->list[0].zigbeeType = ZB_8_UINT;
534         return OC_STACK_OK;
535     }
536     else if (strcmp (OICResourceType, OIC_CONTACT_SENSOR) == 0)
537     {
538         attributeList->count = 1;
539         attributeList->list[0].oicAttribute = OICStrdup(OIC_CONTACT_ATTRIBUTE);
540         attributeList->list[0].zigBeeAttribute = ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID;
541         attributeList->list[0].oicType = OIC_ATTR_BOOL;
542         attributeList->list[0].zigbeeType = ZB_BOOL;
543         return OC_STACK_OK;
544     }
545     else if (strcmp (OICResourceType, OIC_BINARY_SWITCH) == 0)
546     {
547         attributeList->count = 1;
548         attributeList->list[0].oicAttribute = OICStrdup(OIC_ON_OFF_ATTRIBUTE);
549         attributeList->list[0].zigBeeAttribute = ZB_ON_OFF_ATTRIBUTE_ID;
550         attributeList->list[0].oicType = OIC_ATTR_BOOL;
551         attributeList->list[0].zigbeeType = ZB_BOOL;
552         return OC_STACK_OK;
553     }
554
555     return OC_STACK_ERROR;
556 }
557
558 bool getZigBeeAttributesIfValid(const char * OICResourceType,
559                                     AttributeList *attributeList,
560                                     OCRepPayload *payload) // Put
561 {
562     if(!OICResourceType)
563     {
564         return false;
565     }
566     if(strcmp(OICResourceType, OIC_TEMPERATURE_SENSOR) == 0)
567     {
568         // Cant really PUT on the temp sensor, but the code is still there.
569         int64_t temperature = 0;
570
571         // TODO: This if should only look for attributes it supports and ignore the rest
572         // or examine every attribute in the payload and complain about unsupported attributes?
573         if(OCRepPayloadGetPropInt(payload, OIC_TEMPERATURE_ATTRIBUTE, &temperature))
574         {
575             attributeList->count = 1;
576             attributeList->list[0].oicAttribute = OICStrdup(OIC_TEMPERATURE_ATTRIBUTE);
577             attributeList->list[0].zigBeeAttribute = ZB_TEMPERATURE_ATTRIBUTE_ID;
578             attributeList->list[0].oicType = OIC_ATTR_DOUBLE;
579             attributeList->list[0].val.d = temperature;
580             attributeList->list[0].zigbeeType = ZB_16_SINT;
581             attributeList->CIEMask = (CIECommandMask) 0;
582
583             return true;
584         }
585     }
586     else if (strcmp (OICResourceType, OIC_DIMMABLE_LIGHT) == 0)
587     {
588         int64_t onLevel = 0;
589
590         if(OCRepPayloadGetPropInt(payload, OIC_DIMMING_ATTRIBUTE, &onLevel))
591         {
592             attributeList->count = 1;
593             attributeList->list[0].oicAttribute = OICStrdup(OIC_DIMMING_ATTRIBUTE);
594             attributeList->list[0].zigBeeAttribute = ZB_ON_LEVEL_ATTRIBUTE;
595             attributeList->list[0].oicType = OIC_ATTR_INT;
596             attributeList->list[0].val.i = onLevel;
597             attributeList->list[0].zigbeeType = ZB_8_UINT;
598
599             // Level control cluster is dealing with level in the PUT payload.
600             attributeList->CIEMask = attributeList->CIEMask | CIE_MOVE_TO_LEVEL;
601             return true;
602         }
603     }
604     else if (strcmp (OICResourceType, OIC_CONTACT_SENSOR) == 0)
605     {
606         int64_t value = 0;
607
608         if(OCRepPayloadGetPropInt(payload, OIC_CONTACT_ATTRIBUTE, &value))
609         {
610             attributeList->count = 1;
611             attributeList->list[0].oicAttribute = OICStrdup(OIC_CONTACT_ATTRIBUTE);
612             attributeList->list[0].zigBeeAttribute = ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID;
613             attributeList->list[0].oicType = OIC_ATTR_BOOL;
614             attributeList->list[0].val.i = value;
615             attributeList->list[0].zigbeeType = ZB_BOOL;
616             attributeList->CIEMask = (CIECommandMask) 0;
617
618             return true;
619         }
620     }
621     else if (strcmp (OICResourceType, OIC_BINARY_SWITCH) == 0)
622     {
623         bool value = 0;
624
625         if(OCRepPayloadGetPropBool(payload, OIC_ON_OFF_ATTRIBUTE, &value))
626         {
627             attributeList->count = 1;
628             attributeList->list[0].oicAttribute = OICStrdup(OIC_ON_OFF_ATTRIBUTE);
629             attributeList->list[0].zigBeeAttribute = ZB_ON_OFF_ATTRIBUTE_ID;
630             attributeList->list[0].oicType = OIC_ATTR_BOOL;
631             attributeList->list[0].val.b = value;
632             attributeList->list[0].zigbeeType = ZB_BOOL;
633
634             attributeList->CIEMask = attributeList->CIEMask | CIE_RON_OFF;
635             return true;
636         }
637     }
638     return false;
639 }
640
641 OCEntityHandlerResult getDoubleValueFromString (const char *str, double *outDouble)
642 {
643     size_t hexOutValSize = strlen(HexPrepend) + strlen(str) + 1;
644     char * hexOutVal = (char *) OICCalloc(1, hexOutValSize);
645     if(!hexOutVal)
646     {
647         return OC_EH_ERROR;
648     }
649     OICStrcpy(hexOutVal, hexOutValSize, HexPrepend);
650     OICStrcat(hexOutVal, hexOutValSize, str);
651
652     char *endPtr;
653     errno = 0;
654     double value = strtod(hexOutVal, &endPtr);
655
656     if(errno != 0 || *endPtr != 0 || value == HUGE_VALF || value == HUGE_VALL)
657     {
658         OICFree(hexOutVal);
659         return OC_EH_ERROR;
660     }
661
662     OICFree(hexOutVal);
663     *outDouble = value;
664     return OC_EH_OK;
665
666 }
667
668 OCEntityHandlerResult processGetRequest (PIPluginBase * plugin,
669         OCEntityHandlerRequest *ehRequest, OCRepPayload **payload)
670 {
671     if (!plugin || !ehRequest || !payload)
672     {
673         return OC_EH_ERROR;
674     }
675     uint32_t attributeListIndex = 0;
676     OCStackResult stackResult = OC_STACK_OK;
677     PIResource_Zigbee * piResource = NULL;
678
679     AttributeList attributeList = { 0, (CIECommandMask) 0,
680         .list[0] = { NULL, NULL, OIC_ATTR_NULL, ZB_NULL, { .i = 0 } } };
681     stackResult = GetResourceFromHandle(plugin, (PIResource**) (&piResource),
682                         ehRequest->resource);
683     if (stackResult != OC_STACK_OK)
684     {
685         OC_LOG (ERROR, TAG, "Failed to get resource from handle");
686         return OC_EH_ERROR;
687     }
688     stackResult = getZigBeeAttributesForOICResource (
689         piResource->header.piResource.resourceTypeName, &attributeList);
690     if(stackResult != OC_STACK_OK)
691     {
692         OC_LOG_V (ERROR, TAG, "Failed to fetch attributes for %s",
693             piResource->header.piResource.resourceTypeName);
694         return OC_EH_ERROR;
695     }
696
697     *payload = OCRepPayloadCreate();
698     if(!payload)
699     {
700         OC_LOG(ERROR, TAG, PCF("Failed to allocate Payload"));
701         return OC_EH_ERROR;
702     }
703     bool boolRes = OCRepPayloadSetUri(*payload, piResource->header.piResource.uri);
704     if (boolRes == false)
705     {
706         OCRepPayloadDestroy (*payload);
707         return OC_EH_ERROR;
708     }
709     for(uint32_t i = 0; i<attributeList.count; i++)
710     {
711         char * outVal = NULL;
712         uint8_t outValLength = 0;
713
714         stackResult = TWGetAttribute(piResource->eui,
715                                      piResource->nodeId,
716                                      piResource->endpointId,
717                                      piResource->clusterId,
718                                      attributeList.list[i].zigBeeAttribute,
719                                      &outVal,
720                                      &outValLength);
721
722         if (stackResult != OC_STACK_OK || !outVal)
723         {
724             stackResult = OC_EH_ERROR;
725             OCRepPayloadDestroy (*payload);
726             goto exit;
727         }
728         if (attributeList.list[i].oicType == OIC_ATTR_INT)
729         {
730             char *endPtr = NULL;
731             // Third arg is 16 as outVal is a hex Number
732             uint64_t value = strtol (outVal, &endPtr, 16);
733
734             if (*endPtr != 0)
735             {
736                 return OC_EH_ERROR;
737             }
738             if (strcmp(attributeList.list[i].oicAttribute, OIC_DIMMING_ATTRIBUTE) == 0)
739             {
740                 // OIC Dimming operates between 0-100, while Zigbee operates
741                 // between 0-254 (ie. 0xFE).
742                 if (value > 0xFE)
743                 {
744                     value = 0xFE;
745                 }
746                 if (value <= 0xFE)
747                 {
748                     value = value / 2.54;
749                 }
750             }
751             boolRes = OCRepPayloadSetPropInt(*payload,
752                                              attributeList.list[i].oicAttribute,
753                                              (uint64_t) value);
754         }
755         else if (attributeList.list[i].oicType == OIC_ATTR_DOUBLE)
756         {
757             double value = 0;
758
759             if (getDoubleValueFromString (outVal, &value) != OC_EH_OK)
760             {
761                 return OC_EH_ERROR;
762             }
763             if (strcmp(piResource->clusterId, ZB_TEMPERATURE_CLUSTER) == 0)
764             {
765                 // Divide by 100 as temperature readings have a resolution of
766                 // 0.01 or one hundreth of a degree celsius.
767                 value = value/100;
768             }
769             boolRes = OCRepPayloadSetPropDouble(*payload,
770                                                 attributeList.list[i].oicAttribute,
771                                                 value);
772         }
773         else if (attributeList.list[i].oicType == OIC_ATTR_STRING)
774         {
775             boolRes = OCRepPayloadSetPropString(*payload,
776                                                 attributeList.list[i].oicAttribute,
777                                                 outVal);
778         }
779         else if (attributeList.list[i].oicType == OIC_ATTR_BOOL)
780         {
781             char *endPtr = NULL;
782             errno = 0;
783             // Third arg is 16 as outVal is a hex Number
784             uint64_t value = strtol (outVal, &endPtr, 16);
785
786             if (errno != 0 || *endPtr != 0)
787             {
788                 return OC_EH_ERROR;
789             }
790             // value COULD be a bit mask and the LSB indicates boolean true/false.
791             // If not a bit mask, it'll be plain 0 or 1.
792             value = value & 1;
793             boolRes = OCRepPayloadSetPropBool(*payload,
794                                               attributeList.list[i].oicAttribute,
795                                               value);
796         }
797
798         OICFree (outVal);
799     }
800
801     if (boolRes == false)
802     {
803         stackResult = OC_EH_ERROR;
804         goto exit;
805     }
806
807 exit:
808     for(; attributeListIndex < attributeList.count; attributeListIndex++)
809     {
810         OICFree(attributeList.list[attributeListIndex].oicAttribute);
811     }
812     return stackResult;
813 }
814
815 OCEntityHandlerResult processPutRequest(PIPluginBase * plugin,
816     OCEntityHandlerRequest *ehRequest, OCRepPayload **payload)
817 {
818     if (!plugin || !ehRequest || !payload)
819     {
820         return OC_EH_ERROR;
821     }
822     OCStackResult stackResult = OC_STACK_OK;
823     PIResource_Zigbee *piResource = NULL;
824     AttributeList attributeList = {
825         0,
826         (CIECommandMask) 0,
827         .list[0] = { NULL, NULL, OIC_ATTR_NULL, ZB_NULL, { .i = 0 } }
828     };
829
830     stackResult = GetResourceFromHandle(plugin,
831                                         ((PIResource **) (&piResource)),
832                                         ehRequest->resource);
833     if (stackResult != OC_STACK_OK)
834     {
835         OC_LOG (ERROR, TAG, "Failed to get resource from handle");
836         return OC_EH_ERROR;
837     }
838
839     bool boolRes = getZigBeeAttributesIfValid (
840                         piResource->header.piResource.resourceTypeName,
841                         &attributeList, *payload);
842     if(boolRes == false)
843     {
844         OC_LOG_V (ERROR, TAG, "Failed to fetch attributes for %s",
845             piResource->header.piResource.resourceTypeName);
846         return OC_EH_ERROR;
847     }
848
849     uint32_t i = 0;
850     for(; i<attributeList.count; i++)
851     {
852         if (attributeList.list[i].oicType == OIC_ATTR_INT)
853         {
854             char value[MAX_STRLEN_INT] = {};
855             if (attributeList.CIEMask || CIE_MOVE_TO_LEVEL)
856             {
857                 int64_t rangeDiff = 0;
858                 // OIC Dimming operates between 0-100, while Zigbee
859                 // operates between 0-254 (ie. 0xFE).
860                 rangeDiff = attributeList.list[i].val.i * 0xFE/100;
861                 if (rangeDiff > 0xFE)
862                 {
863                     rangeDiff = 0xFE;
864                 }
865                 if (rangeDiff < 0)
866                 {
867                     rangeDiff = 0;
868                 }
869                 if (rangeDiff <= 0xFE)
870                 {
871                     snprintf(value, sizeof(value), "%02x", (unsigned int) rangeDiff);
872                 }
873                 stackResult = TWMoveToLevel(piResource->nodeId, piResource->endpointId,
874                                     DEFAULT_MOVETOLEVEL_MODE, value, DEFAULT_TRANS_TIME);
875             }
876             else
877             {
878                 snprintf(value, sizeof(value), "%"PRId64, attributeList.list[i].val.i);
879                 stackResult = TWSetAttribute(piResource->eui,
880                     piResource->nodeId, piResource->endpointId,
881                     piResource->clusterId, attributeList.list[i].zigBeeAttribute,
882                     getZBDataTypeString(attributeList.list[i].zigbeeType), value);
883             }
884             if (stackResult != OC_STACK_OK)
885             {
886                 return OC_EH_ERROR;
887             }
888         }
889         else if (attributeList.list[i].oicType == OIC_ATTR_DOUBLE)
890         {
891             char value[MAX_STRLEN_DOUBLE] = {};
892             snprintf(value, sizeof(value), "%f", attributeList.list[i].val.d);
893             stackResult = TWSetAttribute(piResource->eui,
894                 piResource->nodeId, piResource->endpointId,
895                  piResource->clusterId, attributeList.list[i].zigBeeAttribute,
896                  getZBDataTypeString(attributeList.list[i].zigbeeType), value);
897         }
898         else if (attributeList.list[i].oicType == OIC_ATTR_STRING)
899         {
900             stackResult = TWSetAttribute(piResource->eui,
901                 piResource->nodeId, piResource->endpointId,
902                 piResource->clusterId, attributeList.list[i].zigBeeAttribute,
903                 getZBDataTypeString(attributeList.list[i].zigbeeType),
904                 attributeList.list[i].val.str);
905             if (stackResult != OC_STACK_OK)
906             {
907                 return OC_EH_ERROR;
908             }
909         }
910         else if (attributeList.list[i].oicType == OIC_ATTR_BOOL)
911         {
912             char * value = attributeList.list[i].val.b ? "1" : "0";
913             if (attributeList.CIEMask || CIE_RON_OFF)
914             {
915                 stackResult = TWSwitchOnOff(piResource->nodeId, piResource->endpointId, value);
916             }
917             else
918             {
919                 stackResult = TWSetAttribute(piResource->eui,
920                     piResource->nodeId, piResource->endpointId,
921                     piResource->clusterId, attributeList.list[i].zigBeeAttribute,
922                     getZBDataTypeString(attributeList.list[i].zigbeeType),
923                     value);
924             }
925             if (stackResult != OC_STACK_OK)
926             {
927                 return OC_EH_ERROR;
928             }
929         }
930         else
931         {
932             continue;
933         }
934     }
935
936     return processGetRequest(plugin, ehRequest, payload);
937 }
938
939 OCEntityHandlerResult ProcessEHRequest(PIPluginBase * plugin,
940     OCEntityHandlerRequest *ehRequest, OCRepPayload **payload)
941 {
942     if(!ehRequest || !payload)
943     {
944         return OC_EH_ERROR;
945     }
946     if(ehRequest->method == OC_REST_GET)
947     {
948         return processGetRequest(plugin, ehRequest, payload);
949     }
950     else if(ehRequest->method == OC_REST_PUT)
951     {
952         return processPutRequest(plugin, ehRequest, payload);
953     }
954     else
955     {
956         return OC_EH_FORBIDDEN;
957     }
958 }
959
960 char * getZBDataTypeString(ZigBeeAttributeDataType attrType)
961 {
962     switch (attrType)
963     {
964         case ZB_NULL:
965             return ZB_DATA_TYPE_NULL;
966         case ZB_8_BIT:
967             return ZB_DATA_TYPE_1_BYTE;
968         case ZB_16_BIT:
969             return ZB_DATA_TYPE_2_BYTE;
970         case ZB_24_BIT:
971             return ZB_DATA_TYPE_3_BYTE;
972         case ZB_32_BIT:
973             return ZB_DATA_TYPE_4_BYTE;
974         case ZB_40_BIT:
975             return ZB_DATA_TYPE_5_BYTE;
976         case ZB_48_BIT:
977             return ZB_DATA_TYPE_6_BYTE;
978         case ZB_56_BIT:
979             return ZB_DATA_TYPE_7_BYTE;
980         case ZB_64_BIT:
981             return ZB_DATA_TYPE_8_BYTE;
982         case ZB_BOOL:
983             return ZB_DATA_TYPE_BOOL;
984         case ZB_8_BITMAP:
985             return ZB_DATA_TYPE_1_BYTE;
986         case ZB_16_BITMAP:
987             return ZB_DATA_TYPE_2_BYTE;
988         case ZB_24_BITMAP:
989             return ZB_DATA_TYPE_3_BYTE;
990         case ZB_32_BITMAP:
991             return ZB_DATA_TYPE_4_BYTE;
992         case ZB_40_BITMAP:
993             return ZB_DATA_TYPE_5_BYTE;
994         case ZB_48_BITMAP:
995             return ZB_DATA_TYPE_6_BYTE;
996         case ZB_56_BITMAP:
997             return ZB_DATA_TYPE_7_BYTE;
998         case ZB_64_BITMAP:
999             return ZB_DATA_TYPE_8_BYTE;
1000         case ZB_16_SINT:
1001             return ZB_DATA_TYPE_SIGNED_INT_16;
1002         case ZB_8_UINT:
1003             return ZB_DATA_TYPE_UNSIGNED_INT_8;
1004         case ZB_16_UINT:
1005             return ZB_DATA_TYPE_UNSIGNED_INT_16;
1006         default:
1007             return ZB_DATA_TYPE_NULL;
1008     }
1009 }