Initial plugin "Nest Plugin" for bridging project.
[iotivity.git] / bridging / plugins / nest_plugin / nest_resource.cpp
1 //******************************************************************
2 //
3 // Copyright 2017 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 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <string>
25 #include <unistd.h>
26 #include <signal.h>
27 #include <pthread.h>
28 #include <fstream>
29 #include <map>
30 #include <memory>
31 #include <set>
32 #include "plugin_server.h"
33 #include "oic_malloc.h"
34 #include "oic_string.h"
35 #include "nest.h"
36 #include <cstdlib>
37 #include <iostream>
38 #include <time.h>
39 #include "logger.h"
40 #include "ConcurrentIotivityUtils.h"
41 #include "message_handler.h"
42 #include "ocpayload.h"
43 #include "gw_error_code.h"
44
45 using namespace std;
46
47 /*******************************************************************************
48  * Pound defines and structure declarations go here
49  ******************************************************************************/
50 #define TAG "NEST_RESOURCE"
51
52 #define DEVICE_NAME "Nest Translator"
53 #define DEVICE_TYPE "oic.d.thermostat"
54 #define MANUFACTURER_NAME "Nest"
55
56 #define NEST_ID_TAG "x.com.intel.id"
57 #define NEST_LAST_CONNECTION_TAG "x.com.intel.lastConnection"
58
59 #define TEMPERATURE_TAG "temperature"
60
61 #define BM 3
62
63 typedef struct
64 {
65     uint16_t humidity;
66     uint32_t temperature;
67     uint16_t targetTempF;
68     uint32_t hvacMode;
69     char accessToken[NEST_ACCESS_TOKEN_LEN];
70     char deviceId[MAX_LENGTH_64];
71 } plugin_specific_data_t;
72
73 /* TODO: Come up with a name dynamically when more than 1 thermostats
74  * are found. At that time of writing this plugin, it is assumed to
75  * be only 1 Nest thermostat.
76  * TODO: Rename URI with thermostat since Nest also provides smoke detector.
77  */
78 static const std::string NEST_THERMOSTAT_IF = "oic.if.a";
79 static const std::string NEST_THERMOSTAT_RT = "oic.r.temperature";
80
81 static char CRED_FILE[] = "./oic_svr_db_nest.dat";
82
83 FILE *nestSecurityFile(const char *path, const char *mode)
84 {
85     (void) path;
86     return fopen(CRED_FILE, mode);
87 }
88
89 /*******************************************************************************
90  * global data goes here
91  ******************************************************************************/
92 plugin_ctx_t *g_ctx = NULL;
93 Nest *g_nest = NULL;
94 NestThermostat::devices g_devices;
95 std::string nest_client_id;
96 std::string nest_client_secret;
97
98 std::map<std::string, NestThermostatSharedPtr> uriToNestThermostatMap;
99 std::map<std::string, NestThermostatSharedPtr> addedThermostats;
100
101 uint16_t getTemperatureAndUpdateMap(NestThermostatSharedPtr t);
102 OCEntityHandlerResult resource_entity_handler_cb(OCEntityHandlerFlag flag,
103                                                  OCEntityHandlerRequest *entityHandlerRequest, void *callbackParam);
104
105 GW_RESULT loadNestAuthConfig(std::string filename, std::string &pincode,
106                      std::string &accessToken)
107 {
108     FILE *fp = fopen(filename.c_str(), "r");
109     char str[1024];
110     GW_RESULT result = GW_RESULT_INTERNAL_ERROR;
111
112     if (NULL != fp)
113     {
114         if (fgets(str, 1023, fp) == NULL)
115         {
116             OIC_LOG_V(ERROR, TAG, "fgets failed on %s", filename.c_str());
117             goto CLEANUP;
118         }
119         //TODO the logic below needs to be fixed.
120         //The intention here was to remove the last character which was new line.
121         //Current logic doesn't cover edge case like empty string and etc.
122         str[strlen(str) - 1] = '\0';
123         pincode = std::string(str);
124
125         if (fgets(str, 1023, fp) == NULL)
126         {
127             OIC_LOG_V(ERROR, TAG, "fgets failed on %s", filename.c_str());
128             goto CLEANUP;
129         }
130         str[strlen(str) - 1] = '\0';
131         accessToken = std::string(str);
132
133         if (fgets(str, 1023, fp) == NULL)
134         {
135             OIC_LOG_V(ERROR, TAG, "fgets failed on %s", filename.c_str());
136             goto CLEANUP;
137         }
138         str[strlen(str) - 1] = '\0';
139         nest_client_id = std::string(str);
140
141         if (fgets(str, 1023, fp) == NULL)
142         {
143             OIC_LOG_V(ERROR, TAG, "fgets failed on %s", filename.c_str());
144             goto CLEANUP;
145         }
146         str[strlen(str) - 1] = '\0';
147         nest_client_secret = std::string(str);
148
149         result = GW_RESULT_OK;
150     }
151     else
152     {
153         OIC_LOG_V(ERROR, TAG, "Could not open %s.\n", filename.c_str());
154     }
155
156     CLEANUP:
157     if (fp != NULL)
158     {
159         fclose(fp);
160     }
161     return result;
162 }
163
164 Nest::ACCESS_TOKEN populateAccessTokenFromFile(std::string accessToken)
165 {
166     Nest::ACCESS_TOKEN aTok(accessToken.c_str());
167
168     return aTok;
169 }
170
171 GW_RESULT checkValidityOfExistingToken(Nest::ACCESS_TOKEN aTok)
172 {
173     GW_RESULT result = GW_RESULT_OK;
174
175     g_nest->setAccessToken(aTok);
176
177     //verify the token by sending the token to nest cloud
178     if (g_nest->isAuthorized())
179     {
180         OIC_LOG(INFO, TAG, "Successfully authorized");
181     }
182     else
183     {
184         OIC_LOG(ERROR, TAG, "Could not authorize, access token is invalid/expired");
185         result = GW_RESULT_INTERNAL_ERROR;
186     }
187
188     return result;
189 }
190
191 void updateNestTokenFile(std::string filename, std::string pincode, std::string accessToken)
192 {
193     FILE *fp = fopen(filename.c_str(), "w");
194     if(fp == NULL)
195     {
196         OIC_LOG(ERROR, TAG, "Failed to open nest.cnf file");
197         return;
198     }
199     fputs(pincode.c_str(), fp);
200     fputs("\n", fp);
201     fputs(accessToken.c_str(), fp);
202     fputs("\n", fp);
203     fputs(nest_client_id.c_str(), fp);
204     fputs("\n", fp);
205     fputs(nest_client_secret.c_str(), fp);
206     fputs("\n", fp);
207
208     fclose(fp);
209 }
210
211 GW_RESULT refreshAccessToken(std::string filename, std::string pincode)
212 {
213     GW_RESULT result = GW_RESULT_OK;
214
215     Nest::ACCESS_TOKEN aToken;
216     if (g_nest->getAccessToken(pincode, aToken, nest_client_id, nest_client_secret) != GW_RESULT_OK)
217     {
218         OIC_LOG(ERROR, TAG, "get token failed");
219         return GW_RESULT_INTERNAL_ERROR;
220     }
221
222     g_nest->setAccessToken(aToken);
223
224     if (g_nest->isAuthorized())
225     {
226         OIC_LOG(INFO, TAG, "Successfully authorized");
227         std::string aTok(aToken.accessToken);
228         updateNestTokenFile(filename, pincode, aTok);
229     }
230     else
231     {
232         OIC_LOG(ERROR, TAG, "Could not authorize, Please put a new pincode into the file");
233         result = GW_RESULT_INTERNAL_ERROR;
234     }
235
236     return result;
237 }
238
239 /* This is a plugin's specific entry point function that allows the plugin
240  * to create and initialize static structures.
241  *
242  * returns:
243  *     GW_RESULT_OK             - no errors
244  *     GW_RESULT_INTERNAL_ERROR - stack process error
245  */
246 GW_RESULT plugin_create(plugin_ctx_t **plugin_specific_ctx)
247 {
248     GW_RESULT result = GW_RESULT_OK;
249
250     /* initialize global data and return value */
251     *plugin_specific_ctx = NULL;
252
253     /* allocate a context structure for the plugin */
254     plugin_ctx_t *ctx = (plugin_ctx_t *) OICMalloc(sizeof(plugin_ctx_t));
255
256     /* initialize the plugin context */
257     if (ctx != NULL)
258     {
259         memset(ctx, 0, sizeof(plugin_ctx_t));
260         *plugin_specific_ctx = ctx;
261         g_ctx = ctx;
262     }
263     else
264     {
265         OIC_LOG(ERROR, TAG, "Unable to allocate plugin specific context.");
266         result = GW_RESULT_INTERNAL_ERROR;
267         return (result);
268     }
269
270     ctx->device_name = (char *) DEVICE_NAME;
271     ctx->resource_type = (char *) DEVICE_TYPE;
272     ctx->open = nestSecurityFile;
273
274     std::string pincode = "";
275     std::string accessToken = "";
276
277     std::string filename = "nest.cnf";
278
279     if (loadNestAuthConfig(filename, pincode, accessToken) != GW_RESULT_OK)
280     {
281         OIC_LOG(ERROR, TAG, "Unable to load nest.cnf");
282         return GW_RESULT_INTERNAL_ERROR;
283     }
284
285     g_nest = new Nest();
286     Nest::ACCESS_TOKEN aTok = populateAccessTokenFromFile(accessToken);
287
288     result = checkValidityOfExistingToken(aTok);
289
290     if(GW_RESULT_OK != result)
291     {
292         OIC_LOG(ERROR, TAG, "Nest object could not be created, requesting new access token");
293         result = refreshAccessToken(filename, pincode);
294     }
295
296     if(GW_RESULT_OK != result)
297     {
298         delete(g_nest);
299     }
300
301     OIC_LOG_V(INFO, TAG, "Plugin create return value:%d.", result);
302
303     /*
304      * NOTE: What do we do if the create for some reason failed.  To we assume that the destroy
305      * will be called if the create fails??  Let let the plugin loader pick up the pieces by
306      * calling destroy on an imperfectly created plugin.  Calling entry point APIs from within
307      * the implementation can cause some real problems (e.g. deadlock situations).
308      */
309
310     return (result);
311 }
312
313 /* Invoked by the plugin's OIC server to allow the plugin to use IoTivity apis
314  *
315  * returns:
316  *     GW_RESULT_OK             - no errors
317  *     GW_RESULT_INTERNAL_ERROR - stack process error
318  */
319 GW_RESULT plugin_start(plugin_ctx_t *plugin_specific_ctx)
320 {
321     GW_RESULT result = GW_RESULT_OK;
322     if (plugin_specific_ctx == NULL)
323     {
324         result = GW_RESULT_INTERNAL_ERROR;
325     }
326
327     return (result);
328 }
329
330 bool createSecureResources()
331 {
332     char *non_secure_env = getenv("NONSECURE");
333
334     if (non_secure_env != NULL && std::string(non_secure_env) == "true")
335     {
336         OIC_LOG(INFO, TAG,"Creating NON SECURE resources");
337         return false;
338     }
339     OIC_LOG(INFO, TAG,"Creating SECURE resources");
340     return true;
341 }
342
343 /*
344  *  For initiating the scan for thermostats
345  *  Parameters :
346  *             plugin_ctx_t - Plugin context ,
347  *             plugin_scan_t - Scan Message.
348  */
349 GW_RESULT plugin_scan(plugin_ctx_t *ctx, mpm_pipe_message_t * message)
350 {
351     (void) ctx; (void) message;
352     int nestResult = GW_RESULT_OK;
353
354     std::vector<NestThermostatSharedPtr> thermostatScanned;
355
356     nestResult = g_nest->getThermostats(thermostatScanned);
357     if (GW_RESULT_OK == nestResult)
358     {
359         if (thermostatScanned.size() <= 0)
360         {
361             OIC_LOG(INFO, TAG, "getThermostats succeeded but zero thermostats found");
362         }
363         else
364         {
365             for (uint32_t i = 0; i < thermostatScanned.size(); ++i)
366             {
367                 NestThermostatSharedPtr thermostat = thermostatScanned[i];
368                 NestThermostat::THERMOSTAT data;
369                 thermostat->get(data);
370                 std::string uri = "/nest/" + data.devInfo.id;
371                 OIC_LOG_V(INFO, TAG, "uri: %s", uri.c_str());
372
373                 if(addedThermostats.find(uri) != addedThermostats.end())
374                 {
375                     OIC_LOG_V(INFO, TAG,"Already added %s. Ignoring", uri.c_str());
376                     continue;
377                 }
378
379                 uriToNestThermostatMap[uri] = thermostat;
380
381                 std::string s = uri;
382                 unsigned long size = s.size() + 1;
383                 char *m = (char*) OICCalloc(1, size);
384                 memcpy(m, s.c_str(), size);
385                 send_response(m, size, MPM_SCAN);
386                 OICFree(m);
387
388             }
389         }
390     }
391     else
392     {
393         OIC_LOG_V(ERROR, TAG, "getThermostats returned error %i", nestResult);
394     }
395
396     return nestResult;
397
398 }
399
400 /***
401  * Creates 1 OCF resource for a NestThermostat for temperature control.
402  *
403  * @param uri Base uri. Resource uri is the same as baseUri
404  * @return
405  */
406 GW_RESULT createOCFResource(const std::string &uri)
407 {
408     uint8_t resourceProperties = (OC_OBSERVABLE | OC_DISCOVERABLE);
409     if (createSecureResources()) resourceProperties |= OC_SECURE;
410
411     ConcurrentIotivityUtils::createResource(uri, NEST_THERMOSTAT_RT, NEST_THERMOSTAT_IF, resource_entity_handler_cb,
412                                             NULL, resourceProperties);
413
414     return GW_RESULT_OK;
415 }
416
417 GW_RESULT  deleteOCFResource(const std::string &uri)
418 {
419     ConcurrentIotivityUtils::deleteResource(uri);
420
421     return GW_RESULT_OK;
422 }
423
424 void createPayloadForMetadata(resourceList **list, const char *uri)
425 {
426     resourceList *tempPtr = NULL;
427     tempPtr = (resourceList *) OICCalloc(1, sizeof(resourceList));
428     if(tempPtr == NULL)
429     {
430         OIC_LOG(ERROR, TAG, "Memory allocation failed");
431         return;
432     }
433
434     OICStrcpy(tempPtr->rt, MAX_LENGTH_64, NEST_THERMOSTAT_RT.c_str());
435     OICStrcpy(tempPtr->href, MAX_URI_LEN, uri);
436     OICStrcpy(tempPtr->interfaces, MAX_LENGTH_64, NEST_THERMOSTAT_IF.c_str());
437     tempPtr->bitmap = BM;
438     tempPtr->next = *list;
439     *list  = tempPtr;
440 }
441
442 void updatePluginSpecificData(NestThermostat::THERMOSTAT thermostat, plugin_specific_data_t *pluginDetails)
443 {
444     pluginDetails->humidity = thermostat.humidity;
445     pluginDetails->hvacMode = thermostat.hvacMode;
446     pluginDetails->targetTempF = thermostat.targetTempF;
447     pluginDetails->temperature = thermostat.temperature;
448     memcpy(pluginDetails->accessToken, g_nest->getTok().c_str(), g_nest->getTok().length());
449     memcpy(pluginDetails->deviceId, thermostat.devInfo.id.c_str(), thermostat.devInfo.id.length());
450 }
451
452 GW_RESULT plugin_add(plugin_ctx_t *ctx, mpm_pipe_message_t * message)
453 {
454     (void) ctx;
455     char *c_uri = NULL;
456     uint8_t *buff = (uint8_t *)OICCalloc(1, MAX_METADATA_LEN);
457     resourceList *list = NULL;
458
459     device_specific_data_t deviceConfiguration;
460     plugin_specific_data_t pluginSpecificDetails;
461     memset(&deviceConfiguration, 0, sizeof(device_specific_data_t));
462     memset(&pluginSpecificDetails, 0, sizeof(plugin_specific_data_t));
463
464     if(message->payloadSize > 0 && message->payloadSize < SIZE_MAX)
465     {
466         c_uri = (char*) OICCalloc(1, message->payloadSize);
467     }
468     if(c_uri == NULL)
469     {
470         OIC_LOG(ERROR, TAG, "OICCalloc Failed");
471         OICFree(buff);
472         return GW_RESULT_INTERNAL_ERROR;
473     }
474     memcpy(c_uri, message->payload, message->payloadSize);
475     std::string uri(c_uri);
476     OICFree(c_uri);
477
478     if(addedThermostats.find(uri) != addedThermostats.end())
479     {
480         OIC_LOG_V(ERROR, TAG,"%s already added", uri.c_str());
481         return GW_RESULT_ALREADY_CREATED;
482     }
483     if(uriToNestThermostatMap.find(uri) == uriToNestThermostatMap.end())
484     {
485         OIC_LOG_V(ERROR, TAG,"%s was NOT discovered in a scan", uri.c_str());
486         return GW_CB_RESULT_ERROR;
487     }
488
489     createOCFResource(uri);
490     addedThermostats[uri] = uriToNestThermostatMap[uri];
491
492     createPayloadForMetadata(&list,uri.c_str());
493
494     NestThermostat::THERMOSTAT thermostat;
495     addedThermostats[uri]->get(thermostat);
496     updatePluginSpecificData(thermostat, &pluginSpecificDetails);
497
498     OICStrcpy(deviceConfiguration.devName, MAX_LENGTH_64, DEVICE_NAME);
499     OICStrcpy(deviceConfiguration.devType, MAX_LENGTH_64, DEVICE_TYPE);
500     OICStrcpy(deviceConfiguration.manufacturerName, MAX_LENGTH_256, MANUFACTURER_NAME);
501     formMetaData(list, &deviceConfiguration, buff, MAX_METADATA_LEN, (void *)&pluginSpecificDetails, sizeof(plugin_specific_data_t));
502
503     add_response response;
504     memset(&response, 0, sizeof(add_response));
505     OICStrcpy(response.uri, MAX_URI_LEN, uri.c_str());
506     memcpy(response.metadata, buff, MAX_METADATA_LEN);
507
508     size_t size = sizeof(add_response);
509
510     send_response(&response, size, MPM_ADD);
511
512     OICFree(buff);
513     return GW_RESULT_OK;
514 }
515
516 GW_RESULT plugin_remove(plugin_ctx_t *ctx, mpm_pipe_message_t * message)
517 {
518     (void) ctx;
519     char *c_uri = NULL;
520
521     if(message->payloadSize > 0 && message->payloadSize < SIZE_MAX)
522     {
523         c_uri = (char*) OICCalloc(1, message->payloadSize);
524     }
525     if(c_uri == NULL)
526     {
527         OIC_LOG(ERROR, TAG, "OICCalloc Failed");
528         return GW_RESULT_INTERNAL_ERROR;
529     }
530     memcpy(c_uri, message->payload, message->payloadSize);
531     std::string uri(c_uri);
532     OICFree(c_uri);
533
534     deleteOCFResource(uri);
535
536     addedThermostats.erase(uri);
537     uriToNestThermostatMap.erase(uri);
538
539     std::string s = uri;
540     unsigned long size = s.size() + 1;
541     char *m = (char*) OICCalloc(1, size);
542     memcpy(m, s.c_str(), size);
543     send_response(m, size, MPM_REMOVE);
544     OICFree(m);
545
546     return GW_RESULT_OK;
547 }
548
549 GW_RESULT plugin_reconnect(plugin_ctx_t *ctx, mpm_pipe_message_t * message)
550 {
551     (void)ctx;
552     GW_RESULT result = GW_RESULT_INTERNAL_ERROR;
553     resourceList *list = NULL, *temp = NULL;
554     char *buff = NULL;
555     std::vector<NestThermostatSharedPtr> thermostatsReconnected;
556     void *details = NULL;
557
558     if(message->payloadSize > 0 &&message->payloadSize < SIZE_MAX)
559     {
560         buff = (char *)OICCalloc(1, message->payloadSize);
561         if(buff == NULL)
562         {
563                 OIC_LOG(ERROR, TAG, "OICCalloc Failed");
564                 return GW_RESULT_INTERNAL_ERROR;
565         }
566     }
567     else
568     {
569         OIC_LOG(ERROR, TAG, "payload size is invalid");
570         return GW_RESULT_INTERNAL_ERROR;
571     }
572
573     memcpy(buff, message->payload, message->payloadSize);
574
575     ParseMetaData((uint8_t*)buff, MAX_METADATA_LEN, &list, &details);
576
577     plugin_specific_data_t *pluginDetails = (plugin_specific_data_t *)details;
578
579     std::shared_ptr<NestThermostat> sharedThermostat = std::make_shared<NestThermostat>
580                                                                         (pluginDetails->accessToken,
581                                                                              pluginDetails->humidity,
582                                                                          pluginDetails->hvacMode,
583                                                                          pluginDetails->targetTempF,
584                                                                          pluginDetails->temperature,
585                                                                          pluginDetails->deviceId);
586     thermostatsReconnected.push_back(sharedThermostat);
587
588     std::string uri;
589
590     NestThermostat::THERMOSTAT data;
591
592     sharedThermostat->get(data);
593     uri = "/nest/" + data.devInfo.id;
594
595     if(uriToNestThermostatMap.find(uri) != uriToNestThermostatMap.end())
596     {
597         OIC_LOG_V(INFO, TAG, "Already found %s. Ignoring", uri.c_str());
598     }
599     else
600     {
601         uriToNestThermostatMap[uri] = sharedThermostat;
602      }
603
604     if(addedThermostats.find(uri) != addedThermostats.end())
605     {
606         OIC_LOG_V(ERROR, TAG, "%s already added", uri.c_str());
607         result = GW_RESULT_ALREADY_CREATED;
608         goto CLEANUP;
609     }
610     if(uriToNestThermostatMap.find(uri) == uriToNestThermostatMap.end())
611     {
612         result = GW_CB_RESULT_ERROR;
613         goto CLEANUP;
614     }
615
616     while(list)
617     {
618         temp = list;
619         std::string resourceUri(list->href);
620         OIC_LOG_V(INFO, TAG, "resource uri = %s", resourceUri.c_str());
621         createOCFResource(uri);
622         list = list->next;
623         OICFree(temp);
624     }
625
626     addedThermostats[uri] = uriToNestThermostatMap[uri];
627     getTemperatureAndUpdateMap(addedThermostats[uri]);
628     result = GW_RESULT_OK;
629
630     CLEANUP:
631     if(buff != NULL)
632     {
633         OICFree(buff);
634         buff = NULL;
635     }
636     if(pluginDetails != NULL)
637     {
638         OICFree(pluginDetails);
639         pluginDetails = NULL;
640     }
641
642     return result;
643 }
644
645 /*  Plugin specific entry-point function to stop the plugin's threads
646  *
647  * returns:
648  *     GW_RESULT_OK             - no errors
649  *     GW_RESULT_INTERNAL_ERROR - stack process error
650  */
651 GW_RESULT plugin_stop(plugin_ctx_t *plugin_specific_ctx)
652 {
653     plugin_ctx_t *ctx = plugin_specific_ctx;
654
655     if (NULL != ctx && g_ctx != NULL)
656     {
657         /*for(auto itr: addedThermostats)
658         {
659                 deleteOCFResource(itr.first);
660         }*/
661         OCStopPresence();
662
663         addedThermostats.clear();
664         uriToNestThermostatMap.clear();
665
666         if (plugin_specific_ctx->started == true)
667         {
668             plugin_specific_ctx->stay_in_process_loop = false;
669             plugin_specific_ctx->started = false;
670         }
671     }
672
673     return (GW_RESULT_OK);
674 }
675
676 /* Plugin specific entry-point function to allow the plugin resources to be
677  * freed.
678  *
679  * returns:
680  *     GW_RESULT_OK             - no errors
681  *     GW_RESULT_INTERNAL_ERROR - stack process error
682  */
683 GW_RESULT plugin_destroy(plugin_ctx_t *plugin_specific_ctx)
684 {
685     GW_RESULT result = GW_RESULT_INTERNAL_ERROR;
686     if (plugin_specific_ctx != NULL)
687     {
688         if (plugin_specific_ctx->started == true)
689         {
690             plugin_stop(plugin_specific_ctx);
691         }
692
693         delete(g_nest);
694         OICFree(plugin_specific_ctx);
695         g_ctx = NULL;
696         result = GW_RESULT_OK;
697     }
698
699     OIC_LOG_V(INFO, TAG, "Plugin destroy's return value:%d", result);
700
701     return (result);
702 }
703
704
705
706 NestThermostatSharedPtr getNestThermostatFromOCFResourceUri(std::string resourceUri)
707 {
708     OIC_LOG_V(INFO, TAG, "Request for %s", resourceUri.c_str());
709
710     for(auto uriToNestPair : addedThermostats)
711     {
712         if (resourceUri.find(uriToNestPair.first) != std::string::npos)
713         {
714             return uriToNestPair.second;
715         }
716     }
717     throw "Resource" + resourceUri + " not found";
718 }
719
720 OCRepPayload *addCommonNestProperties(const NestThermostatSharedPtr &t, OCRepPayload *payload)
721 {
722     NestThermostat::THERMOSTAT data;
723     t->get(data);
724     OCRepPayloadSetPropString(payload, NEST_ID_TAG, data.devInfo.id.c_str());
725     OCRepPayloadSetPropString(payload, NEST_LAST_CONNECTION_TAG, data.devInfo.lastConnection.c_str());
726     return payload;
727 }
728
729 uint16_t getTemperatureAndUpdateMap(NestThermostatSharedPtr t)
730 {
731     NestThermostat::THERMOSTAT targetThermostat;
732     t->get(targetThermostat);
733
734     std::vector<NestThermostatSharedPtr> thermostatsGot;
735     g_nest->getThermostats(thermostatsGot);
736
737     for(unsigned int i = 0; i < thermostatsGot.size(); ++i)
738     {
739         NestThermostat::THERMOSTAT fetchedThermostat;
740         thermostatsGot[i]->get(fetchedThermostat);
741         if(fetchedThermostat.devInfo.id == targetThermostat.devInfo.id)
742         {
743             if(targetThermostat.targetTempF != fetchedThermostat.targetTempF)
744                 {
745                     OIC_LOG(INFO, TAG, "temperature value has changed");
746                     targetThermostat.targetTempF = fetchedThermostat.targetTempF;
747                 }
748                 break;
749         }
750     }
751
752     return targetThermostat.targetTempF;
753 }
754
755 OCRepPayload *getTemperaturePayload(NestThermostatSharedPtr t)
756 {
757     OCRepPayload *payload = OCRepPayloadCreate();
758
759     OCRepPayloadSetPropDouble(payload, TEMPERATURE_TAG, (double) getTemperatureAndUpdateMap(t));
760
761     return addCommonNestProperties(t, payload);
762
763 }
764
765 OCRepPayload* process_get_request(NestThermostatSharedPtr t)
766 {
767     return getTemperaturePayload(t);
768 }
769
770 OCEntityHandlerResult processTemperatureUpdate(OCRepPayload *payload, NestThermostatSharedPtr t)
771 {
772     double targetTemp = 0.0;
773     if (!OCRepPayloadGetPropDouble(payload, TEMPERATURE_TAG, &targetTemp))
774     {
775         throw "Payload must contain \"temperature\"";
776     }
777     int result = g_nest->setAwayMode(Nest::eAWHome);
778     if (result == GW_RESULT_OK)
779     {
780         result = t->setTemperature(targetTemp);
781
782         if (result != GW_RESULT_OK)
783         {
784             throw "Error setting brightness for PUT request";
785         }
786     }
787     else
788     {
789         throw "Error setting away mode to home mode for PUT request";
790     }
791     return OC_EH_OK;
792 }
793
794 OCEntityHandlerResult process_put_request(OCRepPayload *payload, NestThermostatSharedPtr t)
795 {
796     if (payload == NULL)
797     {
798         throw "PUT payload cannot be NULL";
799     }
800
801     return processTemperatureUpdate(payload, t);
802 }
803
804 OCEntityHandlerResult resource_entity_handler_cb(OCEntityHandlerFlag flag,
805                                                  OCEntityHandlerRequest *entityHandlerRequest, void *callbackParam)
806 {
807     (void) flag;
808     (void) callbackParam;
809     OCEntityHandlerResult result = OC_EH_OK;
810     GW_RESULT res = GW_RESULT_OK;
811
812     try
813     {
814         std::string uri;
815         ConcurrentIotivityUtils::getUriFromHandle(entityHandlerRequest->resource, uri);
816
817         NestThermostatSharedPtr targetThermostat = getNestThermostatFromOCFResourceUri(uri);
818
819         switch (entityHandlerRequest->method)
820         {
821             case OC_REST_GET:
822                 // Empty GET case as actual request will be processed after the switch case.
823                 break;
824
825             case OC_REST_PUT:
826             case OC_REST_POST:
827
828                 res = process_put_request((OCRepPayload*) entityHandlerRequest->payload, targetThermostat);
829                 if (res != GW_RESULT_OK)
830                      result = OC_EH_ERROR;
831                  break;
832
833             default:
834                 OIC_LOG_V(INFO, TAG,"Unsupported method (%d) recieved", entityHandlerRequest->method);
835                 ConcurrentIotivityUtils::respondToRequestWithError(entityHandlerRequest, "Unsupported method received",
836                     OC_EH_METHOD_NOT_ALLOWED);
837                 return OC_EH_OK;
838         }
839
840         OCRepPayload *responsePayload = process_get_request(targetThermostat);
841
842         ConcurrentIotivityUtils::respondToRequest(entityHandlerRequest, responsePayload, result);
843
844         OCRepPayloadDestroy(responsePayload);
845     }
846     catch (const char *errorMessage)
847     {
848         ConcurrentIotivityUtils::respondToRequestWithError(entityHandlerRequest, errorMessage, OC_EH_ERROR);
849         return OC_EH_OK;
850     }
851
852     return OC_EH_OK;
853 }