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 OC_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 OC_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 || !nodeId || !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(nodeId) + lenSeparatorSize +
264 strlen(endpointId) + lenSeparatorSize +
266 + 1; // NULL Terminator
267 *output = (char *) OICCalloc(1, newUriSize);
271 OC_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, nodeId);
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 OC_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 OC_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 OC_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 OC_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)
404 OCStackResult result = buildURI(&uri,
408 ZB_IAS_ZONE_CLUSTER);
409 if (result != OC_STACK_OK || !uri)
411 OC_LOG_V(ERROR, TAG, "Failed to build URI with result: %d", result);
415 plugin->header.ObserveNotificationUpdate((PIPluginBase *)plugin, uri);
419 OCStackResult ZigbeeInit(const char * comPort, PIPlugin_Zigbee ** plugin,
420 PINewResourceFound newResourceCB,
421 PIObserveNotificationUpdate observeNotificationUpdate)
425 return OC_STACK_INVALID_PARAM;
427 *plugin = (PIPlugin_Zigbee *) OICMalloc(sizeof(PIPlugin_Zigbee) + sizeof(PIPluginBase));
430 return OC_STACK_NO_MEMORY;
432 ((*plugin)->header).type = PLUGIN_ZIGBEE;
433 ((*plugin)->header).comPort = comPort;
434 ((*plugin)->header).NewResourceFoundCB = newResourceCB;
435 ((*plugin)->header).ObserveNotificationUpdate = observeNotificationUpdate;
436 ((*plugin)->header).next = NULL;
437 ((*plugin)->header).resourceList = NULL;
438 ((*plugin)->header).processEHRequest = ProcessEHRequest;
440 OCStackResult result = TWInitialize(*plugin, comPort);
441 if (result != OC_STACK_OK)
446 return TWSetStatusUpdateCallback(zigbeeZoneStatusUpdate, *plugin);
449 OCStackResult ZigbeeDiscover(PIPlugin_Zigbee * plugin)
451 OCStackResult result = OC_STACK_ERROR;
452 TWSetDiscoveryCallback(foundZigbeeCallback, plugin);
453 result = TWDiscover(plugin);
454 OC_LOG_V(DEBUG, TAG, "ZigbeeDiscover : Status = %d\n", result);
459 OCStackResult ZigbeeStop(PIPlugin_Zigbee * plugin)
461 OCStackResult ret = TWUninitialize(plugin);
466 OCStackResult ZigbeeProcess(PIPlugin_Zigbee * plugin)
468 return TWProcess(plugin);
471 // Function returns an OIC Smart Home resource Type
472 // from the cluster ID. If the cluster is not supported, null is
474 // NOTE: The returned string is NOT malloc'ed.
475 const char* ZigBeeClusterIDToOICResourceType(const char * clusterID) //Discovery/CreateResource
481 if (strcmp(clusterID, ZB_TEMPERATURE_CLUSTER) == 0)
483 return OIC_TEMPERATURE_SENSOR;
485 else if (strcmp(clusterID, ZB_LEVEL_CONTROL_CLUSTER) == 0)
487 return OIC_DIMMABLE_LIGHT;
489 else if (strcmp(clusterID, ZB_IAS_ZONE_CLUSTER) == 0)
491 return OIC_CONTACT_SENSOR;
493 else if (strcmp(clusterID, ZB_ON_OFF_CLUSTER) == 0)
495 return OIC_BINARY_SWITCH;
497 else if (strcmp(clusterID, ZB_COLOR_CONTROL_CLUSTER) == 0)
499 return OIC_CHROMA_LIGHT;
507 const char* OICResourceToZigBeeClusterID(char *oicResourceType)
509 if (!oicResourceType)
513 if (strcmp(oicResourceType, OIC_TEMPERATURE_SENSOR) == 0)
515 return ZB_TEMPERATURE_CLUSTER;
517 else if (strcmp(oicResourceType, OIC_DIMMABLE_LIGHT) == 0)
519 return ZB_LEVEL_CONTROL_CLUSTER;
521 else if (strcmp(oicResourceType, OIC_CONTACT_SENSOR) == 0)
523 return ZB_IAS_ZONE_CLUSTER;
525 else if (strcmp(oicResourceType, OIC_BINARY_SWITCH) == 0)
527 return ZB_ON_OFF_CLUSTER;
529 else if (strcmp(oicResourceType, OIC_BINARY_SWITCH) == 0)
531 return ZB_INDICATOR_CLUSTER;
533 else if (strcmp(oicResourceType, OIC_CHROMA_LIGHT) == 0)
535 return ZB_COLOR_CONTROL_CLUSTER;
543 OCStackResult getZigBeeAttributesForOICResource(const char * OICResourceType,
544 AttributeList *attributeList) // GET
546 if (!OICResourceType || !attributeList)
548 return OC_STACK_INVALID_PARAM;
550 if (strcmp(OICResourceType, OIC_TEMPERATURE_SENSOR) == 0)
552 attributeList->count = 1;
553 attributeList->list[0].oicAttribute = OICStrdup(OIC_TEMPERATURE_ATTRIBUTE);
554 attributeList->list[0].zigBeeAttribute = ZB_TEMPERATURE_ATTRIBUTE_ID;
555 attributeList->list[0].oicType = OIC_ATTR_DOUBLE;
556 attributeList->list[0].zigbeeType = ZB_16_SINT;
559 else if (strcmp(OICResourceType, OIC_DIMMABLE_LIGHT) == 0)
561 attributeList->count = 1;
562 attributeList->list[0].oicAttribute = OICStrdup(OIC_DIMMING_ATTRIBUTE);
563 attributeList->list[0].zigBeeAttribute = ZB_CURRENT_LEVEL_ATTRIBUTE_READONLY;
564 attributeList->list[0].oicType = OIC_ATTR_INT;
565 attributeList->list[0].zigbeeType = ZB_8_UINT;
568 else if (strcmp(OICResourceType, OIC_CONTACT_SENSOR) == 0)
570 attributeList->count = 1;
571 attributeList->list[0].oicAttribute = OICStrdup(OIC_CONTACT_ATTRIBUTE);
572 attributeList->list[0].zigBeeAttribute = ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID;
573 attributeList->list[0].oicType = OIC_ATTR_BOOL;
574 attributeList->list[0].zigbeeType = ZB_BOOL;
577 else if (strcmp(OICResourceType, OIC_WATER_SENSOR) == 0)
579 attributeList->count = 1;
580 attributeList->list[0].oicAttribute = OICStrdup(OIC_WATER_ATTRIBUTE);
581 attributeList->list[0].zigBeeAttribute = ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID;
582 attributeList->list[0].oicType = OIC_ATTR_BOOL;
583 attributeList->list[0].zigbeeType = ZB_BOOL;
586 else if (strcmp(OICResourceType, OIC_MOTION_SENSOR) == 0)
588 attributeList->count = 1;
589 attributeList->list[0].oicAttribute = OICStrdup(OIC_MOTION_ATTRIBUTE);
590 attributeList->list[0].zigBeeAttribute = ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID;
591 attributeList->list[0].oicType = OIC_ATTR_BOOL;
592 attributeList->list[0].zigbeeType = ZB_BOOL;
595 else if (strcmp(OICResourceType, OIC_BINARY_SWITCH) == 0)
597 attributeList->count = 1;
598 attributeList->list[0].oicAttribute = OICStrdup(OIC_ON_OFF_ATTRIBUTE);
599 attributeList->list[0].zigBeeAttribute = ZB_ON_OFF_ATTRIBUTE_ID;
600 attributeList->list[0].oicType = OIC_ATTR_BOOL;
601 attributeList->list[0].zigbeeType = ZB_BOOL;
604 else if (strcmp(OICResourceType, OIC_CHROMA_LIGHT) == 0)
606 attributeList->count = 1;
607 attributeList->list[0].oicAttribute = OICStrdup(OIC_COLOUR_TEMPERATURE_ATTRIBUTE);
608 attributeList->list[0].zigBeeAttribute = ZB_COLOR_TEMPERATURE_ATTRIBUTE_ID;
609 attributeList->list[0].oicType = OIC_ATTR_INT;
610 attributeList->list[0].zigbeeType = ZB_16_UINT;
614 return OC_STACK_ERROR;
617 bool getZigBeeAttributesIfValid(const char * OICResourceType,
618 AttributeList *attributeList,
619 OCRepPayload *payload) // Put
621 if (!OICResourceType)
625 if (strcmp(OICResourceType, OIC_TEMPERATURE_SENSOR) == 0)
627 // Cant really PUT on the temp sensor, but the code is still there.
628 int64_t temperature = 0;
630 // TODO: This if should only look for attributes it supports and ignore the rest
631 // or examine every attribute in the payload and complain about unsupported attributes?
632 if (OCRepPayloadGetPropInt(payload, OIC_TEMPERATURE_ATTRIBUTE, &temperature))
634 attributeList->count = 1;
635 attributeList->list[0].oicAttribute = OICStrdup(OIC_TEMPERATURE_ATTRIBUTE);
636 attributeList->list[0].zigBeeAttribute = ZB_TEMPERATURE_ATTRIBUTE_ID;
637 attributeList->list[0].oicType = OIC_ATTR_DOUBLE;
638 attributeList->list[0].val.d = temperature;
639 attributeList->list[0].zigbeeType = ZB_16_SINT;
640 attributeList->CIEMask = (CIECommandMask) 0;
645 else if (strcmp(OICResourceType, OIC_DIMMABLE_LIGHT) == 0)
649 if (OCRepPayloadGetPropInt(payload, OIC_DIMMING_ATTRIBUTE, &onLevel))
651 attributeList->count = 1;
652 attributeList->list[0].oicAttribute = OICStrdup(OIC_DIMMING_ATTRIBUTE);
653 attributeList->list[0].zigBeeAttribute = ZB_ON_LEVEL_ATTRIBUTE;
654 attributeList->list[0].oicType = OIC_ATTR_INT;
655 attributeList->list[0].val.i = onLevel;
656 attributeList->list[0].zigbeeType = ZB_8_UINT;
658 // Level control cluster is dealing with level in the PUT payload.
659 attributeList->CIEMask = attributeList->CIEMask | CIE_MOVE_TO_LEVEL;
663 else if (strcmp(OICResourceType, OIC_CONTACT_SENSOR) == 0)
667 if (OCRepPayloadGetPropInt(payload, OIC_CONTACT_ATTRIBUTE, &value))
669 attributeList->count = 1;
670 attributeList->list[0].oicAttribute = OICStrdup(OIC_CONTACT_ATTRIBUTE);
671 attributeList->list[0].zigBeeAttribute = ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID;
672 attributeList->list[0].oicType = OIC_ATTR_BOOL;
673 attributeList->list[0].val.i = value;
674 attributeList->list[0].zigbeeType = ZB_BOOL;
675 attributeList->CIEMask = (CIECommandMask) 0;
680 else if (strcmp(OICResourceType, OIC_WATER_SENSOR) == 0)
684 if (OCRepPayloadGetPropInt(payload, OIC_WATER_ATTRIBUTE, &value))
686 attributeList->count = 1;
687 attributeList->list[0].oicAttribute = OICStrdup(OIC_WATER_ATTRIBUTE);
688 attributeList->list[0].zigBeeAttribute = ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID;
689 attributeList->list[0].oicType = OIC_ATTR_BOOL;
690 attributeList->list[0].val.i = value;
691 attributeList->list[0].zigbeeType = ZB_BOOL;
692 attributeList->CIEMask = (CIECommandMask) 0;
697 else if (strcmp(OICResourceType, OIC_BINARY_SWITCH) == 0)
701 if (OCRepPayloadGetPropBool(payload, OIC_ON_OFF_ATTRIBUTE, &value))
703 attributeList->count = 1;
704 attributeList->list[0].oicAttribute = OICStrdup(OIC_ON_OFF_ATTRIBUTE);
705 attributeList->list[0].zigBeeAttribute = ZB_ON_OFF_ATTRIBUTE_ID;
706 attributeList->list[0].oicType = OIC_ATTR_BOOL;
707 attributeList->list[0].val.b = value;
708 attributeList->list[0].zigbeeType = ZB_BOOL;
710 attributeList->CIEMask = attributeList->CIEMask | CIE_RON_OFF;
714 else if (strcmp(OICResourceType, OIC_CHROMA_LIGHT) == 0)
717 if (OCRepPayloadGetPropString(payload, OIC_COLOUR_TEMPERATURE_ATTRIBUTE, &value))
719 attributeList->count = 1;
720 attributeList->list[0].oicAttribute = OICStrdup(OIC_COLOUR_TEMPERATURE_ATTRIBUTE);
721 attributeList->list[0].zigBeeAttribute = ZB_COLOR_TEMPERATURE_ATTRIBUTE_ID;
722 attributeList->list[0].oicType = OIC_ATTR_STRING;
723 attributeList->list[0].val.str = value;
724 attributeList->list[0].zigbeeType = ZB_16_UINT;
725 attributeList->CIEMask = (CIECommandMask) 0;
733 OCEntityHandlerResult getDoubleValueFromString(const char *str, double *outDouble)
735 if (!str || !outDouble)
739 size_t hexOutValSize = strlen(HexPrepend) + strlen(str) + 1;
740 char * hexOutVal = (char *) OICCalloc(1, hexOutValSize);
745 OICStrcpy(hexOutVal, hexOutValSize, HexPrepend);
746 OICStrcat(hexOutVal, hexOutValSize, str);
750 double value = strtod(hexOutVal, &endPtr);
752 if (errno != 0 || *endPtr != 0 || value == HUGE_VALF || value == HUGE_VALL)
764 OCEntityHandlerResult getColourTemperatureFromString(const char* str, int64_t* outVal)
770 // str comes in as "X,Y,T" where "T" is the colour temperature.
771 // Iterate 3 times to retrieve the last value.
772 char * strstr = OICStrdup(str);
773 char * savePtr = NULL;
775 for (int i=0; i<3; i++)
777 temp = strtok_r(strstr, ",", &savePtr);
785 OCStackResult result = getDoubleValueFromString(temp, (double *)outVal);
790 OCEntityHandlerResult processGetRequest(PIPluginBase * plugin,
791 OCEntityHandlerRequest *ehRequest, OCRepPayload **payload)
793 if (!plugin || !ehRequest || !payload)
797 uint32_t attributeListIndex = 0;
798 OCStackResult stackResult = OC_STACK_OK;
799 PIResource_Zigbee * piResource = NULL;
801 AttributeList attributeList = { 0, (CIECommandMask) 0,
802 .list[0] = { NULL, NULL, OIC_ATTR_NULL, ZB_NULL, { .i = 0 } } };
803 stackResult = GetResourceFromHandle(plugin, (PIResource**) (&piResource),
804 ehRequest->resource);
805 if (stackResult != OC_STACK_OK)
807 OC_LOG(ERROR, TAG, "Failed to get resource from handle");
810 stackResult = getZigBeeAttributesForOICResource(
811 piResource->header.piResource.resourceTypeName, &attributeList);
812 if (stackResult != OC_STACK_OK)
814 OC_LOG_V(ERROR, TAG, "Failed to fetch attributes for %s",
815 piResource->header.piResource.resourceTypeName);
819 *payload = OCRepPayloadCreate();
822 OC_LOG(ERROR, TAG, PCF("Failed to allocate Payload"));
825 bool boolRes = OCRepPayloadSetUri(*payload, piResource->header.piResource.uri);
826 if (boolRes == false)
828 OCRepPayloadDestroy(*payload);
831 for (uint32_t i = 0; i<attributeList.count; i++)
833 char * outVal = NULL;
834 uint8_t outValLength = 0;
836 stackResult = TWGetAttribute(piResource->eui,
838 piResource->endpointId,
839 piResource->clusterId,
840 attributeList.list[i].zigBeeAttribute,
843 (PIPlugin_Zigbee *)plugin);
845 if (stackResult != OC_STACK_OK || !outVal)
847 stackResult = OC_EH_ERROR;
848 OCRepPayloadDestroy(*payload);
851 if (attributeList.list[i].oicType == OIC_ATTR_INT)
855 // Third arg is 16 as outVal is a hex Number
856 uint64_t value = strtol(outVal, &endPtr, 16);
858 if (*endPtr != 0 || errno != 0)
862 if (!attributeList.list[i].oicAttribute)
866 if (strcmp(attributeList.list[i].oicAttribute, OIC_DIMMING_ATTRIBUTE) == 0)
868 // OIC Dimming operates between 0-100, while Zigbee operates
869 // between 0-254 (ie. 0xFE).
876 value = value / 2.54;
879 else if (strcmp(attributeList.list[i].oicAttribute, OIC_COLOUR_TEMPERATURE_ATTRIBUTE) == 0)
881 // OIC Chroma requires: Hue, Saturation, and ColorSpaceValue (color space value is
882 // "chromaX, chromaY, colourTemperature").
883 // ZigBee HA requires: "currentX", "currentY", while we're trying to map a specific
884 // device which goes against the HA spec and ONLY implements "ColorTemperature".
885 // Because of the misalignments between OIC Smart Home and ZigBee HA specs, the
886 // follow assumption is required:
887 // - *X and *Y will be zero, and therefore ignored (or passed as zero where
889 // OIC ColorTemperature operates between 0-1000 (degrees Kelvin) in string form,
890 // while ZigBee operates between 0-65279 (bits, with an operating range
891 // 15.32-1,000,000 (degrees Kelvin)).
892 // ZigBee HA states: ColorTemperature(bits) = 1,000,000/ColorTemperature(Kelvin)
893 // Invalid: ColorTemperature==0 || ColorTemperature==65535
894 // However the specific bulb we're mapping only operates between 2700-6500 (Kelvin)
895 // which equates to 0x0099-0x0172 (ie. 153-370; giving a resolution of 217 bits).
896 // Conversion from ZigBee HA specific bulb to OIC temporary mapping:
897 // OIC Value: 0-100 ZigBee Value: (0-217)+153
898 // OICValue = (ZigBeeValue-153)/2.17
900 value = (value-153)/2.17;
902 boolRes = OCRepPayloadSetPropInt(*payload,
903 attributeList.list[i].oicAttribute,
906 else if (attributeList.list[i].oicType == OIC_ATTR_DOUBLE)
910 if (getDoubleValueFromString(outVal, &value) != OC_EH_OK)
914 if (!piResource->clusterId)
918 if (strcmp(piResource->clusterId, ZB_TEMPERATURE_CLUSTER) == 0)
920 // Divide by 100 as temperature readings have a resolution of
921 // 0.01 or one hundreth of a degree celsius.
924 boolRes = OCRepPayloadSetPropDouble(*payload,
925 attributeList.list[i].oicAttribute,
928 else if (attributeList.list[i].oicType == OIC_ATTR_STRING)
930 boolRes = OCRepPayloadSetPropString(*payload,
931 attributeList.list[i].oicAttribute,
934 else if (attributeList.list[i].oicType == OIC_ATTR_BOOL)
938 // Third arg is 16 as outVal is a hex Number
939 uint64_t value = strtol(outVal, &endPtr, 16);
941 if (errno != 0 || *endPtr != 0)
945 // value COULD be a bit mask and the LSB indicates boolean true/false.
946 // If not a bit mask, it'll be plain 0 or 1.
948 boolRes = OCRepPayloadSetPropBool(*payload,
949 attributeList.list[i].oicAttribute,
956 if (boolRes == false)
958 stackResult = OC_EH_ERROR;
963 for (; attributeListIndex < attributeList.count; attributeListIndex++)
965 OICFree(attributeList.list[attributeListIndex].oicAttribute);
970 OCEntityHandlerResult processPutRequest(PIPluginBase * plugin,
971 OCEntityHandlerRequest *ehRequest, OCRepPayload **payload)
973 if (!plugin || !ehRequest || !payload)
977 OCStackResult stackResult = OC_STACK_OK;
978 PIResource_Zigbee *piResource = NULL;
979 AttributeList attributeList = {
982 .list[0] = { NULL, NULL, OIC_ATTR_NULL, ZB_NULL, { .i = 0 } }
985 stackResult = GetResourceFromHandle(plugin,
986 ((PIResource **) (&piResource)),
987 ehRequest->resource);
988 if (stackResult != OC_STACK_OK)
990 OC_LOG(ERROR, TAG, "Failed to get resource from handle");
994 bool boolRes = getZigBeeAttributesIfValid(
995 piResource->header.piResource.resourceTypeName,
996 &attributeList, *payload);
997 if (boolRes == false)
999 OC_LOG_V(ERROR, TAG, "Failed to fetch attributes for %s",
1000 piResource->header.piResource.resourceTypeName);
1005 for (; i<attributeList.count; i++)
1007 if (attributeList.list[i].oicType == OIC_ATTR_INT)
1009 char value[MAX_STRLEN_INT] = {};
1010 if (attributeList.CIEMask || CIE_MOVE_TO_LEVEL)
1012 int64_t rangeDiff = 0;
1013 // OIC Dimming operates between 0-100, while Zigbee
1014 // operates between 0-254 (ie. 0xFE).
1015 rangeDiff = attributeList.list[i].val.i * 0xFE/100;
1016 if (rangeDiff > 0xFE)
1024 if (rangeDiff <= 0xFE)
1027 int strRet = snprintf(value, sizeof(value), "%02x", (unsigned int) rangeDiff);
1030 OC_LOG_V(ERROR, TAG, "Failed to parse string due to errno: %d", errno);
1034 stackResult = TWMoveToLevel(piResource->nodeId, piResource->endpointId,
1035 DEFAULT_MOVETOLEVEL_MODE, value, DEFAULT_TRANS_TIME,
1036 (PIPlugin_Zigbee*)plugin);
1041 int strRet = snprintf(value,
1044 attributeList.list[i].val.i);
1047 OC_LOG_V(ERROR, TAG, "Failed to parse string due to errno: %d", errno);
1050 stackResult = TWSetAttribute(piResource->eui,
1051 piResource->nodeId, piResource->endpointId,
1052 piResource->clusterId, attributeList.list[i].zigBeeAttribute,
1053 getZBDataTypeString(attributeList.list[i].zigbeeType), value,
1054 (PIPlugin_Zigbee*)plugin);
1056 if (stackResult != OC_STACK_OK)
1061 else if (attributeList.list[i].oicType == OIC_ATTR_DOUBLE)
1063 char value[MAX_STRLEN_DOUBLE] = {};
1065 int strRet = snprintf(value, sizeof(value), "%f", attributeList.list[i].val.d);
1068 OC_LOG_V(ERROR, TAG, "Failed to parse string due to errno: %d", errno);
1071 stackResult = TWSetAttribute(piResource->eui,
1072 piResource->nodeId, piResource->endpointId,
1073 piResource->clusterId, attributeList.list[i].zigBeeAttribute,
1074 getZBDataTypeString(attributeList.list[i].zigbeeType), value,
1075 (PIPlugin_Zigbee*)plugin);
1077 else if (attributeList.list[i].oicType == OIC_ATTR_STRING)
1079 if (strcmp(attributeList.list[i].oicAttribute, OIC_COLOUR_TEMPERATURE_ATTRIBUTE) == 0)
1081 char *endPtr = NULL;
1082 uint16_t zbVal = (uint16_t)strtod(attributeList.list[i].val.str, &endPtr);
1083 zbVal = (zbVal*2.17)+153;
1084 char value[5] = {0};
1086 int strRet = snprintf(value, sizeof(value), "%04x", zbVal);
1089 OC_LOG_V(ERROR, TAG, "Failed to parse string due to errno: %d", errno);
1093 TWColorMoveToColorTemperature(piResource->nodeId, piResource->endpointId,
1094 value, DEFAULT_TRANS_TIME,
1095 (PIPlugin_Zigbee*)plugin);
1099 stackResult = TWSetAttribute(piResource->eui,
1100 piResource->nodeId, piResource->endpointId,
1101 piResource->clusterId, attributeList.list[i].zigBeeAttribute,
1102 getZBDataTypeString(attributeList.list[i].zigbeeType),
1103 attributeList.list[i].val.str,
1104 (PIPlugin_Zigbee*)plugin);
1106 if (stackResult != OC_STACK_OK)
1111 else if (attributeList.list[i].oicType == OIC_ATTR_BOOL)
1113 char * value = attributeList.list[i].val.b ? "1" : "0";
1114 if (attributeList.CIEMask || CIE_RON_OFF)
1116 stackResult = TWSwitchOnOff(piResource->nodeId, piResource->endpointId, value,
1117 (PIPlugin_Zigbee*)plugin);
1121 stackResult = TWSetAttribute(piResource->eui,
1122 piResource->nodeId, piResource->endpointId,
1123 piResource->clusterId, attributeList.list[i].zigBeeAttribute,
1124 getZBDataTypeString(attributeList.list[i].zigbeeType),
1126 (PIPlugin_Zigbee*)plugin);
1128 if (stackResult != OC_STACK_OK)
1139 return processGetRequest(plugin, ehRequest, payload);
1142 OCEntityHandlerResult ProcessEHRequest(PIPluginBase * plugin,
1143 OCEntityHandlerRequest *ehRequest, OCRepPayload **payload)
1145 if (!ehRequest || !payload)
1149 if (ehRequest->method == OC_REST_GET)
1151 return processGetRequest(plugin, ehRequest, payload);
1153 else if (ehRequest->method == OC_REST_PUT)
1155 return processPutRequest(plugin, ehRequest, payload);
1159 return OC_EH_FORBIDDEN;
1163 char * getZBDataTypeString(ZigBeeAttributeDataType attrType)
1168 return ZB_DATA_TYPE_NULL;
1170 return ZB_DATA_TYPE_1_BYTE;
1172 return ZB_DATA_TYPE_2_BYTE;
1174 return ZB_DATA_TYPE_3_BYTE;
1176 return ZB_DATA_TYPE_4_BYTE;
1178 return ZB_DATA_TYPE_5_BYTE;
1180 return ZB_DATA_TYPE_6_BYTE;
1182 return ZB_DATA_TYPE_7_BYTE;
1184 return ZB_DATA_TYPE_8_BYTE;
1186 return ZB_DATA_TYPE_BOOL;
1188 return ZB_DATA_TYPE_1_BYTE;
1190 return ZB_DATA_TYPE_2_BYTE;
1192 return ZB_DATA_TYPE_3_BYTE;
1194 return ZB_DATA_TYPE_4_BYTE;
1196 return ZB_DATA_TYPE_5_BYTE;
1198 return ZB_DATA_TYPE_6_BYTE;
1200 return ZB_DATA_TYPE_7_BYTE;
1202 return ZB_DATA_TYPE_8_BYTE;
1204 return ZB_DATA_TYPE_SIGNED_INT_16;
1206 return ZB_DATA_TYPE_UNSIGNED_INT_8;
1208 return ZB_DATA_TYPE_UNSIGNED_INT_16;
1210 return ZB_DATA_TYPE_NULL;