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";
123 PIPlugin_Zigbee ** gPlugin = NULL;
129 ZB_16_BIT, // 2 bytes
130 ZB_24_BIT, // 3 bytes
131 ZB_32_BIT, // 4 bytes
132 ZB_40_BIT, // 5 bytes
133 ZB_48_BIT, // 6 bytes
134 ZB_56_BIT, // 7 bytes
135 ZB_64_BIT, // 8 bytes
148 } ZigBeeAttributeDataType;
150 char * getZBDataTypeString(ZigBeeAttributeDataType attrType);
151 OCEntityHandlerResult ProcessEHRequest(PIPluginBase * plugin, OCEntityHandlerRequest *ehRequest,
152 OCRepPayload **payload);
166 char *zigBeeAttribute;
167 OICAttributeType oicType;
168 ZigBeeAttributeDataType zigbeeType;
177 } OICZigBeeAttributePair;
181 CIE_RON_OFF = 1 << 1,
182 CIE_MOVE_TO_LEVEL = 1 << 2
189 CIECommandMask CIEMask;
190 OICZigBeeAttributePair list[MAX_ATTRIBUTES];
193 const char* ZigBeeClusterIDToOICResourceType(const char * clusterID);
195 OCStackResult getZigBeeAttributesForOICResource(const char * OICResourceType,
196 AttributeList *attributeList);
198 bool getZigBeeAttributesIfValid(const char * OICResourceType,
199 AttributeList *attributeList,
200 OCRepPayload *payload);
202 const char * getResourceTypeForIASZoneType(TWDevice *device)
208 char *IASZoneType = NULL;
209 const char *resourceType = NULL;
212 OCStackResult ret = TWGetAttribute(
215 device->endpointOfInterest->endpointId,
217 ZB_IAS_ZONE_TYPE_ATTRIBUTE_ID,
223 if (ret != OC_STACK_OK || !IASZoneType)
225 OC_LOG_V(ERROR, TAG, "Error %u getting IAS Zone Type", ret);
229 if (strcmp(IASZoneType, IAS_ZONE_TYPE_CONTACT_SENSOR) == 0)
231 resourceType = OIC_CONTACT_SENSOR;
233 else if (strcmp(IASZoneType, IAS_ZONE_TYPE_MOTION_SENSOR) == 0)
235 resourceType = OIC_MOTION_SENSOR;
237 else if (strcmp(IASZoneType, IAS_ZONE_TYPE_WATER_SENSOR) == 0)
239 resourceType = OIC_WATER_SENSOR;
243 OC_LOG_V(ERROR, TAG, "Unsupported Zone Type %s", IASZoneType);
247 OICFree(IASZoneType);
252 OCStackResult buildURI(char ** output,
255 const char * endpointId,
256 const char * clusterId)
258 if (!output || !prefix || !nodeId || !endpointId || !clusterId)
260 return OC_STACK_INVALID_PARAM;
262 const char LEN_SEPARATOR[] = "/";
263 size_t lenSeparatorSize = sizeof(LEN_SEPARATOR) - 1;
264 size_t newUriSize = strlen(prefix) + lenSeparatorSize +
265 strlen(nodeId) + lenSeparatorSize +
266 strlen(endpointId) + lenSeparatorSize +
268 + 1; // NULL Terminator
269 *output = (char *) OICCalloc(1, newUriSize);
273 OC_LOG(ERROR, TAG, "Out of memory");
274 return OC_STACK_NO_MEMORY;
277 char * temp = OICStrcpy(*output, newUriSize, prefix);
282 temp = OICStrcat(*output, newUriSize, LEN_SEPARATOR);
287 temp = OICStrcat(*output, newUriSize, nodeId);
292 temp = OICStrcat(*output, newUriSize, LEN_SEPARATOR);
297 temp = OICStrcat(*output, newUriSize, endpointId);
302 temp = OICStrcat(*output, newUriSize, LEN_SEPARATOR);
307 temp = OICStrcat(*output, newUriSize, clusterId);
318 return OC_STACK_NO_MEMORY;
321 void foundZigbeeCallback(TWDevice *device)
325 OC_LOG(ERROR, TAG, "foundZigbeeCallback: Invalid parameter.");
328 int count = device->endpointOfInterest->clusterList->count;
329 for (int i=0; i < count; i++)
331 PIResource_Zigbee *piResource = (PIResource_Zigbee *) OICMalloc(sizeof(*piResource));
334 OC_LOG(ERROR, TAG, "Out of memory");
337 piResource->header.plugin = (PIPluginBase *)gPlugin;
339 OCStackResult result = buildURI(&piResource->header.piResource.uri,
342 device->endpointOfInterest->endpointId,
343 device->endpointOfInterest->clusterList->clusterIds[i].clusterId);
345 if (result != OC_STACK_OK)
351 char * foundClusterID =
352 device->endpointOfInterest->clusterList->clusterIds[i].clusterId;
354 if (strcmp(foundClusterID, ZB_IAS_ZONE_CLUSTER) == 0)
356 piResource->header.piResource.resourceTypeName
357 = getResourceTypeForIASZoneType(device);
359 OCStackResult ret = TWListenForStatusUpdates(device->nodeId,
360 device->endpointOfInterest->endpointId,
363 if (ret != OC_STACK_OK)
365 // Just log it and move on if this fails?
366 // or not create this resource at all?
367 OC_LOG(ERROR, TAG, "Command to listen for status updates failed");
372 piResource->header.piResource.resourceTypeName =
373 (char *) ZigBeeClusterIDToOICResourceType(foundClusterID);
376 if (piResource->header.piResource.resourceTypeName == NULL)
378 OC_LOG_V(ERROR, TAG, "unsupported clusterId : %s",
379 device->endpointOfInterest->clusterList->clusterIds[i].clusterId);
380 OICFree(piResource->header.piResource.uri);
384 piResource->header.piResource.resourceInterfaceName =
385 OC_RSRVD_INTERFACE_DEFAULT;
387 piResource->header.piResource.callbackParam = NULL;
388 piResource->header.piResource.resourceProperties = 0;
389 piResource->eui = OICStrdup(device->eui);
390 piResource->nodeId = OICStrdup(device->nodeId);
391 piResource->endpointId = OICStrdup(device->endpointOfInterest->endpointId);
392 piResource->clusterId =
393 OICStrdup(device->endpointOfInterest->clusterList->clusterIds[i].clusterId);
394 (*gPlugin)->header.NewResourceFoundCB(&(*gPlugin)->header, &piResource->header);
398 void zigbeeZoneStatusUpdate(TWUpdate * update)
406 OCStackResult result = buildURI(&uri,
410 ZB_IAS_ZONE_CLUSTER);
411 if (result != OC_STACK_OK || !uri)
413 OC_LOG_V(ERROR, TAG, "Failed to build URI with result: %d", result);
417 (*gPlugin)->header.ObserveNotificationUpdate((PIPluginBase *)*gPlugin, uri);
421 OCStackResult ZigbeeInit(const char * comPort, PIPlugin_Zigbee ** plugin,
422 PINewResourceFound newResourceCB,
423 PIObserveNotificationUpdate observeNotificationUpdate)
427 return OC_STACK_INVALID_PARAM;
429 *plugin = (PIPlugin_Zigbee *) OICMalloc(sizeof(PIPlugin_Zigbee) + sizeof(PIPluginBase));
432 return OC_STACK_NO_MEMORY;
434 ((*plugin)->header).type = PLUGIN_ZIGBEE;
435 ((*plugin)->header).comPort = comPort;
436 ((*plugin)->header).NewResourceFoundCB = newResourceCB;
437 ((*plugin)->header).ObserveNotificationUpdate = observeNotificationUpdate;
438 ((*plugin)->header).next = NULL;
439 ((*plugin)->header).resourceList = NULL;
440 ((*plugin)->header).processEHRequest = ProcessEHRequest;
443 OCStackResult result = TWInitialize(*gPlugin, comPort);
444 if (result != OC_STACK_OK)
449 return TWSetStatusUpdateCallback(zigbeeZoneStatusUpdate, *gPlugin);
452 OCStackResult ZigbeeDiscover(PIPlugin_Zigbee * plugin)
454 OCStackResult result = OC_STACK_ERROR;
455 TWSetDiscoveryCallback(foundZigbeeCallback, plugin);
456 result = TWDiscover(plugin);
457 OC_LOG_V(DEBUG, TAG, "ZigbeeDiscover : Status = %d\n", result);
462 OCStackResult ZigbeeStop(PIPlugin_Zigbee * plugin)
464 OCStackResult ret = TWUninitialize(plugin);
469 OCStackResult ZigbeeProcess(PIPlugin_Zigbee * plugin)
471 return TWProcess(plugin);
474 // Function returns an OIC Smart Home resource Type
475 // from the cluster ID. If the cluster is not supported, null is
477 // NOTE: The returned string is NOT malloc'ed.
478 const char* ZigBeeClusterIDToOICResourceType(const char * clusterID) //Discovery/CreateResource
484 if (strcmp(clusterID, ZB_TEMPERATURE_CLUSTER) == 0)
486 return OIC_TEMPERATURE_SENSOR;
488 else if (strcmp(clusterID, ZB_LEVEL_CONTROL_CLUSTER) == 0)
490 return OIC_DIMMABLE_LIGHT;
492 else if (strcmp(clusterID, ZB_IAS_ZONE_CLUSTER) == 0)
494 return OIC_CONTACT_SENSOR;
496 else if (strcmp(clusterID, ZB_ON_OFF_CLUSTER) == 0)
498 return OIC_BINARY_SWITCH;
500 else if (strcmp(clusterID, ZB_COLOR_CONTROL_CLUSTER) == 0)
502 return OIC_CHROMA_LIGHT;
510 const char* OICResourceToZigBeeClusterID(char *oicResourceType)
512 if (!oicResourceType)
516 if (strcmp(oicResourceType, OIC_TEMPERATURE_SENSOR) == 0)
518 return ZB_TEMPERATURE_CLUSTER;
520 else if (strcmp(oicResourceType, OIC_DIMMABLE_LIGHT) == 0)
522 return ZB_LEVEL_CONTROL_CLUSTER;
524 else if (strcmp(oicResourceType, OIC_CONTACT_SENSOR) == 0)
526 return ZB_IAS_ZONE_CLUSTER;
528 else if (strcmp(oicResourceType, OIC_BINARY_SWITCH) == 0)
530 return ZB_ON_OFF_CLUSTER;
532 else if (strcmp(oicResourceType, OIC_BINARY_SWITCH) == 0)
534 return ZB_INDICATOR_CLUSTER;
536 else if (strcmp(oicResourceType, OIC_CHROMA_LIGHT) == 0)
538 return ZB_COLOR_CONTROL_CLUSTER;
546 OCStackResult getZigBeeAttributesForOICResource(const char * OICResourceType,
547 AttributeList *attributeList) // GET
549 if (!OICResourceType || !attributeList)
551 return OC_STACK_INVALID_PARAM;
553 if (strcmp(OICResourceType, OIC_TEMPERATURE_SENSOR) == 0)
555 attributeList->count = 1;
556 attributeList->list[0].oicAttribute = OICStrdup(OIC_TEMPERATURE_ATTRIBUTE);
557 attributeList->list[0].zigBeeAttribute = ZB_TEMPERATURE_ATTRIBUTE_ID;
558 attributeList->list[0].oicType = OIC_ATTR_DOUBLE;
559 attributeList->list[0].zigbeeType = ZB_16_SINT;
562 else if (strcmp(OICResourceType, OIC_DIMMABLE_LIGHT) == 0)
564 attributeList->count = 1;
565 attributeList->list[0].oicAttribute = OICStrdup(OIC_DIMMING_ATTRIBUTE);
566 attributeList->list[0].zigBeeAttribute = ZB_CURRENT_LEVEL_ATTRIBUTE_READONLY;
567 attributeList->list[0].oicType = OIC_ATTR_INT;
568 attributeList->list[0].zigbeeType = ZB_8_UINT;
571 else if (strcmp(OICResourceType, OIC_CONTACT_SENSOR) == 0)
573 attributeList->count = 1;
574 attributeList->list[0].oicAttribute = OICStrdup(OIC_CONTACT_ATTRIBUTE);
575 attributeList->list[0].zigBeeAttribute = ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID;
576 attributeList->list[0].oicType = OIC_ATTR_BOOL;
577 attributeList->list[0].zigbeeType = ZB_BOOL;
580 else if (strcmp(OICResourceType, OIC_WATER_SENSOR) == 0)
582 attributeList->count = 1;
583 attributeList->list[0].oicAttribute = OICStrdup(OIC_WATER_ATTRIBUTE);
584 attributeList->list[0].zigBeeAttribute = ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID;
585 attributeList->list[0].oicType = OIC_ATTR_BOOL;
586 attributeList->list[0].zigbeeType = ZB_BOOL;
589 else if (strcmp(OICResourceType, OIC_MOTION_SENSOR) == 0)
591 attributeList->count = 1;
592 attributeList->list[0].oicAttribute = OICStrdup(OIC_MOTION_ATTRIBUTE);
593 attributeList->list[0].zigBeeAttribute = ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID;
594 attributeList->list[0].oicType = OIC_ATTR_BOOL;
595 attributeList->list[0].zigbeeType = ZB_BOOL;
598 else if (strcmp(OICResourceType, OIC_BINARY_SWITCH) == 0)
600 attributeList->count = 1;
601 attributeList->list[0].oicAttribute = OICStrdup(OIC_ON_OFF_ATTRIBUTE);
602 attributeList->list[0].zigBeeAttribute = ZB_ON_OFF_ATTRIBUTE_ID;
603 attributeList->list[0].oicType = OIC_ATTR_BOOL;
604 attributeList->list[0].zigbeeType = ZB_BOOL;
607 else if (strcmp(OICResourceType, OIC_CHROMA_LIGHT) == 0)
609 attributeList->count = 1;
610 attributeList->list[0].oicAttribute = OICStrdup(OIC_COLOUR_TEMPERATURE_ATTRIBUTE);
611 attributeList->list[0].zigBeeAttribute = ZB_COLOR_TEMPERATURE_ATTRIBUTE_ID;
612 attributeList->list[0].oicType = OIC_ATTR_INT;
613 attributeList->list[0].zigbeeType = ZB_16_UINT;
617 return OC_STACK_ERROR;
620 bool getZigBeeAttributesIfValid(const char * OICResourceType,
621 AttributeList *attributeList,
622 OCRepPayload *payload) // Put
624 if (!OICResourceType)
628 if (strcmp(OICResourceType, OIC_TEMPERATURE_SENSOR) == 0)
630 // Cant really PUT on the temp sensor, but the code is still there.
631 int64_t temperature = 0;
633 // TODO: This if should only look for attributes it supports and ignore the rest
634 // or examine every attribute in the payload and complain about unsupported attributes?
635 if (OCRepPayloadGetPropInt(payload, OIC_TEMPERATURE_ATTRIBUTE, &temperature))
637 attributeList->count = 1;
638 attributeList->list[0].oicAttribute = OICStrdup(OIC_TEMPERATURE_ATTRIBUTE);
639 attributeList->list[0].zigBeeAttribute = ZB_TEMPERATURE_ATTRIBUTE_ID;
640 attributeList->list[0].oicType = OIC_ATTR_DOUBLE;
641 attributeList->list[0].val.d = temperature;
642 attributeList->list[0].zigbeeType = ZB_16_SINT;
643 attributeList->CIEMask = (CIECommandMask) 0;
648 else if (strcmp(OICResourceType, OIC_DIMMABLE_LIGHT) == 0)
652 if (OCRepPayloadGetPropInt(payload, OIC_DIMMING_ATTRIBUTE, &onLevel))
654 attributeList->count = 1;
655 attributeList->list[0].oicAttribute = OICStrdup(OIC_DIMMING_ATTRIBUTE);
656 attributeList->list[0].zigBeeAttribute = ZB_ON_LEVEL_ATTRIBUTE;
657 attributeList->list[0].oicType = OIC_ATTR_INT;
658 attributeList->list[0].val.i = onLevel;
659 attributeList->list[0].zigbeeType = ZB_8_UINT;
661 // Level control cluster is dealing with level in the PUT payload.
662 attributeList->CIEMask = attributeList->CIEMask | CIE_MOVE_TO_LEVEL;
666 else if (strcmp(OICResourceType, OIC_CONTACT_SENSOR) == 0)
670 if (OCRepPayloadGetPropInt(payload, OIC_CONTACT_ATTRIBUTE, &value))
672 attributeList->count = 1;
673 attributeList->list[0].oicAttribute = OICStrdup(OIC_CONTACT_ATTRIBUTE);
674 attributeList->list[0].zigBeeAttribute = ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID;
675 attributeList->list[0].oicType = OIC_ATTR_BOOL;
676 attributeList->list[0].val.i = value;
677 attributeList->list[0].zigbeeType = ZB_BOOL;
678 attributeList->CIEMask = (CIECommandMask) 0;
683 else if (strcmp(OICResourceType, OIC_WATER_SENSOR) == 0)
687 if (OCRepPayloadGetPropInt(payload, OIC_WATER_ATTRIBUTE, &value))
689 attributeList->count = 1;
690 attributeList->list[0].oicAttribute = OICStrdup(OIC_WATER_ATTRIBUTE);
691 attributeList->list[0].zigBeeAttribute = ZB_IAS_ZONE_STATUS_ATTRIBUTE_ID;
692 attributeList->list[0].oicType = OIC_ATTR_BOOL;
693 attributeList->list[0].val.i = value;
694 attributeList->list[0].zigbeeType = ZB_BOOL;
695 attributeList->CIEMask = (CIECommandMask) 0;
700 else if (strcmp(OICResourceType, OIC_BINARY_SWITCH) == 0)
704 if (OCRepPayloadGetPropBool(payload, OIC_ON_OFF_ATTRIBUTE, &value))
706 attributeList->count = 1;
707 attributeList->list[0].oicAttribute = OICStrdup(OIC_ON_OFF_ATTRIBUTE);
708 attributeList->list[0].zigBeeAttribute = ZB_ON_OFF_ATTRIBUTE_ID;
709 attributeList->list[0].oicType = OIC_ATTR_BOOL;
710 attributeList->list[0].val.b = value;
711 attributeList->list[0].zigbeeType = ZB_BOOL;
713 attributeList->CIEMask = attributeList->CIEMask | CIE_RON_OFF;
717 else if (strcmp(OICResourceType, OIC_CHROMA_LIGHT) == 0)
720 if (OCRepPayloadGetPropString(payload, OIC_COLOUR_TEMPERATURE_ATTRIBUTE, &value))
722 attributeList->count = 1;
723 attributeList->list[0].oicAttribute = OICStrdup(OIC_COLOUR_TEMPERATURE_ATTRIBUTE);
724 attributeList->list[0].zigBeeAttribute = ZB_COLOR_TEMPERATURE_ATTRIBUTE_ID;
725 attributeList->list[0].oicType = OIC_ATTR_STRING;
726 attributeList->list[0].val.str = value;
727 attributeList->list[0].zigbeeType = ZB_16_UINT;
728 attributeList->CIEMask = (CIECommandMask) 0;
736 OCEntityHandlerResult getDoubleValueFromString(const char *str, double *outDouble)
738 if (!str || !outDouble)
742 size_t hexOutValSize = strlen(HexPrepend) + strlen(str) + 1;
743 char * hexOutVal = (char *) OICCalloc(1, hexOutValSize);
748 OICStrcpy(hexOutVal, hexOutValSize, HexPrepend);
749 OICStrcat(hexOutVal, hexOutValSize, str);
753 double value = strtod(hexOutVal, &endPtr);
755 if (errno != 0 || *endPtr != 0 || value == HUGE_VALF || value == HUGE_VALL)
767 OCEntityHandlerResult getColourTemperatureFromString(const char* str, int64_t* outVal)
773 // str comes in as "X,Y,T" where "T" is the colour temperature.
774 // Iterate 3 times to retrieve the last value.
775 char * strstr = OICStrdup(str);
776 char * savePtr = NULL;
778 for (int i=0; i<3; i++)
780 temp = strtok_r(strstr, ",", &savePtr);
788 OCStackResult result = getDoubleValueFromString(temp, (double *)outVal);
793 OCEntityHandlerResult processGetRequest(PIPluginBase * plugin,
794 OCEntityHandlerRequest *ehRequest, OCRepPayload **payload)
796 if (!plugin || !ehRequest || !payload)
800 uint32_t attributeListIndex = 0;
801 OCStackResult stackResult = OC_STACK_OK;
802 PIResource_Zigbee * piResource = NULL;
804 AttributeList attributeList = { 0, (CIECommandMask) 0,
805 .list[0] = { NULL, NULL, OIC_ATTR_NULL, ZB_NULL, { .i = 0 } } };
806 stackResult = GetResourceFromHandle(plugin, (PIResource**) (&piResource),
807 ehRequest->resource);
808 if (stackResult != OC_STACK_OK)
810 OC_LOG(ERROR, TAG, "Failed to get resource from handle");
813 stackResult = getZigBeeAttributesForOICResource(
814 piResource->header.piResource.resourceTypeName, &attributeList);
815 if (stackResult != OC_STACK_OK)
817 OC_LOG_V(ERROR, TAG, "Failed to fetch attributes for %s",
818 piResource->header.piResource.resourceTypeName);
822 *payload = OCRepPayloadCreate();
825 OC_LOG(ERROR, TAG, PCF("Failed to allocate Payload"));
828 bool boolRes = OCRepPayloadSetUri(*payload, piResource->header.piResource.uri);
829 if (boolRes == false)
831 OCRepPayloadDestroy(*payload);
834 for (uint32_t i = 0; i<attributeList.count; i++)
836 char * outVal = NULL;
837 uint8_t outValLength = 0;
839 stackResult = TWGetAttribute(piResource->eui,
841 piResource->endpointId,
842 piResource->clusterId,
843 attributeList.list[i].zigBeeAttribute,
848 if (stackResult != OC_STACK_OK || !outVal)
850 stackResult = OC_EH_ERROR;
851 OCRepPayloadDestroy(*payload);
854 if (attributeList.list[i].oicType == OIC_ATTR_INT)
858 // Third arg is 16 as outVal is a hex Number
859 uint64_t value = strtol(outVal, &endPtr, 16);
861 if (*endPtr != 0 || errno != 0)
865 if (!attributeList.list[i].oicAttribute)
869 if (strcmp(attributeList.list[i].oicAttribute, OIC_DIMMING_ATTRIBUTE) == 0)
871 // OIC Dimming operates between 0-100, while Zigbee operates
872 // between 0-254 (ie. 0xFE).
879 value = value / 2.54;
882 else if (strcmp(attributeList.list[i].oicAttribute, OIC_COLOUR_TEMPERATURE_ATTRIBUTE) == 0)
884 // OIC Chroma requires: Hue, Saturation, and ColorSpaceValue (color space value is
885 // "chromaX, chromaY, colourTemperature").
886 // ZigBee HA requires: "currentX", "currentY", while we're trying to map a specific
887 // device which goes against the HA spec and ONLY implements "ColorTemperature".
888 // Because of the misalignments between OIC Smart Home and ZigBee HA specs, the
889 // follow assumption is required:
890 // - *X and *Y will be zero, and therefore ignored (or passed as zero where
892 // OIC ColorTemperature operates between 0-1000 (degrees Kelvin) in string form,
893 // while ZigBee operates between 0-65279 (bits, with an operating range
894 // 15.32-1,000,000 (degrees Kelvin)).
895 // ZigBee HA states: ColorTemperature(bits) = 1,000,000/ColorTemperature(Kelvin)
896 // Invalid: ColorTemperature==0 || ColorTemperature==65535
897 // However the specific bulb we're mapping only operates between 2700-6500 (Kelvin)
898 // which equates to 0x0099-0x0172 (ie. 153-370; giving a resolution of 217 bits).
899 // Conversion from ZigBee HA specific bulb to OIC temporary mapping:
900 // OIC Value: 0-100 ZigBee Value: (0-217)+153
901 // OICValue = (ZigBeeValue-153)/2.17
903 value = (value-153)/2.17;
905 boolRes = OCRepPayloadSetPropInt(*payload,
906 attributeList.list[i].oicAttribute,
909 else if (attributeList.list[i].oicType == OIC_ATTR_DOUBLE)
913 if (getDoubleValueFromString(outVal, &value) != OC_EH_OK)
917 if (!piResource->clusterId)
921 if (strcmp(piResource->clusterId, ZB_TEMPERATURE_CLUSTER) == 0)
923 // Divide by 100 as temperature readings have a resolution of
924 // 0.01 or one hundreth of a degree celsius.
927 boolRes = OCRepPayloadSetPropDouble(*payload,
928 attributeList.list[i].oicAttribute,
931 else if (attributeList.list[i].oicType == OIC_ATTR_STRING)
933 boolRes = OCRepPayloadSetPropString(*payload,
934 attributeList.list[i].oicAttribute,
937 else if (attributeList.list[i].oicType == OIC_ATTR_BOOL)
941 // Third arg is 16 as outVal is a hex Number
942 uint64_t value = strtol(outVal, &endPtr, 16);
944 if (errno != 0 || *endPtr != 0)
948 // value COULD be a bit mask and the LSB indicates boolean true/false.
949 // If not a bit mask, it'll be plain 0 or 1.
951 boolRes = OCRepPayloadSetPropBool(*payload,
952 attributeList.list[i].oicAttribute,
959 if (boolRes == false)
961 stackResult = OC_EH_ERROR;
966 for (; attributeListIndex < attributeList.count; attributeListIndex++)
968 OICFree(attributeList.list[attributeListIndex].oicAttribute);
973 OCEntityHandlerResult processPutRequest(PIPluginBase * plugin,
974 OCEntityHandlerRequest *ehRequest, OCRepPayload **payload)
976 if (!plugin || !ehRequest || !payload)
980 OCStackResult stackResult = OC_STACK_OK;
981 PIResource_Zigbee *piResource = NULL;
982 AttributeList attributeList = {
985 .list[0] = { NULL, NULL, OIC_ATTR_NULL, ZB_NULL, { .i = 0 } }
988 stackResult = GetResourceFromHandle(plugin,
989 ((PIResource **) (&piResource)),
990 ehRequest->resource);
991 if (stackResult != OC_STACK_OK)
993 OC_LOG(ERROR, TAG, "Failed to get resource from handle");
997 bool boolRes = getZigBeeAttributesIfValid(
998 piResource->header.piResource.resourceTypeName,
999 &attributeList, *payload);
1000 if (boolRes == false)
1002 OC_LOG_V(ERROR, TAG, "Failed to fetch attributes for %s",
1003 piResource->header.piResource.resourceTypeName);
1008 for (; i<attributeList.count; i++)
1010 if (attributeList.list[i].oicType == OIC_ATTR_INT)
1012 char value[MAX_STRLEN_INT] = {};
1013 if (attributeList.CIEMask || CIE_MOVE_TO_LEVEL)
1015 int64_t rangeDiff = 0;
1016 // OIC Dimming operates between 0-100, while Zigbee
1017 // operates between 0-254 (ie. 0xFE).
1018 rangeDiff = attributeList.list[i].val.i * 0xFE/100;
1019 if (rangeDiff > 0xFE)
1027 if (rangeDiff <= 0xFE)
1030 int strRet = snprintf(value, sizeof(value), "%02x", (unsigned int) rangeDiff);
1033 OC_LOG_V(ERROR, TAG, "Failed to parse string due to errno: %d", errno);
1037 stackResult = TWMoveToLevel(piResource->nodeId, piResource->endpointId,
1038 DEFAULT_MOVETOLEVEL_MODE, value, DEFAULT_TRANS_TIME,
1039 (PIPlugin_Zigbee*)plugin);
1044 int strRet = snprintf(value,
1047 attributeList.list[i].val.i);
1050 OC_LOG_V(ERROR, TAG, "Failed to parse string due to errno: %d", errno);
1053 stackResult = TWSetAttribute(piResource->eui,
1054 piResource->nodeId, piResource->endpointId,
1055 piResource->clusterId, attributeList.list[i].zigBeeAttribute,
1056 getZBDataTypeString(attributeList.list[i].zigbeeType), value,
1057 (PIPlugin_Zigbee*)plugin);
1059 if (stackResult != OC_STACK_OK)
1064 else if (attributeList.list[i].oicType == OIC_ATTR_DOUBLE)
1066 char value[MAX_STRLEN_DOUBLE] = {};
1068 int strRet = snprintf(value, sizeof(value), "%f", attributeList.list[i].val.d);
1071 OC_LOG_V(ERROR, TAG, "Failed to parse string due to errno: %d", errno);
1074 stackResult = TWSetAttribute(piResource->eui,
1075 piResource->nodeId, piResource->endpointId,
1076 piResource->clusterId, attributeList.list[i].zigBeeAttribute,
1077 getZBDataTypeString(attributeList.list[i].zigbeeType), value,
1078 (PIPlugin_Zigbee*)plugin);
1080 else if (attributeList.list[i].oicType == OIC_ATTR_STRING)
1082 if (strcmp(attributeList.list[i].oicAttribute, OIC_COLOUR_TEMPERATURE_ATTRIBUTE) == 0)
1084 char *endPtr = NULL;
1085 uint16_t zbVal = (uint16_t)strtod(attributeList.list[i].val.str, &endPtr);
1086 zbVal = (zbVal*2.17)+153;
1087 char value[5] = {0};
1089 int strRet = snprintf(value, sizeof(value), "%04x", zbVal);
1092 OC_LOG_V(ERROR, TAG, "Failed to parse string due to errno: %d", errno);
1096 TWColorMoveToColorTemperature(piResource->nodeId, piResource->endpointId,
1097 value, DEFAULT_TRANS_TIME,
1098 (PIPlugin_Zigbee*)plugin);
1102 stackResult = TWSetAttribute(piResource->eui,
1103 piResource->nodeId, piResource->endpointId,
1104 piResource->clusterId, attributeList.list[i].zigBeeAttribute,
1105 getZBDataTypeString(attributeList.list[i].zigbeeType),
1106 attributeList.list[i].val.str,
1107 (PIPlugin_Zigbee*)plugin);
1109 if (stackResult != OC_STACK_OK)
1114 else if (attributeList.list[i].oicType == OIC_ATTR_BOOL)
1116 char * value = attributeList.list[i].val.b ? "1" : "0";
1117 if (attributeList.CIEMask || CIE_RON_OFF)
1119 stackResult = TWSwitchOnOff(piResource->nodeId, piResource->endpointId, value,
1120 (PIPlugin_Zigbee*)plugin);
1124 stackResult = TWSetAttribute(piResource->eui,
1125 piResource->nodeId, piResource->endpointId,
1126 piResource->clusterId, attributeList.list[i].zigBeeAttribute,
1127 getZBDataTypeString(attributeList.list[i].zigbeeType),
1129 (PIPlugin_Zigbee*)plugin);
1131 if (stackResult != OC_STACK_OK)
1142 return processGetRequest(plugin, ehRequest, payload);
1145 OCEntityHandlerResult ProcessEHRequest(PIPluginBase * plugin,
1146 OCEntityHandlerRequest *ehRequest, OCRepPayload **payload)
1148 if (!ehRequest || !payload)
1152 if (ehRequest->method == OC_REST_GET)
1154 return processGetRequest(plugin, ehRequest, payload);
1156 else if (ehRequest->method == OC_REST_PUT)
1158 return processPutRequest(plugin, ehRequest, payload);
1162 return OC_EH_FORBIDDEN;
1166 char * getZBDataTypeString(ZigBeeAttributeDataType attrType)
1171 return ZB_DATA_TYPE_NULL;
1173 return ZB_DATA_TYPE_1_BYTE;
1175 return ZB_DATA_TYPE_2_BYTE;
1177 return ZB_DATA_TYPE_3_BYTE;
1179 return ZB_DATA_TYPE_4_BYTE;
1181 return ZB_DATA_TYPE_5_BYTE;
1183 return ZB_DATA_TYPE_6_BYTE;
1185 return ZB_DATA_TYPE_7_BYTE;
1187 return ZB_DATA_TYPE_8_BYTE;
1189 return ZB_DATA_TYPE_BOOL;
1191 return ZB_DATA_TYPE_1_BYTE;
1193 return ZB_DATA_TYPE_2_BYTE;
1195 return ZB_DATA_TYPE_3_BYTE;
1197 return ZB_DATA_TYPE_4_BYTE;
1199 return ZB_DATA_TYPE_5_BYTE;
1201 return ZB_DATA_TYPE_6_BYTE;
1203 return ZB_DATA_TYPE_7_BYTE;
1205 return ZB_DATA_TYPE_8_BYTE;
1207 return ZB_DATA_TYPE_SIGNED_INT_16;
1209 return ZB_DATA_TYPE_UNSIGNED_INT_8;
1211 return ZB_DATA_TYPE_UNSIGNED_INT_16;
1213 return ZB_DATA_TYPE_NULL;