1 //******************************************************************
3 // Copyright 2015 Intel Mobile Communications GmbH All Rights Reserved.
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
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
11 // http://www.apache.org/licenses/LICENSE-2.0
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.
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
24 * This file contains defined interface for the Zigbee Radio.
36 #include <inttypes.h> // To convert "int64_t" to string.
40 // The following #define must be here under "math.h".
41 // This ifdef ensures that "__STDC_IEC_559__" is defined. If it is defined,
42 // then we are guaranteed that the 'double' type is 64-bit. Otherwise, the
43 // compilation of this file should fail because we are no longer guaranteed.
44 #ifndef __STDC_IEC_559__
45 #error "Requires IEEE 754 floating point!"
48 #include "zigbee_wrapper.h"
49 #include "telegesis_wrapper.h"
50 #include "pluginlist.h"
52 #include "ocpayload.h"
53 #include "oic_malloc.h"
54 #include "oic_string.h"
57 #define HexPrepend "0x"
59 #define TAG "zigbeeWrapper"
61 // TODO: These should eventually go into an XML/JSON/Mapping thing
62 #define MAX_ATTRIBUTES 10
63 #define ZB_TEMPERATURE_CLUSTER "0402"
64 #define ZB_TEMPERATURE_ATTRIBUTE_ID "0000"
65 #define ZB_CURRENT_LEVEL_ATTRIBUTE_READONLY "0000"
66 #define ZB_ON_LEVEL_ATTRIBUTE "0011"
67 #define ZB_LEVEL_CONTROL_CLUSTER "0008"
68 #define ZB_IAS_ZONE_CLUSTER "0500"
69 #define ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID "0002"
70 #define ZB_INDICATOR_CLUSTER "0003"
71 #define ZB_INDICATOR_ATTRIBUTE_ID "0000"
72 #define ZB_ON_OFF_CLUSTER "0006"
73 #define ZB_ON_OFF_ATTRIBUTE_ID "0000"
74 #define ZB_IAS_ZONE_TYPE_ATTRIBUTE_ID "0001"
75 #define ZB_COLOR_CONTROL_CLUSTER "0300"
76 #define ZB_COLOR_TEMPERATURE_ATTRIBUTE_ID "0007"
78 #define IAS_ZONE_TYPE_MOTION_SENSOR "000D"
79 #define IAS_ZONE_TYPE_CONTACT_SENSOR "0015"
80 #define IAS_ZONE_TYPE_WATER_SENSOR "002A"
82 #define ZB_DATA_TYPE_NULL "00"
83 #define ZB_DATA_TYPE_1_BYTE "08"
84 #define ZB_DATA_TYPE_2_BYTE "09"
85 #define ZB_DATA_TYPE_3_BYTE "0a"
86 #define ZB_DATA_TYPE_4_BYTE "0b"
87 #define ZB_DATA_TYPE_5_BYTE "0c"
88 #define ZB_DATA_TYPE_6_BYTE "0d"
89 #define ZB_DATA_TYPE_7_BYTE "0e"
90 #define ZB_DATA_TYPE_8_BYTE "0f"
91 #define ZB_DATA_TYPE_BOOL "10"
92 #define ZB_DATA_TYPE_SIGNED_INT_16 "29"
93 #define ZB_DATA_TYPE_UNSIGNED_INT_8 "20"
94 #define ZB_DATA_TYPE_UNSIGNED_INT_16 "21"
96 #define MAX_STRLEN_INT (10)
97 // DBL_MANT_DIG = Max # of digits after decimal after the leading zeros.
98 // DBL_MIN_EXP = Max # of leading zeros of the mantissa.
99 // Magic number '3' represents a '-' (negative sign), '0' (a possible zero), and '.' (a period).
100 // "-0." from a number like "-0.999999991245", the "-0." adds 3 unaccounted characters.
101 #define MAX_STRLEN_DOUBLE (3 + DBL_MANT_DIG - DBL_MIN_EXP)
102 #define MAX_STRLEN_BOOL (1)
104 #define DEFAULT_TRANS_TIME "0000"
105 #define DEFAULT_MOVETOLEVEL_MODE "0"
107 static const char* OIC_TEMPERATURE_SENSOR = "oic.r.temperature";
108 static const char* OIC_DIMMABLE_LIGHT = "oic.r.light.dimming";
109 static const char* OIC_CONTACT_SENSOR = "oic.r.sensor.contact";
110 static const char* OIC_MOTION_SENSOR = "oic.r.sensor.motion";
111 static const char* OIC_WATER_SENSOR = "oic.r.sensor.water";
112 static const char* OIC_BINARY_SWITCH = "oic.r.switch.binary";
113 static const char* OIC_CHROMA_LIGHT = "oic.r.colour.chroma";
115 static const char* OIC_TEMPERATURE_ATTRIBUTE = "temperature";
116 static const char* OIC_DIMMING_ATTRIBUTE = "dimmingSetting";
117 static const char* OIC_CONTACT_ATTRIBUTE = "value";
118 static const char* OIC_WATER_ATTRIBUTE = "value";
119 static const char* OIC_MOTION_ATTRIBUTE = "value";
120 static const char* OIC_ON_OFF_ATTRIBUTE = "value";
121 static const char* OIC_COLOUR_TEMPERATURE_ATTRIBUTE = "colourspacevalue";
127 ZB_16_BIT, // 2 bytes
128 ZB_24_BIT, // 3 bytes
129 ZB_32_BIT, // 4 bytes
130 ZB_40_BIT, // 5 bytes
131 ZB_48_BIT, // 6 bytes
132 ZB_56_BIT, // 7 bytes
133 ZB_64_BIT, // 8 bytes
146 } ZigBeeAttributeDataType;
148 char * getZBDataTypeString(ZigBeeAttributeDataType attrType);
149 OCEntityHandlerResult ProcessEHRequest(PIPluginBase * plugin, OCEntityHandlerRequest *ehRequest,
150 OCRepPayload **payload);
164 char *zigBeeAttribute;
165 OICAttributeType oicType;
166 ZigBeeAttributeDataType zigbeeType;
175 } OICZigBeeAttributePair;
179 CIE_RON_OFF = 1 << 1,
180 CIE_MOVE_TO_LEVEL = 1 << 2
187 CIECommandMask CIEMask;
188 OICZigBeeAttributePair list[MAX_ATTRIBUTES];
191 const char* ZigBeeClusterIDToOICResourceType(const char * clusterID);
193 OCStackResult getZigBeeAttributesForOICResource(const char * OICResourceType,
194 AttributeList *attributeList);
196 bool getZigBeeAttributesIfValid(const char * OICResourceType,
197 AttributeList *attributeList,
198 OCRepPayload *payload);
200 const char * getResourceTypeForIASZoneType(TWDevice *device, PIPluginBase* plugin)
206 char *IASZoneType = NULL;
207 const char *resourceType = NULL;
210 OCStackResult ret = TWGetAttribute(
213 device->endpointOfInterest->endpointId,
215 ZB_IAS_ZONE_TYPE_ATTRIBUTE_ID,
218 (PIPlugin_Zigbee*)plugin
221 if (ret != OC_STACK_OK || !IASZoneType)
223 OIC_LOG_V(ERROR, TAG, "Error %u getting IAS Zone Type", ret);
227 if (strcmp(IASZoneType, IAS_ZONE_TYPE_CONTACT_SENSOR) == 0)
229 resourceType = OIC_CONTACT_SENSOR;
231 else if (strcmp(IASZoneType, IAS_ZONE_TYPE_MOTION_SENSOR) == 0)
233 resourceType = OIC_MOTION_SENSOR;
235 else if (strcmp(IASZoneType, IAS_ZONE_TYPE_WATER_SENSOR) == 0)
237 resourceType = OIC_WATER_SENSOR;
241 OIC_LOG_V(ERROR, TAG, "Unsupported Zone Type %s", IASZoneType);
245 OICFree(IASZoneType);
250 OCStackResult buildURI(char ** output,
253 const char * endpointId,
254 const char * clusterId)
256 if(!output || !prefix || !eui || !endpointId || !clusterId)
258 return OC_STACK_INVALID_PARAM;
260 const char LEN_SEPARATOR[] = "/";
261 size_t lenSeparatorSize = sizeof(LEN_SEPARATOR) - 1;
262 size_t newUriSize = strlen(prefix) + lenSeparatorSize +
263 strlen(eui) + lenSeparatorSize +
264 strlen(endpointId) + lenSeparatorSize +
266 + 1; // NULL Terminator
267 *output = (char *) OICCalloc(1, newUriSize);
271 OIC_LOG(ERROR, TAG, "Out of memory");
272 return OC_STACK_NO_MEMORY;
275 char * temp = OICStrcpy(*output, newUriSize, prefix);
280 temp = OICStrcat(*output, newUriSize, LEN_SEPARATOR);
285 temp = OICStrcat(*output, newUriSize, eui);
290 temp = OICStrcat(*output, newUriSize, LEN_SEPARATOR);
295 temp = OICStrcat(*output, newUriSize, endpointId);
300 temp = OICStrcat(*output, newUriSize, LEN_SEPARATOR);
305 temp = OICStrcat(*output, newUriSize, clusterId);
316 return OC_STACK_NO_MEMORY;
319 void foundZigbeeCallback(TWDevice *device, PIPlugin_Zigbee* plugin)
323 OIC_LOG(ERROR, TAG, "foundZigbeeCallback: Invalid parameter.");
326 int count = device->endpointOfInterest->clusterList->count;
327 for (int i=0; i < count; i++)
329 PIResource_Zigbee *piResource = (PIResource_Zigbee *) OICMalloc(sizeof(*piResource));
332 OIC_LOG(ERROR, TAG, "Out of memory");
335 piResource->header.plugin = (PIPluginBase *)plugin;
337 OCStackResult result = buildURI(&piResource->header.piResource.uri,
340 device->endpointOfInterest->endpointId,
341 device->endpointOfInterest->clusterList->clusterIds[i].clusterId);
343 if (result != OC_STACK_OK)
349 char * foundClusterID =
350 device->endpointOfInterest->clusterList->clusterIds[i].clusterId;
352 if (strcmp(foundClusterID, ZB_IAS_ZONE_CLUSTER) == 0)
354 piResource->header.piResource.resourceTypeName
355 = getResourceTypeForIASZoneType (device, (PIPluginBase *)plugin);
357 OCStackResult ret = TWListenForStatusUpdates(device->nodeId,
358 device->endpointOfInterest->endpointId,
361 if (ret != OC_STACK_OK)
363 // Just log it and move on if this fails?
364 // or not create this resource at all?
365 OIC_LOG(ERROR, TAG, "Command to listen for status updates failed");
370 piResource->header.piResource.resourceTypeName =
371 (char *) ZigBeeClusterIDToOICResourceType(foundClusterID);
374 if (piResource->header.piResource.resourceTypeName == NULL)
376 OIC_LOG_V(ERROR, TAG, "unsupported clusterId : %s",
377 device->endpointOfInterest->clusterList->clusterIds[i].clusterId);
378 OICFree(piResource->header.piResource.uri);
382 piResource->header.piResource.resourceInterfaceName =
383 OC_RSRVD_INTERFACE_DEFAULT;
385 piResource->header.piResource.callbackParam = NULL;
386 piResource->header.piResource.resourceProperties = 0;
387 piResource->eui = OICStrdup(device->eui);
388 piResource->nodeId = OICStrdup(device->nodeId);
389 piResource->endpointId = OICStrdup(device->endpointOfInterest->endpointId);
390 piResource->clusterId =
391 OICStrdup(device->endpointOfInterest->clusterList->clusterIds[i].clusterId);
392 plugin->header.NewResourceFoundCB(&(plugin)->header, &piResource->header);
396 void zigbeeZoneStatusUpdate(TWUpdate * update, PIPlugin_Zigbee* plugin)
403 PIResource_Zigbee * piResource = NULL;
404 OCStackResult result = GetResourceFromZigBeeNodeId((PIPluginBase *)plugin,
408 ZB_IAS_ZONE_CLUSTER);
409 if (result != OC_STACK_OK || !piResource)
411 OIC_LOG_V(ERROR, TAG, "Failed to retrieve resource handle with result: %d", result);
415 plugin->header.ObserveNotificationUpdate((PIPluginBase *)plugin,
416 piResource->header.piResource.resourceHandle);
419 void deviceNodeIdChanged(const char * eui, const char * nodeId, PIPlugin_Zigbee* plugin)
425 OCStackResult result = UpdateZigbeeResourceNodeId((PIPluginBase *)plugin,
428 if(result != OC_STACK_OK)
430 OIC_LOG_V(ERROR, TAG, "Failed to update Zigbee Resource NodeId due to result: %s", result);
434 OCStackResult ZigbeeInit(const char * comPort, PIPlugin_Zigbee ** plugin,
435 PINewResourceFound newResourceCB,
436 PIObserveNotificationUpdate observeNotificationUpdate)
440 return OC_STACK_INVALID_PARAM;
442 *plugin = (PIPlugin_Zigbee *) OICMalloc(sizeof(PIPlugin_Zigbee) + sizeof(PIPluginBase));
445 return OC_STACK_NO_MEMORY;
447 ((*plugin)->header).type = PLUGIN_ZIGBEE;
448 ((*plugin)->header).comPort = comPort;
449 ((*plugin)->header).NewResourceFoundCB = newResourceCB;
450 ((*plugin)->header).ObserveNotificationUpdate = observeNotificationUpdate;
451 ((*plugin)->header).next = NULL;
452 ((*plugin)->header).resourceList = NULL;
453 ((*plugin)->header).processEHRequest = ProcessEHRequest;
455 OCStackResult result = TWInitialize(*plugin, comPort);
456 if (result != OC_STACK_OK)
460 result = TWSetStatusUpdateCallback(zigbeeZoneStatusUpdate, *plugin);
461 if(result != OC_STACK_OK)
465 return TWSetEndDeviceNodeIdChangedCallback(deviceNodeIdChanged, *plugin);
468 OCStackResult ZigbeeDiscover(PIPlugin_Zigbee * plugin)
470 OCStackResult result = OC_STACK_ERROR;
471 TWSetDiscoveryCallback(foundZigbeeCallback, plugin);
472 result = TWDiscover(plugin);
473 OIC_LOG_V(DEBUG, TAG, "ZigbeeDiscover : Status = %d\n", result);
478 OCStackResult ZigbeeStop(PIPlugin_Zigbee * plugin)
480 OCStackResult ret = TWUninitialize(plugin);
485 OCStackResult ZigbeeProcess(PIPlugin_Zigbee * plugin)
487 return TWProcess(plugin);
490 // Function returns an OIC Smart Home resource Type
491 // from the cluster ID. If the cluster is not supported, null is
493 // NOTE: The returned string is NOT malloc'ed.
494 const char* ZigBeeClusterIDToOICResourceType(const char * clusterID) //Discovery/CreateResource
500 if (strcmp(clusterID, ZB_TEMPERATURE_CLUSTER) == 0)
502 return OIC_TEMPERATURE_SENSOR;
504 else if (strcmp(clusterID, ZB_LEVEL_CONTROL_CLUSTER) == 0)
506 return OIC_DIMMABLE_LIGHT;
508 else if (strcmp(clusterID, ZB_IAS_ZONE_CLUSTER) == 0)
510 return OIC_CONTACT_SENSOR;
512 else if (strcmp(clusterID, ZB_ON_OFF_CLUSTER) == 0)
514 return OIC_BINARY_SWITCH;
516 else if (strcmp(clusterID, ZB_COLOR_CONTROL_CLUSTER) == 0)
518 return OIC_CHROMA_LIGHT;
526 const char* OICResourceToZigBeeClusterID(char *oicResourceType)
528 if (!oicResourceType)
532 if (strcmp(oicResourceType, OIC_TEMPERATURE_SENSOR) == 0)
534 return ZB_TEMPERATURE_CLUSTER;
536 else if (strcmp(oicResourceType, OIC_DIMMABLE_LIGHT) == 0)
538 return ZB_LEVEL_CONTROL_CLUSTER;
540 else if (strcmp(oicResourceType, OIC_CONTACT_SENSOR) == 0)
542 return ZB_IAS_ZONE_CLUSTER;
544 else if (strcmp(oicResourceType, OIC_BINARY_SWITCH) == 0)
546 return ZB_ON_OFF_CLUSTER;
548 else if (strcmp(oicResourceType, OIC_BINARY_SWITCH) == 0)
550 return ZB_INDICATOR_CLUSTER;
552 else if (strcmp(oicResourceType, OIC_CHROMA_LIGHT) == 0)
554 return ZB_COLOR_CONTROL_CLUSTER;
562 OCStackResult getZigBeeAttributesForOICResource(const char * OICResourceType,
563 AttributeList *attributeList) // GET
565 if (!OICResourceType || !attributeList)
567 return OC_STACK_INVALID_PARAM;
569 if (strcmp(OICResourceType, OIC_TEMPERATURE_SENSOR) == 0)
571 attributeList->count = 1;
572 attributeList->list[0].oicAttribute = OICStrdup(OIC_TEMPERATURE_ATTRIBUTE);
573 attributeList->list[0].zigBeeAttribute = ZB_TEMPERATURE_ATTRIBUTE_ID;
574 attributeList->list[0].oicType = OIC_ATTR_DOUBLE;
575 attributeList->list[0].zigbeeType = ZB_16_SINT;
578 else if (strcmp(OICResourceType, OIC_DIMMABLE_LIGHT) == 0)
580 attributeList->count = 1;
581 attributeList->list[0].oicAttribute = OICStrdup(OIC_DIMMING_ATTRIBUTE);
582 attributeList->list[0].zigBeeAttribute = ZB_CURRENT_LEVEL_ATTRIBUTE_READONLY;
583 attributeList->list[0].oicType = OIC_ATTR_INT;
584 attributeList->list[0].zigbeeType = ZB_8_UINT;
587 else if (strcmp(OICResourceType, OIC_CONTACT_SENSOR) == 0)
589 attributeList->count = 1;
590 attributeList->list[0].oicAttribute = OICStrdup(OIC_CONTACT_ATTRIBUTE);
591 attributeList->list[0].zigBeeAttribute = ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID;
592 attributeList->list[0].oicType = OIC_ATTR_BOOL;
593 attributeList->list[0].zigbeeType = ZB_BOOL;
596 else if (strcmp(OICResourceType, OIC_WATER_SENSOR) == 0)
598 attributeList->count = 1;
599 attributeList->list[0].oicAttribute = OICStrdup(OIC_WATER_ATTRIBUTE);
600 attributeList->list[0].zigBeeAttribute = ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID;
601 attributeList->list[0].oicType = OIC_ATTR_BOOL;
602 attributeList->list[0].zigbeeType = ZB_BOOL;
605 else if (strcmp(OICResourceType, OIC_MOTION_SENSOR) == 0)
607 attributeList->count = 1;
608 attributeList->list[0].oicAttribute = OICStrdup(OIC_MOTION_ATTRIBUTE);
609 attributeList->list[0].zigBeeAttribute = ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID;
610 attributeList->list[0].oicType = OIC_ATTR_BOOL;
611 attributeList->list[0].zigbeeType = ZB_BOOL;
614 else if (strcmp(OICResourceType, OIC_BINARY_SWITCH) == 0)
616 attributeList->count = 1;
617 attributeList->list[0].oicAttribute = OICStrdup(OIC_ON_OFF_ATTRIBUTE);
618 attributeList->list[0].zigBeeAttribute = ZB_ON_OFF_ATTRIBUTE_ID;
619 attributeList->list[0].oicType = OIC_ATTR_BOOL;
620 attributeList->list[0].zigbeeType = ZB_BOOL;
623 else if (strcmp(OICResourceType, OIC_CHROMA_LIGHT) == 0)
625 attributeList->count = 1;
626 attributeList->list[0].oicAttribute = OICStrdup(OIC_COLOUR_TEMPERATURE_ATTRIBUTE);
627 attributeList->list[0].zigBeeAttribute = ZB_COLOR_TEMPERATURE_ATTRIBUTE_ID;
628 attributeList->list[0].oicType = OIC_ATTR_INT;
629 attributeList->list[0].zigbeeType = ZB_16_UINT;
633 return OC_STACK_ERROR;
636 bool getZigBeeAttributesIfValid(const char * OICResourceType,
637 AttributeList *attributeList,
638 OCRepPayload *payload) // Put
640 if (!OICResourceType)
644 if (strcmp(OICResourceType, OIC_TEMPERATURE_SENSOR) == 0)
646 // Cant really PUT on the temp sensor, but the code is still there.
647 int64_t temperature = 0;
649 // TODO: This if should only look for attributes it supports and ignore the rest
650 // or examine every attribute in the payload and complain about unsupported attributes?
651 if (OCRepPayloadGetPropInt(payload, OIC_TEMPERATURE_ATTRIBUTE, &temperature))
653 attributeList->count = 1;
654 attributeList->list[0].oicAttribute = OICStrdup(OIC_TEMPERATURE_ATTRIBUTE);
655 attributeList->list[0].zigBeeAttribute = ZB_TEMPERATURE_ATTRIBUTE_ID;
656 attributeList->list[0].oicType = OIC_ATTR_DOUBLE;
657 attributeList->list[0].val.d = temperature;
658 attributeList->list[0].zigbeeType = ZB_16_SINT;
659 attributeList->CIEMask = (CIECommandMask) 0;
664 else if (strcmp(OICResourceType, OIC_DIMMABLE_LIGHT) == 0)
668 if (OCRepPayloadGetPropInt(payload, OIC_DIMMING_ATTRIBUTE, &onLevel))
670 attributeList->count = 1;
671 attributeList->list[0].oicAttribute = OICStrdup(OIC_DIMMING_ATTRIBUTE);
672 attributeList->list[0].zigBeeAttribute = ZB_ON_LEVEL_ATTRIBUTE;
673 attributeList->list[0].oicType = OIC_ATTR_INT;
674 attributeList->list[0].val.i = onLevel;
675 attributeList->list[0].zigbeeType = ZB_8_UINT;
677 // Level control cluster is dealing with level in the PUT payload.
678 attributeList->CIEMask = attributeList->CIEMask | CIE_MOVE_TO_LEVEL;
682 else if (strcmp(OICResourceType, OIC_CONTACT_SENSOR) == 0)
686 if (OCRepPayloadGetPropInt(payload, OIC_CONTACT_ATTRIBUTE, &value))
688 attributeList->count = 1;
689 attributeList->list[0].oicAttribute = OICStrdup(OIC_CONTACT_ATTRIBUTE);
690 attributeList->list[0].zigBeeAttribute = ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID;
691 attributeList->list[0].oicType = OIC_ATTR_BOOL;
692 attributeList->list[0].val.i = value;
693 attributeList->list[0].zigbeeType = ZB_BOOL;
694 attributeList->CIEMask = (CIECommandMask) 0;
699 else if (strcmp(OICResourceType, OIC_WATER_SENSOR) == 0)
703 if (OCRepPayloadGetPropInt(payload, OIC_WATER_ATTRIBUTE, &value))
705 attributeList->count = 1;
706 attributeList->list[0].oicAttribute = OICStrdup(OIC_WATER_ATTRIBUTE);
707 attributeList->list[0].zigBeeAttribute = ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID;
708 attributeList->list[0].oicType = OIC_ATTR_BOOL;
709 attributeList->list[0].val.i = value;
710 attributeList->list[0].zigbeeType = ZB_BOOL;
711 attributeList->CIEMask = (CIECommandMask) 0;
716 else if (strcmp(OICResourceType, OIC_BINARY_SWITCH) == 0)
720 if (OCRepPayloadGetPropBool(payload, OIC_ON_OFF_ATTRIBUTE, &value))
722 attributeList->count = 1;
723 attributeList->list[0].oicAttribute = OICStrdup(OIC_ON_OFF_ATTRIBUTE);
724 attributeList->list[0].zigBeeAttribute = ZB_ON_OFF_ATTRIBUTE_ID;
725 attributeList->list[0].oicType = OIC_ATTR_BOOL;
726 attributeList->list[0].val.b = value;
727 attributeList->list[0].zigbeeType = ZB_BOOL;
729 attributeList->CIEMask = attributeList->CIEMask | CIE_RON_OFF;
733 else if (strcmp(OICResourceType, OIC_CHROMA_LIGHT) == 0)
736 if (OCRepPayloadGetPropString(payload, OIC_COLOUR_TEMPERATURE_ATTRIBUTE, &value))
738 attributeList->count = 1;
739 attributeList->list[0].oicAttribute = OICStrdup(OIC_COLOUR_TEMPERATURE_ATTRIBUTE);
740 attributeList->list[0].zigBeeAttribute = ZB_COLOR_TEMPERATURE_ATTRIBUTE_ID;
741 attributeList->list[0].oicType = OIC_ATTR_STRING;
742 attributeList->list[0].val.str = value;
743 attributeList->list[0].zigbeeType = ZB_16_UINT;
744 attributeList->CIEMask = (CIECommandMask) 0;
752 OCEntityHandlerResult getDoubleValueFromString(const char *str, double *outDouble)
754 if (!str || !outDouble)
758 size_t hexOutValSize = strlen(HexPrepend) + strlen(str) + 1;
759 char * hexOutVal = (char *) OICCalloc(1, hexOutValSize);
764 OICStrcpy(hexOutVal, hexOutValSize, HexPrepend);
765 OICStrcat(hexOutVal, hexOutValSize, str);
769 double value = strtod(hexOutVal, &endPtr);
771 if (errno != 0 || *endPtr != 0 || value == HUGE_VALF || value == HUGE_VALL)
783 OCEntityHandlerResult getColourTemperatureFromString(const char* str, int64_t* outVal)
789 // str comes in as "X,Y,T" where "T" is the colour temperature.
790 // Iterate 3 times to retrieve the last value.
791 char * strstr = OICStrdup(str);
792 char * savePtr = NULL;
794 for (int i=0; i<3; i++)
796 temp = strtok_r(strstr, ",", &savePtr);
804 OCStackResult result = getDoubleValueFromString(temp, (double *)outVal);
809 OCEntityHandlerResult processGetRequest(PIPluginBase * plugin,
810 OCEntityHandlerRequest *ehRequest, OCRepPayload **payload)
812 if (!plugin || !ehRequest || !payload)
816 uint32_t attributeListIndex = 0;
817 OCStackResult stackResult = OC_STACK_OK;
818 PIResource_Zigbee * piResource = NULL;
820 AttributeList attributeList = { 0, (CIECommandMask) 0,
821 .list[0] = { NULL, NULL, OIC_ATTR_NULL, ZB_NULL, { .i = 0 } } };
822 stackResult = GetResourceFromHandle(plugin, (PIResource**) (&piResource),
823 ehRequest->resource);
824 if (stackResult != OC_STACK_OK)
826 OIC_LOG(ERROR, TAG, "Failed to get resource from handle");
829 stackResult = getZigBeeAttributesForOICResource(
830 piResource->header.piResource.resourceTypeName, &attributeList);
831 if (stackResult != OC_STACK_OK)
833 OIC_LOG_V(ERROR, TAG, "Failed to fetch attributes for %s",
834 piResource->header.piResource.resourceTypeName);
838 *payload = OCRepPayloadCreate();
841 OIC_LOG(ERROR, TAG, PCF("Failed to allocate Payload"));
844 bool boolRes = OCRepPayloadSetUri(*payload, piResource->header.piResource.uri);
845 if (boolRes == false)
847 OCRepPayloadDestroy(*payload);
850 for (uint32_t i = 0; i<attributeList.count; i++)
852 char * outVal = NULL;
853 uint8_t outValLength = 0;
855 stackResult = TWGetAttribute(piResource->eui,
857 piResource->endpointId,
858 piResource->clusterId,
859 attributeList.list[i].zigBeeAttribute,
862 (PIPlugin_Zigbee *)plugin);
864 if (stackResult != OC_STACK_OK || !outVal)
866 stackResult = OC_EH_ERROR;
867 OCRepPayloadDestroy(*payload);
870 if (attributeList.list[i].oicType == OIC_ATTR_INT)
874 // Third arg is 16 as outVal is a hex Number
875 uint64_t value = strtol(outVal, &endPtr, 16);
877 if (*endPtr != 0 || errno != 0)
881 if (!attributeList.list[i].oicAttribute)
885 if (strcmp(attributeList.list[i].oicAttribute, OIC_DIMMING_ATTRIBUTE) == 0)
887 // OIC Dimming operates between 0-100, while Zigbee operates
888 // between 0-254 (ie. 0xFE).
895 value = value / 2.54;
898 else if (strcmp(attributeList.list[i].oicAttribute, OIC_COLOUR_TEMPERATURE_ATTRIBUTE) == 0)
900 // OIC Chroma requires: Hue, Saturation, and ColorSpaceValue (color space value is
901 // "chromaX, chromaY, colourTemperature").
902 // ZigBee HA requires: "currentX", "currentY", while we're trying to map a specific
903 // device which goes against the HA spec and ONLY implements "ColorTemperature".
904 // Because of the misalignments between OIC Smart Home and ZigBee HA specs, the
905 // follow assumption is required:
906 // - *X and *Y will be zero, and therefore ignored (or passed as zero where
908 // OIC ColorTemperature operates between 0-1000 (degrees Kelvin) in string form,
909 // while ZigBee operates between 0-65279 (bits, with an operating range
910 // 15.32-1,000,000 (degrees Kelvin)).
911 // ZigBee HA states: ColorTemperature(bits) = 1,000,000/ColorTemperature(Kelvin)
912 // Invalid: ColorTemperature==0 || ColorTemperature==65535
913 // However the specific bulb we're mapping only operates between 2700-6500 (Kelvin)
914 // which equates to 0x0099-0x0172 (ie. 153-370; giving a resolution of 217 bits).
915 // Conversion from ZigBee HA specific bulb to OIC temporary mapping:
916 // OIC Value: 0-100 ZigBee Value: (0-217)+153
917 // OICValue = (ZigBeeValue-153)/2.17
919 value = (value-153)/2.17;
921 boolRes = OCRepPayloadSetPropInt(*payload,
922 attributeList.list[i].oicAttribute,
925 else if (attributeList.list[i].oicType == OIC_ATTR_DOUBLE)
929 if (getDoubleValueFromString(outVal, &value) != OC_EH_OK)
933 if (!piResource->clusterId)
937 if (strcmp(piResource->clusterId, ZB_TEMPERATURE_CLUSTER) == 0)
939 // Divide by 100 as temperature readings have a resolution of
940 // 0.01 or one hundreth of a degree celsius.
943 boolRes = OCRepPayloadSetPropDouble(*payload,
944 attributeList.list[i].oicAttribute,
947 else if (attributeList.list[i].oicType == OIC_ATTR_STRING)
949 boolRes = OCRepPayloadSetPropString(*payload,
950 attributeList.list[i].oicAttribute,
953 else if (attributeList.list[i].oicType == OIC_ATTR_BOOL)
957 // Third arg is 16 as outVal is a hex Number
958 uint64_t value = strtol(outVal, &endPtr, 16);
960 if (errno != 0 || *endPtr != 0)
964 // value COULD be a bit mask and the LSB indicates boolean true/false.
965 // If not a bit mask, it'll be plain 0 or 1.
967 boolRes = OCRepPayloadSetPropBool(*payload,
968 attributeList.list[i].oicAttribute,
975 if (boolRes == false)
977 stackResult = OC_EH_ERROR;
982 for (; attributeListIndex < attributeList.count; attributeListIndex++)
984 OICFree(attributeList.list[attributeListIndex].oicAttribute);
989 OCEntityHandlerResult processPutRequest(PIPluginBase * plugin,
990 OCEntityHandlerRequest *ehRequest, OCRepPayload **payload)
992 if (!plugin || !ehRequest || !payload)
996 OCStackResult stackResult = OC_STACK_OK;
997 PIResource_Zigbee *piResource = NULL;
998 AttributeList attributeList = {
1001 .list[0] = { NULL, NULL, OIC_ATTR_NULL, ZB_NULL, { .i = 0 } }
1004 stackResult = GetResourceFromHandle(plugin,
1005 ((PIResource **) (&piResource)),
1006 ehRequest->resource);
1007 if (stackResult != OC_STACK_OK)
1009 OIC_LOG(ERROR, TAG, "Failed to get resource from handle");
1013 bool boolRes = getZigBeeAttributesIfValid(
1014 piResource->header.piResource.resourceTypeName,
1015 &attributeList, *payload);
1016 if (boolRes == false)
1018 OIC_LOG_V(ERROR, TAG, "Failed to fetch attributes for %s",
1019 piResource->header.piResource.resourceTypeName);
1024 for (; i<attributeList.count; i++)
1026 if (attributeList.list[i].oicType == OIC_ATTR_INT)
1028 char value[MAX_STRLEN_INT] = {};
1029 if (attributeList.CIEMask || CIE_MOVE_TO_LEVEL)
1031 int64_t rangeDiff = 0;
1032 // OIC Dimming operates between 0-100, while Zigbee
1033 // operates between 0-254 (ie. 0xFE).
1034 rangeDiff = attributeList.list[i].val.i * 0xFE/100;
1035 if (rangeDiff > 0xFE)
1043 if (rangeDiff <= 0xFE)
1046 int strRet = snprintf(value, sizeof(value), "%02x", (unsigned int) rangeDiff);
1049 OIC_LOG_V(ERROR, TAG, "Failed to parse string due to errno: %d", errno);
1053 stackResult = TWMoveToLevel(piResource->nodeId, piResource->endpointId,
1054 DEFAULT_MOVETOLEVEL_MODE, value, DEFAULT_TRANS_TIME,
1055 (PIPlugin_Zigbee*)plugin);
1060 int strRet = snprintf(value,
1063 attributeList.list[i].val.i);
1066 OIC_LOG_V(ERROR, TAG, "Failed to parse string due to errno: %d", errno);
1069 stackResult = TWSetAttribute(piResource->eui,
1070 piResource->nodeId, piResource->endpointId,
1071 piResource->clusterId, attributeList.list[i].zigBeeAttribute,
1072 getZBDataTypeString(attributeList.list[i].zigbeeType), value,
1073 (PIPlugin_Zigbee*)plugin);
1075 if (stackResult != OC_STACK_OK)
1080 else if (attributeList.list[i].oicType == OIC_ATTR_DOUBLE)
1082 char value[MAX_STRLEN_DOUBLE] = {};
1084 int strRet = snprintf(value, sizeof(value), "%f", attributeList.list[i].val.d);
1087 OIC_LOG_V(ERROR, TAG, "Failed to parse string due to errno: %d", errno);
1090 stackResult = TWSetAttribute(piResource->eui,
1091 piResource->nodeId, piResource->endpointId,
1092 piResource->clusterId, attributeList.list[i].zigBeeAttribute,
1093 getZBDataTypeString(attributeList.list[i].zigbeeType), value,
1094 (PIPlugin_Zigbee*)plugin);
1096 else if (attributeList.list[i].oicType == OIC_ATTR_STRING)
1098 if (strcmp(attributeList.list[i].oicAttribute, OIC_COLOUR_TEMPERATURE_ATTRIBUTE) == 0)
1100 char *endPtr = NULL;
1101 uint16_t zbVal = (uint16_t)strtod(attributeList.list[i].val.str, &endPtr);
1102 zbVal = (zbVal*2.17)+153;
1103 char value[5] = {0};
1105 int strRet = snprintf(value, sizeof(value), "%04x", zbVal);
1108 OIC_LOG_V(ERROR, TAG, "Failed to parse string due to errno: %d", errno);
1112 TWColorMoveToColorTemperature(piResource->nodeId, piResource->endpointId,
1113 value, DEFAULT_TRANS_TIME,
1114 (PIPlugin_Zigbee*)plugin);
1118 stackResult = TWSetAttribute(piResource->eui,
1119 piResource->nodeId, piResource->endpointId,
1120 piResource->clusterId, attributeList.list[i].zigBeeAttribute,
1121 getZBDataTypeString(attributeList.list[i].zigbeeType),
1122 attributeList.list[i].val.str,
1123 (PIPlugin_Zigbee*)plugin);
1125 if (stackResult != OC_STACK_OK)
1130 else if (attributeList.list[i].oicType == OIC_ATTR_BOOL)
1132 char * value = attributeList.list[i].val.b ? "1" : "0";
1133 if (attributeList.CIEMask || CIE_RON_OFF)
1135 stackResult = TWSwitchOnOff(piResource->nodeId, piResource->endpointId, value,
1136 (PIPlugin_Zigbee*)plugin);
1140 stackResult = TWSetAttribute(piResource->eui,
1141 piResource->nodeId, piResource->endpointId,
1142 piResource->clusterId, attributeList.list[i].zigBeeAttribute,
1143 getZBDataTypeString(attributeList.list[i].zigbeeType),
1145 (PIPlugin_Zigbee*)plugin);
1147 if (stackResult != OC_STACK_OK)
1158 return processGetRequest(plugin, ehRequest, payload);
1161 OCEntityHandlerResult ProcessEHRequest(PIPluginBase * plugin,
1162 OCEntityHandlerRequest *ehRequest, OCRepPayload **payload)
1164 if (!ehRequest || !payload)
1168 if (ehRequest->method == OC_REST_GET)
1170 return processGetRequest(plugin, ehRequest, payload);
1172 else if (ehRequest->method == OC_REST_PUT)
1174 return processPutRequest(plugin, ehRequest, payload);
1178 return OC_EH_FORBIDDEN;
1182 char * getZBDataTypeString(ZigBeeAttributeDataType attrType)
1187 return ZB_DATA_TYPE_NULL;
1189 return ZB_DATA_TYPE_1_BYTE;
1191 return ZB_DATA_TYPE_2_BYTE;
1193 return ZB_DATA_TYPE_3_BYTE;
1195 return ZB_DATA_TYPE_4_BYTE;
1197 return ZB_DATA_TYPE_5_BYTE;
1199 return ZB_DATA_TYPE_6_BYTE;
1201 return ZB_DATA_TYPE_7_BYTE;
1203 return ZB_DATA_TYPE_8_BYTE;
1205 return ZB_DATA_TYPE_BOOL;
1207 return ZB_DATA_TYPE_1_BYTE;
1209 return ZB_DATA_TYPE_2_BYTE;
1211 return ZB_DATA_TYPE_3_BYTE;
1213 return ZB_DATA_TYPE_4_BYTE;
1215 return ZB_DATA_TYPE_5_BYTE;
1217 return ZB_DATA_TYPE_6_BYTE;
1219 return ZB_DATA_TYPE_7_BYTE;
1221 return ZB_DATA_TYPE_8_BYTE;
1223 return ZB_DATA_TYPE_SIGNED_INT_16;
1225 return ZB_DATA_TYPE_UNSIGNED_INT_8;
1227 return ZB_DATA_TYPE_UNSIGNED_INT_16;
1229 return ZB_DATA_TYPE_NULL;