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