Use `URI` all caps in documentation
[iotivity.git] / bridging / plugins / lyric_plugin / honeywellResource.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
22 /* This file contains plugin specific code that adapts the native resource model
23  * of native devices into the resource model of OCF.  The file is divided into two
24  * sections; first plugin specific entry points are implemented followed by the
25  * implementation of the resource entity handler required by the IoTivity implementation
26  * for each resource.
27  *
28  * NOTE: This file is plumbed ready for dynamic resource additions.  There is a
29  * thread provided to manage the devices.  When a resource is found it is added
30  * to a work queue which is serviced by the plugin process function.  The plugin
31  * process function is a thread safe place for the plugin specific code to call
32  * OIC APIs.
33  */
34
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string>
38 #include <unistd.h>
39 #include <signal.h>
40 #include <pthread.h>
41 #include <fstream>
42 #include <map>
43 #include <iostream>
44 #include "honeywell.h"
45 #include "mpmErrorCode.h"
46 #include "pluginServer.h"
47 #include "rapidjson.h"
48 #include "document.h"
49 #include "stringbuffer.h"
50
51 #include "oic_malloc.h"
52 #include "oic_string.h"
53 #include "honeywellLyric.h"
54 #include "experimental/logger.h"
55 #include "ConcurrentIotivityUtils.h"
56 #include "octypes.h"
57 #include "ocstack.h"
58 #include "ocpayload.h"
59 #include "messageHandler.h"
60
61 #include "honeywellThermostat.h"
62 #include "honeywellHelpers.h"
63
64 #define LOG_TAG "HONEYWELL_RESOURCE"
65
66 #define MANUFACTURER_NAME "HONEYWELL"
67 #define DEVICE_NAME "Honeywell Lyric Translator"
68 #define DEVICE_TYPE "oic.d.thermostat"
69 #define MAX_RESOURCES 3
70 #define MAX_DEVICE_ID_LEN 32
71 #define BM 3
72 #define MAX_CHANGEABLEVALUES_LEN 103
73
74 using namespace OC::Bridging;
75
76 /*******************************************************************************
77  * Pound defines and structure declarations go here
78  ******************************************************************************/
79 typedef struct {
80         int locationId;
81         char deviceIdStr[MAX_DEVICE_ID_LEN];
82         char uniqueId[MPM_MAX_UNIQUE_ID_LEN];
83         double ambientTempF;
84         char changeableValues[MAX_CHANGEABLEVALUES_LEN];
85 } ThermostatDetails;
86
87 /* Resources can come and go based on having a IOT device added or removed from
88  * the physical infrastructure.  Also the Iotivity entity handler does not pass
89  * or present a resource handle in the entity handler callback.  Therefore, in
90  * the plugin specific code an associative structure needs to be kept around in
91  * global space to allow the implementer to make the association between URI and
92  * other attributes corresponding with the IOT device in the data model.
93  *
94  * NOTE: Iotivity guarantees that one can not represent two devices with exactly
95  *       the same URI, so keying off of the URI is a successful strategy.
96  */
97
98 /*******************************************************************************
99  * global data goes here
100  ******************************************************************************/
101
102 std::map<std::string, LyricThermostatSharedPtr> uriToLyricThermostatMap;
103 std::map<std::string, LyricThermostatSharedPtr> addedThermostats;
104
105 HoneywellLyric g_honeywell;
106 Honeywell::ACCESS_TOKEN m_token;
107 bool g_isAuthorized = false;
108 Honeywell::CLIENT_ID_SECRET m_clientId_secret;
109
110 /*******************************************************************************
111  * prototypes go here
112  ******************************************************************************/
113 MPMResult loadAccessToken(const std::string &filename, Honeywell::ACCESS_TOKEN &token);
114
115 OCEntityHandlerResult resourceEntityHandlerCb(OCEntityHandlerFlag,
116                                                  OCEntityHandlerRequest *entityHandlerRequest,
117                                                  void *);
118
119 OCEntityHandlerResult processPutRequest(OCRepPayload * payload, LyricThermostatSharedPtr targetThermostat, const std::string uri);
120
121 OCRepPayload *getPayload(const std::string uri, const THERMOSTAT &data);
122
123 void *accessTokenMonitorThread(void *pointer);
124
125 FILE *honeywellFopen(const char *path, const char *mode)
126 {
127     std::string filename = std::string("lyric_") + path;
128     return fopen(filename.c_str(), mode);
129 }
130
131 MPMPluginCtx *g_pluginCtx = NULL;
132
133 MPMResult pluginCreate(MPMPluginCtx **pluginSpecificCtx)
134 {
135     MPMResult result = MPM_RESULT_OK;
136
137     if (g_pluginCtx != NULL)
138     {
139         OIC_LOG(ERROR, LOG_TAG, "Plugin is already created");
140         return MPM_RESULT_ALREADY_CREATED;
141     }
142
143     /* allocate a context structure for the plugin */
144     MPMPluginCtx *ctx = (MPMPluginCtx *) OICCalloc(1, sizeof(MPMPluginCtx));
145
146     /* initialize the plugin context */
147     if (ctx == NULL)
148     {
149         OIC_LOG(ERROR, LOG_TAG, "Unable to allocate plugin specific context.");
150         return MPM_RESULT_INTERNAL_ERROR;
151     }
152
153     *pluginSpecificCtx = ctx;
154     g_pluginCtx = ctx;
155
156     ctx->device_name = DEVICE_NAME;
157     ctx->resource_type = DEVICE_TYPE;
158     ctx->open = honeywellFopen;
159
160     ifstream tokenFile ("./lyric.cnf");
161
162     if (!tokenFile.is_open())
163     {
164         OIC_LOG(ERROR, LOG_TAG, "error loading lyric.cnf file.");
165         return MPM_RESULT_INTERNAL_ERROR;
166     }
167
168     std::string acode;
169
170     if (!getline (tokenFile, acode))
171     {
172         OIC_LOG(ERROR, LOG_TAG, "Failed to read ./lyric.cnf");
173         tokenFile.close();
174         return MPM_RESULT_INTERNAL_ERROR;
175     }
176
177     std::string str;
178     if (!getline (tokenFile, str))
179     {
180         OIC_LOG(ERROR, LOG_TAG, "Failed to read ./lyric.cnf");
181         tokenFile.close();
182         return MPM_RESULT_INTERNAL_ERROR;
183     }
184     OICStrcpy(m_clientId_secret.honeywell_clientId, HONEYWELL_CLIENT_ID_BUFFSIZE, str.c_str());
185
186     if (!getline (tokenFile, str))
187     {
188         OIC_LOG(ERROR, LOG_TAG, "Failed to read ./lyric.cnf");
189         tokenFile.close();
190         return MPM_RESULT_INTERNAL_ERROR;
191     }
192     OICStrcpy(m_clientId_secret.honeywell_client_secret, HONEYWELL_CLIENT_AND_SECRET_64_BUFFSIZE, str.c_str());
193     tokenFile.close();
194
195     g_honeywell.setClientIdAndSecret(m_clientId_secret);
196
197     result = (MPMResult) g_honeywell.getAccessToken(acode, m_token);
198     if (MPM_RESULT_OK != result)
199     {
200         OIC_LOG_V(ERROR, LOG_TAG, "getAccessToken failed with %d", result);
201         // TODO - what to do in case of failure? free resources? reset auth flag?
202         g_isAuthorized = false;
203         return MPM_RESULT_INTERNAL_ERROR;
204     }
205     else
206     {
207         OIC_LOG(DEBUG, LOG_TAG, "getAccessToken is successful");
208         g_isAuthorized = true;
209         g_honeywell.setAccessToken(m_token);
210     }
211
212     OIC_LOG_V(INFO, LOG_TAG, "Plugin create return value: %d.", result);
213
214     return result;
215 }
216
217 MPMResult pluginStart(MPMPluginCtx *pluginSpecificCtx)
218 {
219     MPMResult result = MPM_RESULT_INTERNAL_ERROR;
220     int error = -1;
221
222     if (pluginSpecificCtx != NULL)
223     {
224         // set global plugin context
225         g_pluginCtx = pluginSpecificCtx;
226
227         /* create house keeping thread */
228         error = pthread_create(&(pluginSpecificCtx->thread_handle), NULL, accessTokenMonitorThread, pluginSpecificCtx);
229         if (error == 0)
230         {
231             pluginSpecificCtx->stay_in_process_loop = true;
232             pluginSpecificCtx->started = true;
233             result = MPM_RESULT_OK;
234         }
235         else
236         {
237             pluginSpecificCtx->stay_in_process_loop = false;
238             pluginSpecificCtx->started = false;
239             OIC_LOG_V(ERROR, LOG_TAG, "Can't create plugin specific thread :[%s]", strerror(error));
240             result = MPM_RESULT_STARTED_FAILED;
241         }
242     }
243
244     OIC_LOG_V(INFO, LOG_TAG, "Plugin start return value: %d.", result);
245
246     return result;
247 }
248
249 MPMResult pluginStop(MPMPluginCtx *pluginSpecificCtx)
250 {
251     MPMResult result = MPM_RESULT_INTERNAL_ERROR;
252
253     if (NULL != pluginSpecificCtx)
254     {
255         result = MPM_RESULT_OK;
256
257         if (pluginSpecificCtx->started == true)
258         {
259             pluginSpecificCtx->stay_in_process_loop = false;
260             pthread_join(pluginSpecificCtx->thread_handle, NULL);
261             pluginSpecificCtx->started = false;
262         }
263     }
264
265     OIC_LOG_V(INFO, LOG_TAG, "Plugin stop's return value:%d", result);
266
267     return result;
268 }
269
270 MPMResult pluginDestroy(MPMPluginCtx *pluginSpecificCtx)
271 {
272     MPMResult result = MPM_RESULT_INTERNAL_ERROR;
273
274     if (pluginSpecificCtx != NULL)
275     {
276
277         if (pluginSpecificCtx->started == true)
278         {
279             pluginStop(pluginSpecificCtx);
280         }
281
282         // freeing the resource allocated in create
283         OICFree(pluginSpecificCtx);
284         pluginSpecificCtx = NULL;
285         result = MPM_RESULT_OK;
286     }
287
288     OIC_LOG_V(INFO, LOG_TAG, "Plugin destroy's return value:%d", result);
289
290     return result;
291 }
292
293 /**
294  * This method checks if the request is for an actuator(oic.if.a) resource or a
295  * sensor(oic.if.s) resource. Sensors allow only GET. Actuators allow GET PUT
296  * POST. Sensors resources end with /current. Actuators have uris ending with
297  * /heater or /cooler.
298  *
299  * @param[in] uri           Resource URI
300  * @param[in] operation     Operation to be checked.
301  */
302 OCEntityHandlerResult checkIfOperationIsAllowed(std::string uri, OCMethod operation)
303 {
304     if (operation == OC_REST_GET)
305     {
306         return OC_EH_OK;
307     }
308     if (operation == OC_REST_DELETE)
309     {
310         return OC_EH_FORBIDDEN;
311     }
312
313     std::string sensor_suffix = "/current";
314
315     // uri is smaller than suffix.. which is weird.. but not a failure in this function.
316     if (uri.length() < sensor_suffix.length())
317     {
318         return OC_EH_OK;
319     }
320
321     // Code here means operation is not a GET. Disallow operation if it is a sensor.
322     if (std::equal(sensor_suffix.rbegin(), sensor_suffix.rend(), uri.rbegin()))
323     {
324         OIC_LOG(INFO, LOG_TAG, "PUT/POST not allowed on sensors");
325         return OC_EH_FORBIDDEN;
326     }
327
328     return OC_EH_OK;
329 }
330
331 /**
332  * This method creates payload for response to GET/PUT/POST request.
333  *
334  * @param[in] uri           Resource URI
335  * @param[in] data          Thermostat detials to be sent in response.
336  */
337 OCRepPayload *getPayload(const std::string uri, const THERMOSTAT &data)
338 {
339     bool result = true;
340     std::string modeString;
341     OCRepPayload *payload = OCRepPayloadCreate();
342     if (NULL == payload)
343     {
344         OIC_LOG(ERROR, LOG_TAG, "Failed to allocate Payload");
345         result = false;
346     }
347
348     OIC_LOG_V(INFO, LOG_TAG,
349           "coolSP: %f, heatSP: %f, ambient: %f, desired: %f",
350           data.coolSetpointF,
351           data.heatSetpointF,
352           data.ambientTempF,
353           computeTargetTemp(data.heatSetpointF, data.coolSetpointF));
354
355     if (result)
356     {
357         result = OCRepPayloadSetUri(payload, uri.c_str());
358         if (false == result)
359         {
360             OIC_LOG(ERROR, LOG_TAG, "OCRepPayloadSetUri failed");
361         }
362     }
363
364     if (result)
365     {
366         result = OCRepPayloadAddResourceType(payload, HONEYWELL_THERMOSTAT_RT);
367         if (false == result)
368         {
369             OIC_LOG(ERROR, LOG_TAG, "OCRepPayloadAddResourceType failed");
370         }
371     }
372
373     if (result)
374     {
375         // high target temp is cool setpoint (hottest it gets before cooling kicks on)
376         result = OCRepPayloadSetPropDouble(payload, REP_NAME_TARGET_TEMP_HIGH, data.coolSetpointF);
377         if (false == result)
378         {
379             OIC_LOG(ERROR, LOG_TAG, "OCRepPayloadSetPropDouble REP_NAME_TARGET_TEMP_HIGH failed");
380         }
381     }
382     if (result)
383     {
384         // low target temp is heat setpoint (coolest it gets before heating kicks on)
385         result = OCRepPayloadSetPropDouble(payload, REP_NAME_TARGET_TEMP_LOW, data.heatSetpointF);
386         if (false == result)
387         {
388             OIC_LOG(ERROR, LOG_TAG, "OCRepPayloadSetPropDouble REP_NAME_TARGET_TEMP_LOW failed");
389         }
390     }
391     if (result)
392     {
393         // Return ambient/indoor temperature (if available) in optional property
394         result = OCRepPayloadSetPropDouble(payload, REP_NAME_INDOOR_TEMP, data.ambientTempF);
395         if (false == result)
396         {
397             OIC_LOG(ERROR, LOG_TAG, "OCRepPayloadSetPropDouble REP_NAME_INDOOR_TEMP failed");
398         }
399         // Return desired/target temperature for both gets and sets
400         double temperature = computeTargetTemp(data.heatSetpointF, data.coolSetpointF);
401         OIC_LOG_V(INFO, LOG_TAG, "Setting temperature in payload as %f", temperature);
402
403         result = OCRepPayloadSetPropDouble(
404                 payload, REP_NAME_TEMPERATURE, temperature);
405         if (false == result)
406         {
407             OIC_LOG(ERROR, LOG_TAG, "OCRepPayloadSetPropDouble REP_NAME_TEMPERATURE failed");
408         }
409     }
410     if (result)
411     {
412         if (data.hvacMode == HVAC_COOL)
413         {
414             modeString = REP_VALUE_COOL;
415         }
416         else if (data.hvacMode == HVAC_HEAT)
417         {
418             modeString = REP_VALUE_HEAT;
419         }
420         else
421         {
422             modeString = REP_VALUE_OFF;
423         }
424
425         result = OCRepPayloadSetPropString(payload, REP_NAME_MODE, modeString.c_str());
426         if (false == result)
427         {
428             OIC_LOG(ERROR, LOG_TAG, "OCRepPayloadSetPropString REP_NAME_MODE failed");
429         }
430     }
431
432     if (false == result)
433     {
434         OIC_LOG(ERROR, LOG_TAG, "Failed to set payload value(s)");
435
436         // if we are in an error state, we need to free the payload.
437         if (NULL != payload)
438         {
439             OCRepPayloadDestroy(payload);
440             payload = NULL;
441         }
442     }
443
444     return payload;
445 }
446
447 OCEntityHandlerResult
448 resourceEntityHandlerCb(OCEntityHandlerFlag, OCEntityHandlerRequest *request, void *)
449 {
450     OCEntityHandlerResult result = OC_EH_OK;
451
452     try
453     {
454         std::string resourceUri;
455         std::size_t found = 0;
456         ConcurrentIotivityUtils::getUriFromHandle(request->resource, resourceUri);
457         found = resourceUri.find_last_of("/");
458         std::string uri = resourceUri.substr(0, found);  //device uri
459         LyricThermostatSharedPtr targetThermostat = addedThermostats[uri];
460         THERMOSTAT data;
461
462         result = checkIfOperationIsAllowed(resourceUri, request->method);
463         if (result != OC_EH_OK)
464         {
465            OIC_LOG_V(INFO, LOG_TAG, "Operation not allowed on %s", resourceUri.c_str());
466            return result;
467         }
468
469         switch (request->method)
470         {
471             case OC_REST_GET:
472                 // Empty GET case as actual request will be processed after the switch case.
473                 OIC_LOG_V(INFO, LOG_TAG, "GET on %s", resourceUri.c_str());
474                 break;
475
476             case OC_REST_PUT:
477             case OC_REST_POST:
478
479                 OIC_LOG_V(INFO, LOG_TAG, "UPDATE on %s", resourceUri.c_str());
480                 result = processPutRequest((OCRepPayload*) request->payload, targetThermostat, resourceUri);
481                 if (result != OC_EH_OK)
482                 {
483                     OIC_LOG_V(ERROR, LOG_TAG, "process_put_request returned Error = %d", result);
484                 }
485
486                 break;
487
488             default:
489                 OIC_LOG_V(INFO, LOG_TAG,"Unsupported method (%d) received", request->method);
490                 ConcurrentIotivityUtils::respondToRequestWithError(request, "Unsupported method received",
491                     OC_EH_METHOD_NOT_ALLOWED);
492                 return OC_EH_OK;
493         }
494
495         targetThermostat->get(data);
496         OCRepPayload *payload = getPayload(uri.c_str(), data);
497
498         ConcurrentIotivityUtils::respondToRequest(request, payload, result);
499         OCRepPayloadDestroy(payload);
500     }
501
502     catch (std::string errorMessage)
503     {
504         ConcurrentIotivityUtils::respondToRequestWithError(request, errorMessage.c_str(), OC_EH_ERROR);
505         return OC_EH_OK;
506     }
507
508     return OC_EH_OK;
509 }
510
511
512 OCEntityHandlerResult processPutRequest(OCRepPayload * payload, LyricThermostatSharedPtr targetThermostat, const std::string uri)
513 {
514     OCEntityHandlerResult ehResult = OC_EH_OK;
515     OIC_LOG_V(INFO, LOG_TAG, "Put request for %s", uri.c_str());
516     THERMOSTAT localData;
517     int result = MPM_RESULT_OK;
518     if (!payload)
519     {
520         OIC_LOG(ERROR, LOG_TAG, "Entity handler request payload is empty while processing PUT request");
521         return OC_EH_ERROR;
522     }
523
524     // Get pointer to query
525     if (!OCRepPayloadGetPropDouble(payload, REP_NAME_TEMPERATURE, &(localData.targetTempF)))
526     {
527         OIC_LOG(ERROR, LOG_TAG, "REP_NAME_TEMPERATURE not found in payload (data).");
528         return OC_EH_ERROR;
529     }
530
531     OIC_LOG_V(INFO, LOG_TAG, "localData.targetTempF %f", localData.targetTempF);
532     // compute cool and hot setpoints based on desired temperature
533     // NOTE: low = heatSetpoint, high = coolSetpoint (they are the temperatures those
534     //       modes try to achieve.)
535     computeSetpoints(localData.targetTempF, localData.heatSetpointF, localData.coolSetpointF);
536
537     OIC_LOG_V(INFO, LOG_TAG, "localData.heatSetpointF %f", localData.heatSetpointF);
538     OIC_LOG_V(INFO, LOG_TAG, "localData.coolSetpointF %f", localData.coolSetpointF);
539
540     result = g_honeywell.setTemperature(targetThermostat, localData, uri);
541
542     OIC_LOG_V(INFO, LOG_TAG, "setTemperature returned result = %d", result);
543     if (result != MPM_RESULT_OK)
544     {
545         throw "Error setting temperature for PUT request";
546     }
547
548     return ehResult;
549 }
550
551 MPMResult pluginScan(MPMPluginCtx *, MPMPipeMessage *)
552 {
553     OIC_LOG(INFO, LOG_TAG, "Inside plugin_scan");
554     std::vector<LyricThermostatSharedPtr> thermostatsScanned;
555
556     MPMResult result = MPM_RESULT_INTERNAL_ERROR;
557
558     result = (MPMResult) g_honeywell.getThermostats(thermostatsScanned);
559     if (MPM_RESULT_OK == result)
560     {
561         for (uint32_t i = 0; i < thermostatsScanned.size(); ++i)
562         {
563             LyricThermostatSharedPtr thermostat = thermostatsScanned[i];
564
565             std::string uri = "/honeywell/" + thermostat->getDeviceUniqueId();
566             if(addedThermostats.find(uri) != addedThermostats.end())
567             {
568                 OIC_LOG_V(INFO, LOG_TAG, "Already Added %s. Ignoring", uri.c_str());
569                 continue;
570             }
571
572             uriToLyricThermostatMap[uri] = thermostat;
573
574             MPMSendResponse(uri.c_str(), uri.size(), MPM_SCAN);
575         }
576     }
577     else
578     {
579         OIC_LOG_V(ERROR, LOG_TAG, "getThermostats failed with %d", result);
580     }
581     OIC_LOG(INFO, LOG_TAG, "Leaving plugin specific thread handler.");
582     return result;
583 }
584
585 bool isSecureEnvironmentSet()
586 {
587     char *non_secure_env = getenv("NONSECURE");
588
589     if (non_secure_env != NULL && (strcmp(non_secure_env, "true") == 0))
590     {
591         OIC_LOG(INFO, LOG_TAG, "Creating NON SECURE resources");
592         return false;
593     }
594     OIC_LOG(INFO, LOG_TAG, "Creating SECURE resources");
595     return true;
596 }
597
598 void createPayloadForMetadata(MPMResourceList **list , const std::string &uri, const std::string &interface)
599 {
600     MPMResourceList *tempPtr;
601     tempPtr = (MPMResourceList *) OICCalloc(1, sizeof(MPMResourceList));
602
603     if (tempPtr == NULL)
604     {
605         OIC_LOG(ERROR, LOG_TAG, "Memory Allocation failed");
606         return;
607     }
608     OICStrcpy(tempPtr->rt, MPM_MAX_LENGTH_64, HONEYWELL_THERMOSTAT_RT);
609     OICStrcpy(tempPtr->href, MPM_MAX_URI_LEN, uri.c_str());
610     OICStrcpy(tempPtr->interfaces, MPM_MAX_LENGTH_64, interface.c_str());
611     tempPtr->bitmap = BM;
612     tempPtr->next = *list;
613     *list  = tempPtr;
614 }
615
616 void updatePluginSpecificData(THERMOSTAT thermostat, ThermostatDetails *thermostatDetails)
617 {
618     OICStrcpy(thermostatDetails->deviceIdStr, MAX_DEVICE_ID_LEN, thermostat.devInfo.deviceIdStr.c_str());
619     OICStrcpy(thermostatDetails->uniqueId, MPM_MAX_UNIQUE_ID_LEN, thermostat.devInfo.uniqueId.c_str());
620     thermostatDetails->locationId = thermostat.devInfo.locationId;
621     thermostatDetails->ambientTempF = thermostat.ambientTempF;
622 }
623
624 MPMResult pluginAdd(MPMPluginCtx *, MPMPipeMessage * message)
625 {
626     uint8_t resourceProperties = (OC_OBSERVABLE | OC_DISCOVERABLE);
627
628     if (isSecureEnvironmentSet())
629     {
630         resourceProperties |= OC_SECURE;
631     }
632
633     std::string uri = reinterpret_cast<const char *>(message->payload);
634     if(addedThermostats.find(uri) != addedThermostats.end())
635     {
636         OIC_LOG_V(ERROR, LOG_TAG, "%s already added", uri.c_str());
637         return MPM_RESULT_ALREADY_CREATED;
638     }
639     if(uriToLyricThermostatMap.find(uri) == uriToLyricThermostatMap.end())
640     {
641         OIC_LOG_V(ERROR, LOG_TAG, "%s was NOT discovered in a scan", uri.c_str());
642         return MPM_RESULT_INTERNAL_ERROR;
643     }
644
645     MPMResourceList *list = NULL;
646
647     /* Heater Resource */
648     std::string honeywellDeviceActuatorHeaterUri = uri + "/heater";
649     ConcurrentIotivityUtils::queueCreateResource(honeywellDeviceActuatorHeaterUri, HONEYWELL_THERMOSTAT_RT, HONEYWELL_THERMOSTAT_ACTUATOR_IF, resourceEntityHandlerCb, NULL, resourceProperties);
650     createPayloadForMetadata(&list, honeywellDeviceActuatorHeaterUri.c_str(), HONEYWELL_THERMOSTAT_ACTUATOR_IF);
651
652     /* Cooler Resource */
653     std::string honeywellDeviceActuatorCoolerUri = uri + "/cooler";
654     ConcurrentIotivityUtils::queueCreateResource(honeywellDeviceActuatorCoolerUri, HONEYWELL_THERMOSTAT_RT, HONEYWELL_THERMOSTAT_ACTUATOR_IF, resourceEntityHandlerCb, NULL, resourceProperties);
655     createPayloadForMetadata(&list, honeywellDeviceActuatorCoolerUri.c_str(), HONEYWELL_THERMOSTAT_ACTUATOR_IF);
656
657     /* Sensor Resource */
658     std::string honeywellDeviceSensorUri = uri + "/current";
659     ConcurrentIotivityUtils::queueCreateResource(honeywellDeviceSensorUri, HONEYWELL_THERMOSTAT_RT, HONEYWELL_THERMOSTAT_SENSOR_IF, resourceEntityHandlerCb, NULL, resourceProperties);
660     createPayloadForMetadata(&list, honeywellDeviceSensorUri.c_str(), HONEYWELL_THERMOSTAT_SENSOR_IF);
661
662     addedThermostats[uri] = uriToLyricThermostatMap[uri];
663
664     uint8_t * buff = (uint8_t *) OICCalloc(1, MPM_MAX_METADATA_LEN);
665     ThermostatDetails thermostatDetails;
666     MPMDeviceSpecificData deviceConfiguration;
667     THERMOSTAT thermostat;
668     std::string changeableValues;
669
670     addedThermostats[uri]->get(thermostat);
671     changeableValues = addedThermostats[uri]->getChangeableValues();
672     memset(&thermostatDetails, 0, sizeof(ThermostatDetails));
673     memset(&deviceConfiguration, 0, sizeof(MPMDeviceSpecificData));
674
675     OICStrcpy(thermostatDetails.changeableValues, MAX_CHANGEABLEVALUES_LEN, changeableValues.c_str());
676     updatePluginSpecificData(thermostat, &thermostatDetails);
677     OICStrcpy(deviceConfiguration.devName, MPM_MAX_LENGTH_64, DEVICE_NAME);
678     OICStrcpy(deviceConfiguration.devType, MPM_MAX_LENGTH_64, DEVICE_TYPE);
679     OICStrcpy(deviceConfiguration.manufacturerName, MPM_MAX_LENGTH_256, MANUFACTURER_NAME);
680     MPMFormMetaData(list, &deviceConfiguration, buff, MPM_MAX_METADATA_LEN, (void *)&thermostatDetails, sizeof(ThermostatDetails));
681
682     MPMAddResponse addResponse;
683     memset(&addResponse, 0, sizeof(MPMAddResponse));
684     OICStrcpy(addResponse.uri, MPM_MAX_URI_LEN, uri.c_str());
685     memcpy(addResponse.metadata, buff, MPM_MAX_METADATA_LEN);
686     MPMSendResponse(&addResponse, sizeof(MPMAddResponse), MPM_ADD);
687     OICFree(buff);
688     return MPM_RESULT_OK;
689
690 }
691
692 MPMResult pluginRemove(MPMPluginCtx *, MPMPipeMessage * message)
693 {
694     std::string uri = reinterpret_cast<const char *>(message->payload);
695
696     /* Heater Resource */
697     std::string honeywellDeviceActuatorHeaterUri = uri + "/heater";
698     ConcurrentIotivityUtils::queueDeleteResource(honeywellDeviceActuatorHeaterUri);
699
700     /* Cooler Resource */
701     std::string honeywellDeviceActuatorCoolerUri = uri + "/cooler";
702     ConcurrentIotivityUtils::queueDeleteResource(honeywellDeviceActuatorCoolerUri);
703
704     /* Sensor Resource */
705     std::string honeywellDeviceSensorUri = uri + "/current";
706     ConcurrentIotivityUtils::queueDeleteResource(honeywellDeviceSensorUri);
707
708     addedThermostats.erase(uri);
709     uriToLyricThermostatMap.erase(uri);
710
711     MPMSendResponse(uri.c_str(), uri.size(), MPM_REMOVE);
712
713     return MPM_RESULT_OK;
714 }
715
716 MPMResult pluginReconnect(MPMPluginCtx *, MPMPipeMessage * message)
717 {
718     MPMResourceList *list = NULL, *temp = NULL;
719     THERMOSTAT thermostat;
720     std::vector<LyricThermostatSharedPtr> thermostatsReconnected;
721     void *details = NULL;
722     MPMResult result = MPM_RESULT_INTERNAL_ERROR;
723     uint8_t resourceProperties = (OC_OBSERVABLE | OC_DISCOVERABLE);
724     std::shared_ptr<HoneywellThermostat> sharedThermostat;
725     std::string thermostatMode;
726     std::string uri;
727
728     if(message->payloadSize <= 0 && message->payload == NULL)
729     {
730         OIC_LOG(ERROR, LOG_TAG, "No payload received, failed to reconnect");
731         return MPM_RESULT_INTERNAL_ERROR;
732     }
733
734     MPMParseMetaData(message->payload, MPM_MAX_METADATA_LEN, &list, &details);
735
736     ThermostatDetails *thermostatDetails = (ThermostatDetails *)details;
737     HoneywellThermostat honeywellThermostat;
738
739     thermostat.devInfo.locationId = thermostatDetails->locationId;
740     thermostat.devInfo.deviceIdStr.assign(thermostatDetails->deviceIdStr);
741     thermostat.devInfo.uniqueId.assign(thermostatDetails->uniqueId);
742     thermostat.ambientTempF = thermostatDetails->ambientTempF;
743     honeywellThermostat.setDeviceUniqueId(thermostat.devInfo.uniqueId.c_str());
744
745     honeywellThermostat.setChangeableValues(thermostatDetails->changeableValues);
746
747     rapidjson:: Document values;
748     values.SetObject();
749     if (values.Parse(thermostatDetails->changeableValues).HasParseError())
750     {
751         OIC_LOG(ERROR, LOG_TAG,"Parse error in set changeableValues");
752         result = MPM_RESULT_JSON_ERROR;
753         goto CLEANUP;
754     }
755
756     if(values.HasMember(JSON_MODE))
757     {
758         thermostatMode = values[JSON_MODE].GetString();
759     }
760
761     if(values.HasMember(HONEYWELL_HEAT_SETPOINT))
762     {
763        thermostat.heatSetpointF = values[HONEYWELL_HEAT_SETPOINT].GetDouble();
764     }
765
766     if(values.HasMember(HONEYWELL_COOL_SETPOINT))
767     {
768        thermostat.coolSetpointF = values[HONEYWELL_COOL_SETPOINT].GetDouble();
769     }
770
771     if (0 == strncmp(thermostatMode.c_str(), THERMOSTAT_MODE_COOL, sizeof(THERMOSTAT_MODE_COOL)))
772     {
773         thermostat.hvacMode = HVAC_COOL;
774     }
775     else if (0 == strncmp(thermostatMode.c_str(), THERMOSTAT_MODE_HEAT, sizeof(THERMOSTAT_MODE_HEAT)))
776     {
777         thermostat.hvacMode = HVAC_HEAT;
778     }
779     else
780     {
781         thermostat.hvacMode = HVAC_OFF;
782     }
783
784     thermostat.targetTempF = computeTargetTemp(thermostat.heatSetpointF, thermostat.coolSetpointF);
785     dump_details(thermostat, "thermostatData");
786     honeywellThermostat.set(thermostat);
787
788     sharedThermostat = std::make_shared<HoneywellThermostat>(honeywellThermostat);
789
790     uri = "/honeywell/" + sharedThermostat->getDeviceUniqueId();
791     if(uriToLyricThermostatMap.find(uri) != uriToLyricThermostatMap.end())
792     {
793         OIC_LOG_V(INFO, LOG_TAG, "Already found %s. Ignoring", uri.c_str());
794     }
795     else
796     {
797         OIC_LOG_V(INFO, LOG_TAG, "Adding %s to uriToLyricThermostatMap", uri.c_str());
798         uriToLyricThermostatMap[uri] = sharedThermostat;
799     }
800
801     if(addedThermostats.find(uri) != addedThermostats.end())
802     {
803         OIC_LOG_V(ERROR, LOG_TAG, "%s already added", uri.c_str());
804         result = MPM_RESULT_ALREADY_CREATED;
805         goto CLEANUP;
806     }
807     if(uriToLyricThermostatMap.find(uri) == uriToLyricThermostatMap.end())
808     {
809         result = MPM_RESULT_INTERNAL_ERROR;
810         goto CLEANUP;
811     }
812
813     if (isSecureEnvironmentSet())
814     {
815         resourceProperties |= OC_SECURE;
816     }
817
818     while(list)
819     {
820         temp = list;
821         std::string resourceUri(list->href);
822         OIC_LOG_V(INFO, LOG_TAG, "resource uri = %s", resourceUri.c_str());
823         ConcurrentIotivityUtils::queueCreateResource(resourceUri, list->rt, list->interfaces, resourceEntityHandlerCb, NULL, resourceProperties);
824         list = list->next;
825         OICFree(temp);
826     }
827
828     addedThermostats[uri] = uriToLyricThermostatMap[uri];
829     result = MPM_RESULT_OK;
830
831     CLEANUP:
832     if (thermostatDetails !=NULL)
833     {
834         OICFree(thermostatDetails);
835         details = NULL;
836     }
837     return result;
838 }
839
840 /**
841  * The Lyric Access Token process loop.
842  * - manages honeywell device resources in response to changes in
843  *   authentication status
844  * - performs re-authentication with Honeywell servers to keep a fresh
845  *   access token (Lyric access tokens only last for 10 minutes)
846  *
847  * @param[in] pointer     plugin specific context
848  */
849 void *accessTokenMonitorThread(void *pointer)
850 {
851     OIC_LOG(DEBUG, LOG_TAG, "Entered accessTokenMonitorThread");
852     MPMPluginCtx *ctx = (MPMPluginCtx *) pointer;
853     int reauthCountdown = HW_QUERY_INTERVAL_SECONDS; // period to wait before attempting re-auth
854     bool lastAuthorized = false;
855     std::string emptycode;
856     MPMResult result = MPM_RESULT_INTERNAL_ERROR;
857
858     if (ctx != NULL)
859     {
860         while (ctx->stay_in_process_loop)
861         {
862             // check if there has been a change in authorization status
863             if (lastAuthorized != g_isAuthorized)
864             {
865                 // make sure we don't get into an unproductive cycle
866                 lastAuthorized = g_isAuthorized;
867             }
868
869             // Only do Lyric re-auth logic if we are already authenticated.
870             if (lastAuthorized && (reauthCountdown <= 0))
871             {
872                 result = (MPMResult) g_honeywell.getAccessToken(emptycode, m_token);
873                 if (MPM_RESULT_OK != result)
874                 {
875                     OIC_LOG_V(ERROR, LOG_TAG, "getAccessToken failed with %d", result);
876                     // TODO - what to do in case of failure? free resources? reset auth flag?
877                     g_isAuthorized = false;
878                 }
879                 else
880                 {
881                     // reset the counter
882                     reauthCountdown = (HW_AUTH_LOOP_MINUTES * 60);
883                     OIC_LOG(DEBUG, LOG_TAG, "getAccessToken is successful");
884                     g_isAuthorized = true;
885                     g_honeywell.setAccessToken(m_token);
886                 }
887             }
888             else
889             {
890                 // not time to refresh token yet, just decrement
891                 reauthCountdown--;
892             }
893             sleep(MPM_THREAD_PROCESS_SLEEPTIME);
894         }
895         OIC_LOG(INFO, LOG_TAG,"Leaving LYRIC monitor thread");
896     }
897     pthread_exit(NULL);
898 }
899