Initial plugin "Nest Plugin" for bridging project.
authorvijendrx <vijendrax.kumar@intel.com>
Thu, 23 Feb 2017 11:33:18 +0000 (17:03 +0530)
committerTodd Malsbary <todd.malsbary@intel.com>
Fri, 24 Feb 2017 00:18:17 +0000 (00:18 +0000)
Change-Id: I79bc33cf7ddf7a6b29c870f0922c5db3d970f6b8
Signed-off-by: Joseph Morrow <joseph.l.morrow@intel.com>
Signed-off-by: vijendrx <vijendrax.kumar@intel.com>
Signed-off-by: Mandeep Shetty <mandeep.shetty@intel.com>
Signed-off-by: vijendrx <vijendrax.kumar@intel.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/16863
Tested-by: jenkins-iotivity <jenkins@iotivity.org>
Reviewed-by: Todd Malsbary <todd.malsbary@intel.com>
bridging/SConscript
bridging/include/messageHandler.h
bridging/plugins/nest_plugin/README [new file with mode: 0644]
bridging/plugins/nest_plugin/SConscript [new file with mode: 0644]
bridging/plugins/nest_plugin/nest_objects/nest.cpp [new file with mode: 0644]
bridging/plugins/nest_plugin/nest_objects/nest.h [new file with mode: 0644]
bridging/plugins/nest_plugin/nest_objects/nest_defs.h [new file with mode: 0644]
bridging/plugins/nest_plugin/nest_objects/nest_thermostat.cpp [new file with mode: 0644]
bridging/plugins/nest_plugin/nest_objects/nest_thermostat.h [new file with mode: 0644]
bridging/plugins/nest_plugin/nest_resource.cpp [new file with mode: 0644]

index 18056b1..0fd62fc 100644 (file)
@@ -44,6 +44,6 @@ if target_os not in ['android', 'arduino', 'darwin', 'ios', 'tizen', 'msys_nt',
 
 #    SConscript(os.path.join('plugins', 'hue_plugin', 'SConscript'))
 
-#    SConscript(os.path.join('plugins', 'nest_plugin', 'SConscript'))
+    SConscript(os.path.join('plugins', 'nest_plugin', 'SConscript'))
 
     SConscript(os.path.join('plugins', 'lyric_plugin', 'SConscript'))
index 65a7858..debc161 100644 (file)
@@ -40,6 +40,7 @@ extern "C" {
 #define MPM_MAX_LENGTH_32     32
 #define MPM_MAX_LENGTH_64     64
 #define MPM_MAX_LENGTH_256    256
+#define MPM_MAX_LENGTH_1024   1024
 #define MPM_MAX_UNIQUE_ID_LEN 128
 #define MPM_MAX_METADATA_LEN  3000
 
diff --git a/bridging/plugins/nest_plugin/README b/bridging/plugins/nest_plugin/README
new file mode 100644 (file)
index 0000000..0fa3bee
--- /dev/null
@@ -0,0 +1,104 @@
+General:
+To use this plugin, a config file "nest.cnf" needs to be populated and placed in
+the same directory as the mpm client (i.e. "mpm_sample_client")
+generated by IoTivity's build system.
+
+Note: A Nest Thermostat with a non-proxied internet connection is required to use
+this plugin. This plugin will NOT provision your thermostat for you. You will need
+to perform the set-up steps through the Nest app (this can be found in either the
+iOS or Android app store) and your own Nest user account.
+
+What should this file look like?
+
+    See sample "nest.cnf.sample" file with contents as shown
+    below(without spaces, tabs or quotes):
+
+    "
+
+        A5TA4W7L
+        d.L6y17o9NXdI8D4bISIwS3m7N5B0Em4zGEi8cIsJQWhZPqGxjKOLreuunOUe4utN6oENhV22DeQmkHlNoZtFhJdBvwepEa1P31msLiL8MmXt4X5M1yLX6D03uJXrjpNNGqKTBynCaAHH42Gtv
+        d9dd021b-8ca4-4c21-ad81-3b6f3c845cf7
+        O1BGktjJergHztgXStzLdNb3G
+
+    "
+
+What is contained in this file?
+
+    It consists of four values:
+    1. First line of this file represents a PIN CODE value.
+    2. Second line represents the accessToken.
+    3. Third line represents API KEY/CLIENT ID value.
+    4. Fourth line represents the CLIENT SECRET value.
+
+What is PIN CODE?
+
+    It is a randomly generated 8 digit alphanumeric value
+    which can be used to obtain a new access token from
+    nest server. Please note that each pin code is valid
+    only for one-time usage. The procedure on how to use
+    pin code to obtain a new access token is discussed
+    below under the heading "Where can I obtain the token
+     as shown in the above example".
+
+Where to put this file?
+    The placement of the nest.cnf file should be where
+    your mpm client is also:
+
+    <iotivity>/out/<TARGET_OS>/<TARGET_ARCH>/<BUILD>/bridging/src/mpm_client
+
+    Example: <iotivity>/out/linux/x86_64/release/bridging/src/mpm_client
+        Depending on your build configuration, the path may
+        look mildly different.
+
+What is this key or token?
+
+    The Nest mapping requires usage of the Nest
+    Developer's API. The usage of this API requires
+    that every user/developer has their own HTTP API
+    Token.This HTTP API Token allows the
+    libnestplugin.so to perform actions within your
+    Nest cloud account on your behalf.
+
+
+Where can I obtain API KEY/CLIENT IB and SECRET as shown in the above examples?
+
+    Please go through the instructions on the below link to
+    generate the API KEY/CLIENT ID and  SECRET:
+
+    https://developers.nest.com/documentation/cloud/how-to-auth/
+
+
+Where can I obtain the token as shown in the above example?
+
+    Use the same username and password you used to set
+    up your Nest thermostat at the following address to
+    obtain a token and click on "Accept":
+
+    https://home.nest.com/login/oauth2?client_id=<nest_client_id>&state=STATE
+
+    (where nest_client_id is read from nest.cnf file itself appearing in the fourth
+     line in the file)
+
+    Copy the 8 letter pincode thus generates and
+    paste it in the nest.cnf file and then start
+    the plugin. The access token will be obtained
+    from the cloud and and stored in nest.cnf in
+    the third line of the file. Please note that
+    the pin code is for one time use and a new one
+    has to be generated everytime access token expires.
+    The lifetime of access token is 365 days. Do not
+    share this token with anyone. This token can give
+    someone complete access to your Nest account. Be careful.
+
+How can I start this plugin?
+
+    Use either binary executable 'mpm_sample_client' to load
+    and control this plugin.
+
+    More information on these clients can be found at
+    <iotivity>/bridging/src/mpm_client/README.
+
+For proper documentation of this plugin, Mini Plugin
+Manager, the client applications, and other plugins, please
+perform a query on the "Bridging" or "Bridging Project" at
+wiki.iotivity.org.
diff --git a/bridging/plugins/nest_plugin/SConscript b/bridging/plugins/nest_plugin/SConscript
new file mode 100644 (file)
index 0000000..92a2cdd
--- /dev/null
@@ -0,0 +1,99 @@
+#******************************************************************
+#
+# Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+##
+# Nest Plugin build script
+##
+
+import os
+import os.path
+
+Import('env')
+
+target_os = env.get('TARGET_OS')
+src_dir = env.get('SRC_DIR')
+bridging_path = os.path.join(src_dir, 'bridging')
+
+nest_env = env.Clone()
+
+print "Reading Nest Plugin script"
+
+######################################################################
+# Build flags
+######################################################################
+
+def maskFlags(flags):
+    flags = [flags.replace('-Wl,--no-undefined', '' ) for flags in flags]
+    return flags
+
+nest_env.PrependUnique(CPPPATH = [ os.path.join(src_dir, 'resource', 'c_common', 'oic_malloc', 'include'),
+                              os.path.join(src_dir, 'resource', 'c_common', 'oic_string', 'include'),
+                              os.path.join(src_dir, 'resource', 'c_common'),
+                              os.path.join(src_dir, 'resource', 'oc_logger', 'include'),
+                              os.path.join(src_dir, 'resource', 'csdk', 'logger', 'include'),
+                              os.path.join(src_dir, 'resource', 'csdk', 'include'),
+                              os.path.join(src_dir, 'resource', 'csdk', 'stack', 'include'),
+                              os.path.join(src_dir, 'resource', 'include'),
+                              os.path.join(src_dir, 'extlibs', 'cjson'),
+                              os.path.join(src_dir, 'extlibs', 'tinycbor', 'src'),
+                              os.path.join(src_dir, 'extlibs', 'rapidjson', 'rapidjson', 'include', 'rapidjson')
+                              ])
+nest_env.AppendUnique(CPPPATH = [ os.path.join(bridging_path, 'include'),
+                             os.path.join(bridging_path, 'plugins', 'nest_plugin', 'nest_objects')
+                             ])
+
+if target_os not in ['arduino', 'windows']:
+    nest_env.AppendUnique(CPPDEFINES = ['WITH_POSIX'])
+
+if target_os in ['darwin','ios']:
+    nest_env.AppendUnique(CPPDEFINES = ['_DARWIN_C_SOURCE'])
+
+if 'g++' in nest_env.get('CXX'):
+    nest_env.AppendUnique(CXXFLAGS = ['-std=c++0x', '-Wall', '-Wextra', '-Werror'])
+
+nest_env.AppendUnique(RPATH = [nest_env.get('BUILD_DIR')])
+nest_env.AppendUnique(LIBPATH = [nest_env.get('BUILD_DIR')])
+
+if nest_env.get('LOGGING'):
+    nest_env.AppendUnique(CPPDEFINES = ['TB_LOG'])
+
+nest_env['LINKFLAGS'] = maskFlags(env['LINKFLAGS'])
+nest_env.AppendUnique(LINKFLAGS = ['-Wl,--allow-shlib-undefined'])
+nest_env.AppendUnique(LINKFLAGS = ['-Wl,--whole-archive', nest_env.get('BUILD_DIR') +'libmpmcommon.a','-Wl,-no-whole-archive'])
+
+nest_env.PrependUnique(LIBS = ['m',
+                               'octbstack',
+                               'ocsrm',
+                               'connectivity_abstraction',
+                               'coap',
+                               'curl' ])
+
+#####################################################################
+# Source files and Target(s)
+######################################################################
+nest_src = [
+         os.path.join(bridging_path, 'plugins', 'nest_plugin', 'nest_resource.cpp'),
+         os.path.join(bridging_path, 'plugins', 'nest_plugin', 'nest_objects', 'nest.cpp'),
+         os.path.join(bridging_path, 'plugins', 'nest_plugin', 'nest_objects', 'nest_thermostat.cpp'),
+         ]
+
+nest_env.AppendUnique(NEST_SRC = nest_src)
+nestlib = nest_env.SharedLibrary('nestplugin', nest_env.get('NEST_SRC'))
+nest_env.InstallTarget(nestlib, 'nestplugin')
+nest_env.UserInstallTargetLib(nestlib, 'nestplugin')
diff --git a/bridging/plugins/nest_plugin/nest_objects/nest.cpp b/bridging/plugins/nest_plugin/nest_objects/nest.cpp
new file mode 100644 (file)
index 0000000..be945bd
--- /dev/null
@@ -0,0 +1,352 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+#include <time.h>
+#include <stdio.h>
+#include <iostream>
+#include "nest.h"
+#include "curlClient.h"
+#include "nest_defs.h"
+#include "rapidjson.h"
+#include "document.h"
+#include "stringbuffer.h"
+#include "writer.h"
+#include "JsonHelper.h"
+#include "logger.h"
+
+using namespace rapidjson;
+using namespace OC::Bridging;
+
+#define TAG "NEST"
+
+Nest::Nest(const ACCESS_TOKEN &accessToken) : m_accessToken(accessToken), m_isAuthorized(false)
+{
+    m_metaInfo.awayMode = eAWUndefined;
+}
+
+Nest::Nest() : m_isAuthorized(false) {}
+
+void Nest::setAccessToken(const ACCESS_TOKEN &token)
+{
+    m_accessToken = token;
+}
+
+Nest::AWAY_MODE Nest::getAwayMode(const char *value) const
+{
+    if (!strncmp(value, NEST_HOME_TAG, strlen(NEST_HOME_TAG)))
+    {
+        return eAWHome;
+    }
+    else if (!strncmp(value, NEST_AWAY_TAG, strlen(NEST_AWAY_TAG)))
+    {
+        return eAWAway;
+    }
+
+    return eAWUndefined;
+}
+
+std::string Nest::getTok()
+{
+    return m_accessToken.accessToken;
+}
+
+bool Nest::isAuthorized()
+{
+    if (!strlen(m_accessToken.accessToken))
+    {
+        OIC_LOG(ERROR, TAG, "Length of access token is zero");
+        return false;
+    }
+
+    std::string uri(NEST_BASE_URL);
+    uri += NEST_STRUCTURE_AUTH_STR + std::string(m_accessToken.accessToken);
+
+    OIC_LOG_V(INFO, TAG, "uri: %s", uri.c_str());
+
+    CurlClient cc = CurlClient(CurlClient::CurlMethod::GET, uri)
+                    .addRequestHeader(CURL_HEADER_ACCEPT_JSON)
+                    .addRequestHeader(CURL_CONTENT_TYPE_JSON);
+
+    int curlCode = cc.send();
+    std::string response = cc.getResponseBody();
+
+    OIC_LOG_V(DEBUG, TAG, "The curl response string is: %s", response.c_str());
+
+    if (curlCode != MPM_RESULT_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "Curl GET Request operation failed. Error code %d", curlCode);
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    if (MPM_RESULT_OK != parseStructureJsonResponse(response, m_metaInfo))
+    {
+        OIC_LOG(ERROR, TAG, "Parsing Json response failed on authorization");
+        return false;
+    }
+
+    return true;
+}
+
+MPMResult Nest::getAccessToken(std::string &authorizationCode, ACCESS_TOKEN &accessToken,
+                               std::string &nest_client_id, std::string &nest_client_secret)
+{
+    MPMResult result = MPM_RESULT_OK;
+
+    std::string uri(ACCESS_TOKEN_URL);
+
+    uri += NEST_CLIENT_ID_STR + nest_client_id;
+    uri += NEST_CODE_STR + authorizationCode;
+    uri += NEST_CLIENT_SECRET_STR + nest_client_secret;
+    uri += NEST_AUTH_CODE_STR;
+
+    CurlClient cc = CurlClient(CurlClient::CurlMethod::POST, uri)
+                    .addRequestHeader(CURL_HEADER_ACCEPT_JSON)
+                    .addRequestHeader(CURL_CONTENT_TYPE_JSON);
+
+    int curlCode = cc.send();
+    std::string response = cc.getResponseBody();
+
+    OIC_LOG_V(DEBUG, TAG, "The curl response string is: %s", response.c_str());
+
+    if (curlCode != MPM_RESULT_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "Post for getting access token failed. Error code %d", curlCode);
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    rapidjson::Document doc;
+    doc.SetObject();
+
+    if (!doc.Parse<0>(response.c_str()).HasParseError())
+    {
+        if (doc.HasMember("error"))
+        {
+            OIC_LOG(ERROR, TAG, "get error message from nest server");
+            return MPM_RESULT_INTERNAL_ERROR;
+        }
+        time_t expires = time(NULL);
+        expires += doc[NEST_VALIDITY_TAG].GetInt();
+        struct tm *expTime = localtime(&expires);
+        if (expTime != NULL)
+        {
+            char *chExpTime = asctime(expTime);
+            if (chExpTime != NULL)
+            {
+                OICStrcpy(accessToken.expires, NEST_ACCESS_TOKEN_EXPIRY - 1, chExpTime);
+            }
+        }
+        OICStrcpy(accessToken.accessToken, NEST_ACCESS_TOKEN_LEN - 1,
+                  doc[NEST_ACCESS_TOKEN_TAG].GetString());
+        accessToken.grantTime = doc[NEST_VALIDITY_TAG].GetInt();
+        m_accessToken = accessToken;
+        m_isAuthorized = true;
+    }
+    else
+    {
+        result = MPM_RESULT_JSON_ERROR;
+    }
+
+    return result;
+}
+
+MPMResult Nest::parseStructureJsonResponse(std::string &json, META_INFO &meta)
+{
+    MPMResult result = MPM_RESULT_NOT_AUTHORIZED;
+    rapidjson::Document doc;
+    doc.SetObject();
+
+    if (doc.Parse<0>(json.c_str()).HasParseError())
+    {
+        return MPM_RESULT_JSON_ERROR;
+    }
+
+    for (Value::ConstMemberIterator it = doc.MemberBegin(); it != doc.MemberEnd(); it++)
+    {
+        if (doc.HasMember("error"))
+        {
+            OIC_LOG(ERROR, TAG, "access token invalid");
+            break;
+        }
+        std::string name = it->name.GetString();
+        if (doc[name.c_str()].HasMember(NEST_AWAY_TAG) &&
+            doc[name.c_str()].HasMember(NEST_NAME_TAG))
+        {
+            meta.homeName = it->value[NEST_NAME_TAG].GetString();
+            meta.countryCode = it->value[NEST_COUNTRY_TAG].GetString();
+            meta.timeZone = it->value[NEST_TIMEZONE_TAG].GetString();
+            meta.structId = it->value[NEST_STRUCT_ID_TAG].GetString();
+            meta.awayMode =
+                getAwayMode(it->value[NEST_AWAY_TAG].GetString());
+            m_isAuthorized = true;
+            result = MPM_RESULT_OK;
+        }
+    }
+
+    return result;
+}
+
+MPMResult Nest::parseDevJsonResponse(std::string &json,
+                                     std::vector<std::shared_ptr<NestThermostat> > &thermostats)
+{
+    rapidjson::Document doc;
+    doc.SetObject();
+
+    if (doc.Parse<0>(json.c_str()).HasParseError())
+    {
+        return MPM_RESULT_JSON_ERROR;
+    }
+
+    if (doc.HasMember(NEST_THERMOSTAT_TAG)
+        && doc[NEST_THERMOSTAT_TAG].IsObject())
+    {
+        // The thermostat object holds each thermostat instance
+        for (Value::ConstMemberIterator it = doc[NEST_THERMOSTAT_TAG].MemberBegin();
+             it != doc[NEST_THERMOSTAT_TAG].MemberEnd(); it++)
+        {
+            // Extract the string representation from each thermostat instance
+            std::string json = JsonHelper::toString(it);
+
+            // Create the thermostat object, internally parse the JSON reprsentation
+            // and add it to the thermostat vector
+            std::shared_ptr<NestThermostat> thermostat = std::make_shared<NestThermostat>
+                    (m_accessToken.accessToken, json);
+            thermostats.push_back(thermostat);
+        }
+    }
+    else
+    {
+        return MPM_RESULT_NOT_AUTHORIZED;
+    }
+
+    return MPM_RESULT_OK;
+}
+
+MPMResult Nest::getThermostats(std::vector<std::shared_ptr<NestThermostat> > &thermostats)
+{
+    if (!strlen(m_accessToken.accessToken))
+    {
+        OIC_LOG(ERROR, TAG, "Not authorized");
+        return MPM_RESULT_NOT_AUTHORIZED;
+    }
+
+    MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+
+    std::string uri(NEST_BASE_URL);
+    uri += NEST_DEVICES_AUTH_STR + std::string(m_accessToken.accessToken);
+
+    OIC_LOG_V(INFO, TAG, "GET request Uri: %s", uri.c_str());
+
+    CurlClient cc = CurlClient(CurlClient::CurlMethod::GET, uri)
+                    .addRequestHeader(CURL_HEADER_ACCEPT_JSON)
+                    .addRequestHeader(CURL_CONTENT_TYPE_JSON);
+
+    int curlCode = cc.send();
+    std::string response = cc.getResponseBody();
+
+    OIC_LOG_V(DEBUG, TAG, "The curl response string is: %s", response.c_str());
+
+    if (curlCode != MPM_RESULT_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "GET request failed while getting thermostat information. Error code %d",
+                  curlCode);
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    if ((result = parseDevJsonResponse(response, thermostats)) != MPM_RESULT_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "GET request failed while parsing thermostat information with result: %d",
+                  result);
+        return result;
+    }
+
+    return MPM_RESULT_OK;
+}
+
+MPMResult Nest::setAwayMode(const AWAY_MODE &awayMode)
+{
+    MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+    std::string awayValue;
+    std::string request = "{ }";
+    rapidjson::Document doc;
+
+    if (m_isAuthorized)
+    {
+        if (awayMode == eAWAway)
+        {
+            awayValue = NEST_AWAY_TAG;
+        }
+        else if (awayMode == eAWHome)
+        {
+            awayValue = NEST_HOME_TAG;
+        }
+        else
+        {
+            // bad request
+            result = MPM_RESULT_INVALID_DATA;
+        }
+
+        if (result != MPM_RESULT_INVALID_DATA)
+        {
+            // build the URI
+            std::string uri(NEST_BASE_URL);
+            uri += NEST_STRUCTURE_AUTH_STR;
+            uri += m_accessToken.accessToken;
+
+            // build the request
+            if (!doc.Parse<0>(request.c_str()).HasParseError())
+            {
+                Document::AllocatorType &allocator = doc.GetAllocator();
+
+                JsonHelper::setMember(doc, m_metaInfo.structId, "");
+                doc[m_metaInfo.structId.c_str()].SetObject();
+
+                rapidjson::Value val(awayValue.c_str(), allocator);
+                JsonHelper::setMember(doc, m_metaInfo.structId, NEST_AWAY_TAG, val);
+
+                // serialize the DOM to a string
+                request = JsonHelper::toString(doc);
+
+                CurlClient cc = CurlClient(CurlClient::CurlMethod::PUT, uri)
+                                .addRequestHeader(CURL_HEADER_ACCEPT_JSON)
+                                .addRequestHeader(CURL_CONTENT_TYPE_JSON)
+                                .setRequestBody(request);
+
+                int curlCode = cc.send();
+
+                if (curlCode != MPM_RESULT_OK)
+                {
+                    OIC_LOG_V(ERROR, TAG, "PUT request for set away mode failed. Error code %d", curlCode);
+                    return MPM_RESULT_INTERNAL_ERROR;
+                }
+                result = MPM_RESULT_OK;
+            }
+            else
+            {
+                result = MPM_RESULT_JSON_ERROR;
+            }
+        }
+    }
+    else
+    {
+        result = MPM_RESULT_NOT_AUTHORIZED;
+    }
+
+    return result;
+}
diff --git a/bridging/plugins/nest_plugin/nest_objects/nest.h b/bridging/plugins/nest_plugin/nest_objects/nest.h
new file mode 100644 (file)
index 0000000..642d663
--- /dev/null
@@ -0,0 +1,176 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+#ifndef __NEST_H__
+#define __NEST_H__
+
+#include <time.h>
+#include <stdio.h>
+#include <vector>
+#include <stdint.h>
+#include "oic_malloc.h"
+#include "oic_string.h"
+#include "nest_defs.h"
+#include "nest_thermostat.h"
+#include <string.h>
+
+using namespace std;
+
+/**
+ * This class encapsulates basic Nest functionality and
+ * the ability to enumerate and retrieve instances of Nest
+ * devices.
+ */
+class Nest
+{
+    public:
+
+        /*
+         * Holds an access token.
+         */
+#pragma pack(push, 1)
+        typedef struct _ACCESS_TOKEN
+        {
+            /** The access token used in all REST calls */
+            char accessToken[NEST_ACCESS_TOKEN_LEN];
+
+            /** UTC time when token expires */
+            char expires[NEST_ACCESS_TOKEN_EXPIRY];
+
+            /** The time the token was acquired (epoc time-January,1,1970) */
+            time_t acquiredTime;
+
+            /** The time the token is valid for (seconds) */
+            uint32_t grantTime;
+
+            _ACCESS_TOKEN()
+            {
+                bzero(accessToken, sizeof(accessToken));
+                bzero(expires, sizeof(expires));
+                acquiredTime = 0;
+                grantTime = 0;
+            }
+
+            _ACCESS_TOKEN(const char *a_token)
+            {
+                OICStrcpy(accessToken, NEST_ACCESS_TOKEN_LEN - 1, a_token);
+                memset(expires, 0, NEST_ACCESS_TOKEN_EXPIRY);
+                acquiredTime = 0;
+                grantTime = 0;
+            }
+        } ACCESS_TOKEN;
+#pragma pack(pop)
+
+        typedef enum
+        {
+            eAWUndefined = 0,                   // undefined
+            eAWHome,                            // home
+            eAWAway,                            // away
+            eAWMax                              // max
+        } AWAY_MODE;
+
+        typedef struct _META_INFO
+        {
+            std::string homeName;           // describes the name/address
+            std::string countryCode;        // country
+            std::string timeZone;           // time zone (e.g. America/Los Angeles
+            std::string structId;           // structure id
+            AWAY_MODE awayMode;             // home / away
+        } META_INFO;
+
+        Nest(const ACCESS_TOKEN &accessToken);
+
+        Nest();
+
+        virtual ~Nest()
+        { }
+
+        /**
+         * Returns a previously acquired access token value to the Nest client.
+         *
+         * @return access token as string
+         */
+        std::string getTok();
+
+        /**
+         * Attempts to obtain an access token given the authorization code.
+         *
+         * @param[in] authorizationCode            A reference to an authorization code retrieved
+         *                                         OOB
+         * @param[out] accessToken                 A valid ACCESS_TOKEN structure appropriately
+         *                                         filled
+         * @param[in] nest_client_id               A client id associated with user's cloud account
+         * @param[in] nest_client_secret           A client secret code associated with user's
+         *                                         cloud account
+         *
+         * @return MPM_RESULT_OK if ok, appropriate error code as defined in mpmErrorCode.h on error
+         */
+        MPMResult getAccessToken(std::string &authorizationCode, ACCESS_TOKEN &accessToken,
+                                 std::string &nest_client_id, std::string &nest_client_secret);
+
+        /**
+         * Sets a previously acquired access token structure to the Nest client.
+         *
+         * @param token          A reference to a valid access token.
+         */
+        void setAccessToken(const ACCESS_TOKEN &token);
+
+        /**
+         * Returns the authorization state of the client.
+         *
+         * @return true if the client is authorized to use the devices, otherwise false.
+         *
+         */
+        bool isAuthorized();
+
+        /**
+         * Returns the device list of thermostats.
+         *
+         * @param[in,out] devices        A vector reference to hold the found thermostat details
+         *
+         * @return MPM_RESULT_OK if ok, appropriate error code as defined in mpmErrorCode.h on
+         *  error. On success the devices vector will contain the authorized/found thermostats.
+         */
+        MPMResult getThermostats(std::vector<std::shared_ptr<NestThermostat> > &devices);
+
+        /**
+         * Sets the home/away mode.
+         *
+         * @param awayMode            A reference to the desired away/home mode.
+         *
+         * @return MPM_RESULT_OK if ok, appropriate error code as defined in mpmErrorCode.h
+         */
+        MPMResult setAwayMode(const AWAY_MODE &awayMode);
+
+    private:
+
+        MPMResult parseDevJsonResponse(std::string &json,
+                                       std::vector<std::shared_ptr<NestThermostat> > &device);
+
+        MPMResult parseStructureJsonResponse(std::string &json, META_INFO &meta);
+
+        AWAY_MODE getAwayMode(const char *value) const;
+
+        ACCESS_TOKEN m_accessToken;
+        META_INFO m_metaInfo;
+        bool m_isAuthorized;
+};
+
+#endif /* __NEST_H__ */
diff --git a/bridging/plugins/nest_plugin/nest_objects/nest_defs.h b/bridging/plugins/nest_plugin/nest_objects/nest_defs.h
new file mode 100644 (file)
index 0000000..94ff7a8
--- /dev/null
@@ -0,0 +1,90 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+#ifndef __NEST_DEFS_H__
+#define __NEST_DEFS_H__
+
+#include <string>
+
+static const char ACCESS_TOKEN_URL[]   = "https://api.home.nest.com/oauth2/access_token";
+static const char NEST_BASE_URL[]      = "https://developer-api.nest.com";
+
+///
+/// json tag definitions
+///
+static const char NEST_ACCESS_TOKEN_TAG[]      = "access_token";
+static const char NEST_VALIDITY_TAG[]          = "expires_in";
+static const char NEST_THERMOSTAT_TAG[]        = "thermostats";
+static const char NEST_DEVICE_ID_TAG[]         = "device_id";
+static const char NEST_LOCALE_TAG[]            = "locale";
+static const char NEST_SW_VER_TAG[]            = "software_version";
+static const char NEST_STRUCT_ID_TAG[]         = "structure_id";
+static const char NEST_NAME_TAG[]              = "name";
+static const char NEST_NAME_LONG_TAG[]         = "name_long";
+static const char NEST_LAST_CONN_TAG[]         = "last_connection";
+static const char NEST_ONLINE_TAG[]            = "is_online";
+static const char NEST_COOL_TAG[]              = "can_cool";
+static const char NEST_HEAT_TAG[]              = "can_heat";
+static const char NEST_FAN_TAG[]               = "has_fan";
+static const char NEST_HUMIDITY_TAG[]          = "humidity";
+static const char NEST_TEMP_SCALE[]            = "temperature_scale";
+static const char NEST_HVAC_MODE_TAG[]         = "hvac_mode";
+static const char NEST_TARGET_TEMP_C_TAG[]     = "target_temperature_c";
+static const char NEST_TARGET_TEMP_F_TAG[]     = "target_temperature_f";
+static const char NEST_TARGET_TEMP_HIGH_C_TAG[] = "target_temperature_high_c";
+static const char NEST_TARGET_TEMP_HIGH_F_TAG[] = "target_temperature_high_f";
+static const char NEST_TARGET_TEMP_LOW_C_TAG[] = "target_temperature_low_c";
+static const char NEST_TARGET_TEMP_LOW_F_TAG[] = "target_temperature_low_f";
+static const char NEST_AMBIENT_TEMP_C_TAG[]    = "ambient_temperature_c";
+static const char NEST_AMBIENT_TEMP_F_TAG[]    = "ambient_temperature_f";
+static const char NEST_AWAY_TEMP_HIGH_C_TAG[]  = "away_temperature_high_c";
+static const char NEST_AWAY_TEMP_HIGH_F_TAG[]  = "away_temperature_high_f";
+static const char NEST_AWAY_TEMP_LOW_C_TAG[]   = "away_temperature_low_c";
+static const char NEST_AWAY_TEMP_LOW_F_TAG[]   = "away_temperature_low_f";
+static const char NEST_FAN_TIMER_ACTIVE_TAG[]  = "fan_timer_active";
+static const char NEST_LEAF_TAG[]              = "has_leaf";
+static const char NEST_EMERGENCY_HEAT_TAG[]    = "is_using_emergency_heat";
+static const char NEST_AWAY_TAG[]              = "away";
+static const char NEST_HOME_TAG[]              = "home";
+static const char NEST_POSTAL_TAG[]            = "postal_code";
+static const char NEST_COUNTRY_TAG[]           = "country_code";
+static const char NEST_TIMEZONE_TAG[]          = "time_zone";
+
+// Other constants
+static const int NEST_ACCESS_TOKEN_LEN              = 512;
+static const int NEST_ACCESS_TOKEN_EXPIRY           = 128;
+static const char NEST_HVAC_HEAT[]             = "heat";
+static const char NEST_HVAC_COOL[]             = "cool";
+static const char NEST_HVAC_MIXED[]            = "heat-cool";
+static const char NEST_HVAC_OFF[]              = "off";
+static const char NEST_TEMP_SCALE_C[]          = "C";
+static const char NEST_TEMP_SCALE_F[]          = "F";
+
+// Other definitions
+static const char NEST_STRUCTURE_AUTH_STR[]    = "/structures?auth=";
+static const char NEST_CLIENT_ID_STR[]         = "?client_id=";
+static const char NEST_CODE_STR[]              = "&code=";
+static const char NEST_CLIENT_SECRET_STR[]     = "&client_secret=";
+static const char NEST_AUTH_CODE_STR[]         = "&grant_type=authorization_code";
+static const char NEST_DEVICES_AUTH_STR[]      = "/devices?auth=";
+static const char NEST_DEVICES_THERMOSTAT_STR[] = "/devices/thermostats/";
+static const char NEST_AUTH_STR[]              = "?auth=";
+
+#endif /* __NEST_DEFS_H__ */
diff --git a/bridging/plugins/nest_plugin/nest_objects/nest_thermostat.cpp b/bridging/plugins/nest_plugin/nest_objects/nest_thermostat.cpp
new file mode 100644 (file)
index 0000000..1b7e306
--- /dev/null
@@ -0,0 +1,178 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+#include <stdio.h>
+#include <string>
+#include "nest_thermostat.h"
+#include "nest_defs.h"
+#include "rapidjson.h"
+#include "document.h"
+#include "JsonHelper.h"
+#include "writer.h"
+#include "stringbuffer.h"
+#include "curlClient.h"
+#include "logger.h"
+
+#define TAG "NEST_THERMOSTAT"
+
+using namespace OC::Bridging;
+
+NestThermostat::NestThermostat(const std::string &token, uint16_t hum, uint32_t hvac,
+                               uint16_t temp, uint32_t scale, const char *devId) : m_token(token)
+{
+    m_thermostat.humidity = hum;
+    m_thermostat.targetTempF = temp;
+    m_thermostat.temperature = (TEMPERATURE_SCALE) scale;
+    m_thermostat.hvacMode = (HVAC_MODE) hvac;
+    m_thermostat.devInfo.id = devId;
+}
+
+NestThermostat::NestThermostat(const std::string &token, const std::string &thermostat)
+    : m_token(token)
+{
+    buildThermostat(thermostat);
+}
+
+NestThermostat::HVAC_MODE NestThermostat::getHVACmode(const std::string &hvacMode)
+{
+    NestThermostat::HVAC_MODE result = HVAC_UNDEFINED;
+    if (hvacMode == NEST_HVAC_HEAT)
+    {
+        result = HVAC_HEAT;
+    }
+    else if (hvacMode == NEST_HVAC_COOL)
+    {
+        result = HVAC_COOL;
+    }
+    else if (hvacMode == NEST_HVAC_MIXED)
+    {
+        result = HVAC_MIXED;
+    }
+    else if (hvacMode == NEST_HVAC_OFF)
+    {
+        result = HVAC_OFF;
+    }
+    return result;
+}
+
+NestThermostat::TEMPERATURE_SCALE NestThermostat::getTemperatureScale(const std::string &tempScale)
+{
+    NestThermostat::TEMPERATURE_SCALE result = TEMP_UNDEFINED;
+    if (tempScale == NEST_TEMP_SCALE_C)
+    {
+        result = TEMP_CELSIUS;
+    }
+    else if (tempScale == NEST_TEMP_SCALE_F)
+    {
+        result = TEMP_FAHRENHEIT;
+    }
+    return result;
+}
+
+MPMResult NestThermostat::buildThermostat(const std::string &thermostat)
+{
+    if (thermostat.empty())
+    {
+        return MPM_RESULT_INVALID_DATA;
+    }
+
+    rapidjson::Document doc;
+    doc.SetObject();
+
+    if (doc.Parse<0>(thermostat.c_str()).HasParseError())
+    {
+        return MPM_RESULT_JSON_ERROR;
+    }
+
+    // Read the general/common device infor structure (common for all Nest devices)
+    m_thermostat.devInfo.version = doc[NEST_SW_VER_TAG].GetString();
+    m_thermostat.devInfo.locale = doc[NEST_LOCALE_TAG].GetString();
+    m_thermostat.devInfo.nameLong = doc[NEST_NAME_LONG_TAG].GetString();
+    m_thermostat.devInfo.id = doc[NEST_DEVICE_ID_TAG].GetString();
+    m_thermostat.devInfo.name = doc[NEST_NAME_TAG].GetString();
+    m_thermostat.devInfo.structId = doc[NEST_STRUCT_ID_TAG].GetString();
+
+    // Read the details of the thermostat
+    m_thermostat.humidity = doc[NEST_HUMIDITY_TAG].GetInt();
+    m_thermostat.hasFan = doc[NEST_FAN_TAG].GetBool();
+    m_thermostat.hasLeaf = doc[NEST_LEAF_TAG].GetBool();
+    m_thermostat.canHeat = doc[NEST_HEAT_TAG].GetBool();
+    m_thermostat.canCool = doc[NEST_COOL_TAG].GetBool();
+    m_thermostat.targetTempC = doc[NEST_TARGET_TEMP_C_TAG].GetDouble();
+    m_thermostat.targetTempF = doc[NEST_TARGET_TEMP_F_TAG].GetUint();
+    m_thermostat.targetTempHighC = doc[NEST_TARGET_TEMP_HIGH_C_TAG].GetDouble();
+    m_thermostat.targetTempHighF = doc[NEST_TARGET_TEMP_HIGH_F_TAG].GetUint();
+    m_thermostat.targetTempLowC = doc[NEST_TARGET_TEMP_LOW_C_TAG].GetDouble();
+    m_thermostat.targetTempLowF = doc[NEST_TARGET_TEMP_LOW_F_TAG].GetUint();
+    m_thermostat.ambientTempC = doc[NEST_AMBIENT_TEMP_C_TAG].GetDouble();
+    m_thermostat.ambientTempF = doc[NEST_AMBIENT_TEMP_F_TAG].GetDouble();
+    m_thermostat.awayTempHighC = doc[NEST_AWAY_TEMP_HIGH_C_TAG].GetDouble();
+    m_thermostat.awayTempHighF = doc[NEST_AWAY_TEMP_HIGH_F_TAG].GetDouble();
+    m_thermostat.awayTempLowC = doc[NEST_AWAY_TEMP_LOW_C_TAG].GetDouble();
+    m_thermostat.awayTempLowF = doc[NEST_AWAY_TEMP_LOW_F_TAG].GetDouble();
+    m_thermostat.fanTimerActive = doc[NEST_FAN_TIMER_ACTIVE_TAG].GetBool();
+    m_thermostat.isOnline = doc[NEST_ONLINE_TAG].GetBool();
+    m_thermostat.hvacMode = getHVACmode(doc[NEST_HVAC_MODE_TAG].GetString());
+    m_thermostat.temperature = getTemperatureScale(doc[NEST_TEMP_SCALE].GetString());
+
+    return MPM_RESULT_OK;
+}
+
+MPMResult NestThermostat::setTemperature(uint16_t targetTemp)
+{
+    std::string scale;
+    std::string request = "{ }";
+    rapidjson::StringBuffer sb;
+    rapidjson::Document doc;
+
+    if (m_thermostat.temperature == TEMP_FAHRENHEIT)
+    {
+        scale = NEST_TARGET_TEMP_F_TAG;
+    }
+    else
+    {
+        scale = NEST_TARGET_TEMP_C_TAG;
+    }
+
+    std::string uri(NEST_BASE_URL);
+    uri += NEST_DEVICES_THERMOSTAT_STR + m_thermostat.devInfo.id + NEST_AUTH_STR + m_token;
+
+    doc.SetObject();
+    JsonHelper::setMember(doc, scale, targetTemp);
+    request = JsonHelper::toString(doc);
+
+    CurlClient cc = CurlClient(CurlClient::CurlMethod::PUT, uri)
+                    .addRequestHeader(CURL_HEADER_ACCEPT_JSON)
+                    .addRequestHeader(CURL_CONTENT_TYPE_JSON)
+                    .setRequestBody(request);
+
+    int curlCode = cc.send();
+    std::string response = cc.getResponseBody();
+
+    OIC_LOG_V(DEBUG, TAG, "The curl response string is: %s", response.c_str());
+
+    if (curlCode != MPM_RESULT_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "Set temperature failed. Error code %d", curlCode);
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    return MPM_RESULT_OK;
+}
diff --git a/bridging/plugins/nest_plugin/nest_objects/nest_thermostat.h b/bridging/plugins/nest_plugin/nest_objects/nest_thermostat.h
new file mode 100644 (file)
index 0000000..600c145
--- /dev/null
@@ -0,0 +1,138 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+#ifndef __NEST_THERMOSTAT_H__
+#define __NEST_THERMOSTAT_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <vector>
+#include <string>
+#include <iostream>
+#include <memory>
+#include "mpmErrorCode.h"
+
+using namespace std;
+
+class NestThermostat
+{
+    public:
+        typedef std::vector<NestThermostat> devices;
+
+        NestThermostat(const std::string &token, uint16_t hum, uint32_t hvac,
+                       uint16_t temp, uint32_t scale, const char *devId);
+
+        NestThermostat(const std::string &token, const std::string &jsonThermostat);
+
+        virtual ~NestThermostat()
+        { }
+
+        enum TEMPERATURE_SCALE
+        {
+            TEMP_UNDEFINED = 0,
+            TEMP_CELSIUS = 1,
+            TEMP_FAHRENHEIT = 2,
+            TEMP_MAX
+        };
+
+        enum HVAC_MODE
+        {
+            HVAC_UNDEFINED = 0,
+            HVAC_HEAT = 1,
+            HVAC_COOL = 2,
+            HVAC_MIXED = 3,
+            HVAC_OFF = 4,
+            HVAC_MAX
+        };
+
+        typedef struct _DEVICE_INFO
+        {
+            std::string id;                 // the unique ID for this instance
+            std::string version;            // software version
+            std::string structId;           // structure ID
+            std::string name;               // Friendly name of the thermostat
+            std::string nameLong;           // Long friendly name
+            std::string lastConnection;     // Last connection time
+            std::string locale;             // Locale
+        } DEVICE_INFO;
+
+        typedef struct _THERMOSTAT
+        {
+            DEVICE_INFO devInfo;            // Device info
+            bool isOnline;                  // Is thermostat online now?
+            bool canCool;                   // AC connected
+            bool canHeat;                   // Heater connected
+            bool usingEmergencyHeat;        // True if emergency heat is in use
+            bool hasFan;                    // Is a fan installed
+            bool fanTimerActive;            // Is the fan timer currently active
+            bool fanTimerTimeout;           // Fan timer timeout (absolut timee)
+            bool hasLeaf;                   // Is leaf (savings) displayed
+            TEMPERATURE_SCALE temperature;  // Temperature scale (C or F))
+            double targetTempC;             // Target temperature C
+            uint16_t targetTempF;           // Target temperature F
+            double targetTempHighC;         // Target high temperature C
+            uint16_t targetTempHighF;       // Target high temperature F
+            double targetTempLowC;          // Target low temperature C
+            uint16_t targetTempLowF;        // Target low temperature F
+            double awayTempHighF;           // Away high tempereature F
+            double awayTempHighC;           // Away high temperature C
+            uint16_t awayTempLowF;          // Away low temperature F
+            uint16_t awayTempLowC;          // Away low temperature C
+            HVAC_MODE hvacMode;             // Current HVAC mode
+            double ambientTempF;            // Ambient temperature F
+            double ambientTempC;            // Ambient temperature C
+            uint16_t humidity;              // Current humidity (0 - 100)
+
+            _THERMOSTAT()
+            {
+                isOnline = canCool = canHeat = usingEmergencyHeat = hasFan = fanTimerActive = fanTimerTimeout =
+                                                   hasLeaf = false;
+                targetTempC = targetTempHighC = targetTempLowC = awayTempHighF = awayTempHighC = ambientTempF =
+                                                    ambientTempC = 0.0;
+                targetTempF = targetTempHighF = targetTempLowF = awayTempLowF = awayTempLowC = humidity = 0;
+                temperature = (TEMPERATURE_SCALE) - 1;
+                hvacMode = (HVAC_MODE) - 1;
+            }
+
+        } THERMOSTAT;
+
+        MPMResult get(THERMOSTAT &data)
+        {
+            data = m_thermostat;
+            return MPM_RESULT_OK;
+        }
+
+        MPMResult setTemperature(uint16_t targetTemp);
+
+    private:
+
+        MPMResult buildThermostat(const std::string &json);
+
+        HVAC_MODE getHVACmode(const std::string &hvacMode);
+
+        TEMPERATURE_SCALE getTemperatureScale(const std::string &tempScale);
+
+        THERMOSTAT m_thermostat;
+        std::string m_token;
+};
+
+typedef std::shared_ptr<NestThermostat> NestThermostatSharedPtr;
+#endif /* __NEST_THERMOSTAT_H__ */
diff --git a/bridging/plugins/nest_plugin/nest_resource.cpp b/bridging/plugins/nest_plugin/nest_resource.cpp
new file mode 100644 (file)
index 0000000..7a146b4
--- /dev/null
@@ -0,0 +1,835 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <string>
+#include <unistd.h>
+#include <signal.h>
+#include <pthread.h>
+#include <assert.h>
+#include <fstream>
+#include <map>
+#include <memory>
+#include <set>
+#include "pluginServer.h"
+#include "oic_malloc.h"
+#include "oic_string.h"
+#include "nest.h"
+#include <cstdlib>
+#include <iostream>
+#include <time.h>
+#include "logger.h"
+#include "ConcurrentIotivityUtils.h"
+#include "messageHandler.h"
+#include "ocpayload.h"
+#include "mpmErrorCode.h"
+
+using namespace std;
+
+/*******************************************************************************
+ * Pound defines and structure declarations go here
+ ******************************************************************************/
+#define TAG "NEST_RESOURCE"
+
+#define DEVICE_NAME "Nest Translator"
+#define DEVICE_TYPE "oic.d.thermostat"
+#define MANUFACTURER_NAME "Nest"
+
+#define NEST_ID_TAG "x.com.intel.id"
+#define NEST_LAST_CONNECTION_TAG "x.com.intel.lastConnection"
+
+#define TEMPERATURE_TAG "temperature"
+
+#define BM 3
+
+using namespace OC::Bridging;
+
+typedef struct
+{
+    uint16_t humidity;
+    uint32_t temperature;
+    uint16_t targetTempF;
+    uint32_t hvacMode;
+    char accessToken[NEST_ACCESS_TOKEN_LEN];
+    char deviceId[MPM_MAX_LENGTH_64];
+} MPMPluginSpecificData;
+
+static const std::string NEST_THERMOSTAT_IF = "oic.if.a";
+static const std::string NEST_THERMOSTAT_RT = "oic.r.temperature";
+
+static const char CRED_FILE[] = "./oic_svr_db_nest.dat";
+
+FILE *nestSecurityFile(const char *, const char *mode)
+{
+    return fopen(CRED_FILE, mode);
+}
+
+/*******************************************************************************
+ * global data goes here
+ ******************************************************************************/
+MPMPluginCtx *g_ctx = NULL;
+Nest *g_nest = NULL;
+NestThermostat::devices g_devices;
+std::string nest_client_id;
+std::string nest_client_secret;
+
+std::map<std::string, NestThermostatSharedPtr> uriToNestThermostatMap;
+std::map<std::string, NestThermostatSharedPtr> addedThermostats;
+
+uint16_t getTemperatureAndUpdateMap(NestThermostatSharedPtr t);
+OCEntityHandlerResult resourceEntityHandlerCb(OCEntityHandlerFlag flag,
+        OCEntityHandlerRequest *entityHandlerRequest, void *callbackParam);
+
+MPMResult loadNestAuthConfig(std::string filename, std::string &pincode,
+                             std::string &accessToken)
+{
+    FILE *fp = fopen(filename.c_str(), "r");
+    char str[MPM_MAX_LENGTH_1024];
+    MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+
+    if (NULL != fp)
+    {
+        if (fgets(str, MPM_MAX_LENGTH_1024, fp) == NULL)
+        {
+            OIC_LOG_V(ERROR, TAG, "fgets failed on %s", filename.c_str());
+            goto CLEANUP;
+        }
+        // @todo the logic below needs to be fixed.
+        // The intention here was to remove the last character which was new line.
+        // Current logic doesn't cover edge case like empty string and etc.
+        str[strlen(str) - 1] = '\0';
+        pincode = std::string(str);
+
+        if (fgets(str, MPM_MAX_LENGTH_1024, fp) == NULL)
+        {
+            OIC_LOG_V(ERROR, TAG, "fgets failed on %s", filename.c_str());
+            goto CLEANUP;
+        }
+        str[strlen(str) - 1] = '\0';
+        accessToken = std::string(str);
+
+        if (fgets(str, MPM_MAX_LENGTH_1024, fp) == NULL)
+        {
+            OIC_LOG_V(ERROR, TAG, "fgets failed on %s", filename.c_str());
+            goto CLEANUP;
+        }
+        str[strlen(str) - 1] = '\0';
+        nest_client_id = std::string(str);
+
+        if (fgets(str, MPM_MAX_LENGTH_1024, fp) == NULL)
+        {
+            OIC_LOG_V(ERROR, TAG, "fgets failed on %s", filename.c_str());
+            goto CLEANUP;
+        }
+        str[strlen(str) - 1] = '\0';
+        nest_client_secret = std::string(str);
+
+        result = MPM_RESULT_OK;
+    }
+    else
+    {
+        OIC_LOG_V(ERROR, TAG, "Could not open %s.\n", filename.c_str());
+    }
+
+CLEANUP:
+    if (fp != NULL)
+    {
+        fclose(fp);
+    }
+    return result;
+}
+
+Nest::ACCESS_TOKEN populateAccessTokenFromFile(std::string accessToken)
+{
+    Nest::ACCESS_TOKEN aTok(accessToken.c_str());
+
+    return aTok;
+}
+
+MPMResult checkValidityOfExistingToken(Nest::ACCESS_TOKEN aTok)
+{
+    MPMResult result = MPM_RESULT_OK;
+
+    g_nest->setAccessToken(aTok);
+
+    //verify the token by sending the token to nest cloud
+    if (g_nest->isAuthorized())
+    {
+        OIC_LOG(INFO, TAG, "Successfully authorized");
+    }
+    else
+    {
+        OIC_LOG(ERROR, TAG, "Could not authorize, access token is invalid/expired");
+        result = MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    return result;
+}
+
+void updateNestTokenFile(std::string filename, std::string pincode, std::string accessToken)
+{
+    FILE *fp = fopen(filename.c_str(), "w");
+    if (fp == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "Failed to open nest.cnf file");
+        return;
+    }
+    fputs(pincode.c_str(), fp);
+    fputs("\n", fp);
+    fputs(accessToken.c_str(), fp);
+    fputs("\n", fp);
+    fputs(nest_client_id.c_str(), fp);
+    fputs("\n", fp);
+    fputs(nest_client_secret.c_str(), fp);
+    fputs("\n", fp);
+
+    fclose(fp);
+}
+
+MPMResult refreshAccessToken(std::string filename, std::string pincode)
+{
+    MPMResult result = MPM_RESULT_OK;
+
+    Nest::ACCESS_TOKEN aToken;
+    if (g_nest->getAccessToken(pincode, aToken, nest_client_id, nest_client_secret) != MPM_RESULT_OK)
+    {
+        OIC_LOG(ERROR, TAG, "get token failed");
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    g_nest->setAccessToken(aToken);
+
+    if (g_nest->isAuthorized())
+    {
+        OIC_LOG(INFO, TAG, "Successfully authorized");
+        std::string aTok(aToken.accessToken);
+        updateNestTokenFile(filename, pincode, aTok);
+    }
+    else
+    {
+        OIC_LOG(ERROR, TAG, "Could not authorize, Please put a new pincode into the file");
+        result = MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    return result;
+}
+
+/**
+ * This is a plugin's specific entry point function that allows the plugin
+ * to create and initialize static structures.
+ *
+ * @param[out] pluginSpecificCtx            the plugin specific context populated by
+ *                                          the plugin.
+ *
+ * @return MPM_RESULT_OK if no errors, MPM_RESULT_INTERNAL_ERROR if stack process error
+ */
+MPMResult pluginCreate(MPMPluginCtx **pluginSpecificCtx)
+{
+    MPMResult result = MPM_RESULT_OK;
+
+    if (g_ctx)
+    {
+        OIC_LOG(ERROR, TAG, "Plugin is already created.");
+        return MPM_RESULT_ALREADY_CREATED;
+    }
+
+    /* allocate a context structure for the plugin */
+    MPMPluginCtx *ctx = (MPMPluginCtx *) OICCalloc(1, sizeof(MPMPluginCtx));
+
+    /* initialize the plugin context */
+    if (!ctx)
+    {
+        OIC_LOG(ERROR, TAG, "Unable to allocate plugin specific context.");
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    *pluginSpecificCtx = ctx;
+    g_ctx = ctx;
+
+    ctx->device_name = DEVICE_NAME;
+    ctx->resource_type = DEVICE_TYPE;
+    ctx->open = nestSecurityFile;
+
+    std::string pincode = "";
+    std::string accessToken = "";
+
+    std::string filename = "nest.cnf";
+
+    if (loadNestAuthConfig(filename, pincode, accessToken) != MPM_RESULT_OK)
+    {
+        OIC_LOG(ERROR, TAG, "Unable to load nest.cnf");
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    g_nest = new Nest();
+    Nest::ACCESS_TOKEN aTok = populateAccessTokenFromFile(accessToken);
+
+    result = checkValidityOfExistingToken(aTok);
+
+    if (MPM_RESULT_OK != result)
+    {
+        OIC_LOG(ERROR, TAG, "Nest object could not be created, requesting new access token");
+        result = refreshAccessToken(filename, pincode);
+    }
+
+    if (MPM_RESULT_OK != result)
+    {
+        delete (g_nest);
+        g_nest = NULL;
+    }
+
+    OIC_LOG_V(INFO, TAG, "Plugin create return value:%d.", result);
+
+    /*
+     * NOTE: What do we do if the create for some reason failed.  To we assume that the destroy
+     * will be called if the create fails??  Let let the plugin loader pick up the pieces by
+     * calling destroy on an imperfectly created plugin.  Calling entry point APIs from within
+     * the implementation can cause some real problems (e.g. deadlock situations).
+     */
+
+    return result;
+}
+
+/**
+ * Invoked by the plugin's OCF server to allow the plugin to use IoTivity APIs
+ *
+ * @param[in] pluginSpecificCtx            The plugin specific context created during plugin
+ *                                         create
+ *
+ * @return MPM_RESULT_OK if no errors, MPM_RESULT_INTERNAL_ERROR if stack process error
+ */
+MPMResult pluginStart(MPMPluginCtx *pluginSpecificCtx)
+{
+    MPMResult result = MPM_RESULT_OK;
+    if (!pluginSpecificCtx)
+    {
+        result = MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    return result;
+}
+
+bool isSecureEnvironmentSet()
+{
+    char *non_secure_env = getenv("NONSECURE");
+
+    if (non_secure_env && (strcmp(non_secure_env, "true") == 0))
+    {
+        OIC_LOG(INFO, TAG, "Creating NON SECURE resources");
+        return false;
+    }
+    OIC_LOG(INFO, TAG, "Creating SECURE resources");
+    return true;
+}
+
+/**
+ * For initiating a scan for thermostats
+ *
+ * @return MPM_RESULT_OK if no error, specific error code defined in mpmErrorCode.h
+ * upon error
+ */
+MPMResult pluginScan(MPMPluginCtx *, MPMPipeMessage *)
+{
+    MPMResult nestResult = MPM_RESULT_OK;
+
+    std::vector<NestThermostatSharedPtr> thermostatScanned;
+
+    nestResult = g_nest->getThermostats(thermostatScanned);
+    if (MPM_RESULT_OK == nestResult)
+    {
+        if (thermostatScanned.size() <= 0)
+        {
+            OIC_LOG(INFO, TAG, "getThermostats succeeded but zero thermostats found");
+        }
+        else
+        {
+            for (uint32_t i = 0; i < thermostatScanned.size(); ++i)
+            {
+                NestThermostatSharedPtr thermostat = thermostatScanned[i];
+                NestThermostat::THERMOSTAT data;
+                thermostat->get(data);
+                std::string uri = "/nest/" + data.devInfo.id;
+                OIC_LOG_V(INFO, TAG, "uri: %s", uri.c_str());
+
+                if (addedThermostats.find(uri) != addedThermostats.end())
+                {
+                    OIC_LOG_V(INFO, TAG, "Already added %s. Ignoring", uri.c_str());
+                    continue;
+                }
+
+                uriToNestThermostatMap[uri] = thermostat;
+
+                MPMSendResponse(uri.c_str(), uri.size(), MPM_SCAN);
+            }
+        }
+    }
+    else
+    {
+        OIC_LOG_V(ERROR, TAG, "getThermostats returned error %i", nestResult);
+    }
+
+    return nestResult;
+
+}
+
+/**
+ * Creates 1 OCF resource for a NestThermostat for temperature control.
+ *
+ * @param[in] uri            Base uri. Resource uri is the same as base uri
+ *
+ * @return MPM_RESULT_OK
+ */
+MPMResult createOCFResource(const std::string &uri)
+{
+    uint8_t resourceProperties = (OC_OBSERVABLE | OC_DISCOVERABLE);
+    if (isSecureEnvironmentSet())
+    {
+        resourceProperties |= OC_SECURE;
+    }
+
+    ConcurrentIotivityUtils::queueCreateResource(uri, NEST_THERMOSTAT_RT, NEST_THERMOSTAT_IF,
+            resourceEntityHandlerCb,
+            NULL, resourceProperties);
+
+    return MPM_RESULT_OK;
+}
+
+MPMResult  deleteOCFResource(const std::string &uri)
+{
+    ConcurrentIotivityUtils::queueDeleteResource(uri);
+
+    return MPM_RESULT_OK;
+}
+
+void createPayloadForMetadata(MPMResourceList **list, const char *uri)
+{
+    MPMResourceList *tempPtr = NULL;
+    tempPtr = (MPMResourceList *) OICCalloc(1, sizeof(MPMResourceList));
+    if (!tempPtr)
+    {
+        OIC_LOG(ERROR, TAG, "Memory allocation failed");
+        return;
+    }
+
+    OICStrcpy(tempPtr->rt, MPM_MAX_LENGTH_64, NEST_THERMOSTAT_RT.c_str());
+    OICStrcpy(tempPtr->href, MPM_MAX_URI_LEN, uri);
+    OICStrcpy(tempPtr->interfaces, MPM_MAX_LENGTH_64, NEST_THERMOSTAT_IF.c_str());
+    tempPtr->bitmap = BM;
+    tempPtr->next = *list;
+    *list  = tempPtr;
+}
+
+void updateMPMPluginSpecificData(NestThermostat::THERMOSTAT thermostat,
+                                 MPMPluginSpecificData *pluginDetails)
+{
+    pluginDetails->humidity = thermostat.humidity;
+    pluginDetails->hvacMode = thermostat.hvacMode;
+    pluginDetails->targetTempF = thermostat.targetTempF;
+    pluginDetails->temperature = thermostat.temperature;
+    memcpy(pluginDetails->accessToken, g_nest->getTok().c_str(), g_nest->getTok().length());
+    memcpy(pluginDetails->deviceId, thermostat.devInfo.id.c_str(), thermostat.devInfo.id.length());
+}
+
+/**
+ * For adding a new thermostat
+ *
+ * @param[in] message            The add request message coming from client. It contains
+ *                               the uri of the device to be added
+ *
+ * @return MPM_RESULT_OK if no error, specific error code defined in mpmErrorCode.h
+ * upon error
+ */
+MPMResult pluginAdd(MPMPluginCtx *, MPMPipeMessage *message)
+{
+    if (message->payloadSize <= 0)
+    {
+        OIC_LOG(ERROR, TAG, "add payload is null");
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    MPMResourceList *list = NULL;
+    uint8_t *buff = NULL;
+
+    MPMDeviceSpecificData deviceConfiguration;
+    MPMPluginSpecificData pluginSpecificDetails;
+    memset(&deviceConfiguration, 0, sizeof(MPMDeviceSpecificData));
+    memset(&pluginSpecificDetails, 0, sizeof(MPMPluginSpecificData));
+
+    std::string uri = reinterpret_cast<const char *>(message->payload);
+
+    if (addedThermostats.find(uri) != addedThermostats.end())
+    {
+        OIC_LOG_V(ERROR, TAG, "%s already added", uri.c_str());
+        return MPM_RESULT_ALREADY_CREATED;
+    }
+    if (uriToNestThermostatMap.find(uri) == uriToNestThermostatMap.end())
+    {
+        OIC_LOG_V(ERROR, TAG, "%s was NOT discovered in a scan", uri.c_str());
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    createOCFResource(uri);
+    addedThermostats[uri] = uriToNestThermostatMap[uri];
+
+    createPayloadForMetadata(&list, uri.c_str());
+
+    NestThermostat::THERMOSTAT thermostat;
+    addedThermostats[uri]->get(thermostat);
+    updateMPMPluginSpecificData(thermostat, &pluginSpecificDetails);
+
+    buff = (uint8_t *)OICCalloc(1, MPM_MAX_METADATA_LEN);
+    if (buff == NULL)
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to allocate memory for reconnect buffer");
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    OICStrcpy(deviceConfiguration.devName, MPM_MAX_LENGTH_64, DEVICE_NAME);
+    OICStrcpy(deviceConfiguration.devType, MPM_MAX_LENGTH_64, DEVICE_TYPE);
+    OICStrcpy(deviceConfiguration.manufacturerName, MPM_MAX_LENGTH_256, MANUFACTURER_NAME);
+    MPMFormMetaData(list, &deviceConfiguration, buff, MPM_MAX_METADATA_LEN,
+                    (void *)&pluginSpecificDetails, sizeof(MPMPluginSpecificData));
+
+    MPMAddResponse response;
+    memset(&response, 0, sizeof(MPMAddResponse));
+    OICStrcpy(response.uri, MPM_MAX_URI_LEN, uri.c_str());
+    memcpy(response.metadata, buff, MPM_MAX_METADATA_LEN);
+
+    MPMSendResponse(&response, sizeof(MPMAddResponse), MPM_ADD);
+
+    OICFree(buff);
+    return MPM_RESULT_OK;
+}
+
+/**
+ * For removing a thermostat
+ *
+ * @param[in] message            The remove request message coming from client. It contains
+ *                               the uri of the device to be removed
+ *
+ * @return MPM_RESULT_OK if no error, MPM_RESULT_INTERNAL_ERROR if error
+ */
+MPMResult pluginRemove(MPMPluginCtx *, MPMPipeMessage *message)
+{
+    if (message->payloadSize > 0 && message->payload != NULL)
+    {
+        std::string uri = reinterpret_cast<const char *>(message->payload);
+        OIC_LOG_V(DEBUG, TAG, "device uri to be removed - %s ", uri.c_str());
+
+        if (addedThermostats.find(uri) == addedThermostats.end())
+        {
+            OIC_LOG(ERROR, TAG, "Device to be removed is not added yet");
+            return MPM_RESULT_NOT_PRESENT;
+        }
+
+        deleteOCFResource(uri);
+        addedThermostats.erase(uri);
+        uriToNestThermostatMap.erase(uri);
+
+        MPMSendResponse(uri.c_str(), uri.size(), MPM_REMOVE);
+        return MPM_RESULT_OK;
+    }
+
+    return MPM_RESULT_INTERNAL_ERROR;
+}
+
+/**
+ * For reconnecting a thermostat after a system reboot. This method is invoked once
+ * for each device reconnect request for which is trigerred by the client.
+ *
+ * @param[in] message            The reconnect request message coming from client. It contains
+ *                               the metadata for the device to be reconnected
+ *
+ * @return MPM_RESULT_OK if no error, MPM_RESULT_INTERNAL_ERROR if error
+ */
+MPMResult pluginReconnect(MPMPluginCtx *, MPMPipeMessage *message)
+{
+    MPMResourceList *list = NULL, *temp = NULL;
+    void *pluginSpecificDetails = NULL;
+    std::vector<NestThermostatSharedPtr> thermostatsReconnected;
+
+    if (message->payloadSize <= 0 && message->payload == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "No paylaod received, failed to reconnect");
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    MPMParseMetaData(message->payload, MPM_MAX_METADATA_LEN, &list, &pluginSpecificDetails);
+
+    MPMPluginSpecificData *pluginDetails = (MPMPluginSpecificData *)pluginSpecificDetails;
+
+    std::shared_ptr<NestThermostat> sharedThermostat = std::make_shared<NestThermostat>
+            (pluginDetails->accessToken,
+             pluginDetails->humidity,
+             pluginDetails->hvacMode,
+             pluginDetails->targetTempF,
+             pluginDetails->temperature,
+             pluginDetails->deviceId);
+    thermostatsReconnected.push_back(sharedThermostat);
+
+    std::string uri;
+
+    NestThermostat::THERMOSTAT data;
+
+    sharedThermostat->get(data);
+    uri = "/nest/" + data.devInfo.id;
+
+    if (uriToNestThermostatMap.find(uri) != uriToNestThermostatMap.end())
+    {
+        OIC_LOG_V(INFO, TAG, "Already found %s. Ignoring", uri.c_str());
+    }
+    else
+    {
+        uriToNestThermostatMap[uri] = sharedThermostat;
+    }
+
+    if (addedThermostats.find(uri) != addedThermostats.end())
+    {
+        OIC_LOG_V(ERROR, TAG, "%s already added", uri.c_str());
+        return MPM_RESULT_ALREADY_CREATED;
+    }
+    if (uriToNestThermostatMap.find(uri) == uriToNestThermostatMap.end())
+    {
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    while (list)
+    {
+        temp = list;
+        OIC_LOG_V(INFO, TAG, "resource uri = %s", list->href);
+        createOCFResource(list->href);
+        list = list->next;
+        OICFree(temp);
+    }
+
+    addedThermostats[uri] = uriToNestThermostatMap[uri];
+    getTemperatureAndUpdateMap(addedThermostats[uri]);
+    free(pluginDetails);
+    return MPM_RESULT_OK;
+}
+
+/**
+ * Plugin specific entry-point function to stop the plugin's threads
+ *
+ * @param[in] pluginSpecificCtx            The plugin specific context created during plugin
+ *                                         create
+ * @return MPM_RESULT_OK if no errors, MPM_RESULT_INTERNAL_ERROR if stack process error
+ */
+MPMResult pluginStop(MPMPluginCtx *pluginSpecificCtx)
+{
+    MPMPluginCtx *ctx = pluginSpecificCtx;
+
+    if (ctx)
+    {
+        assert(g_ctx);
+        addedThermostats.clear();
+        uriToNestThermostatMap.clear();
+    }
+
+    return MPM_RESULT_OK;
+}
+
+/**
+ * Plugin specific entry-point function to allow the plugin resources to be
+ * freed.
+ *
+ * @param[in] pluginSpecificCtx            The plugin specific context created during plugin
+ *                                         create
+ * @return MPM_RESULT_OK if no errors, MPM_RESULT_INTERNAL_ERROR if stack process error
+ */
+MPMResult pluginDestroy(MPMPluginCtx *pluginSpecificCtx)
+{
+    MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+    MPMPluginCtx *ctx = pluginSpecificCtx;
+    if (ctx)
+    {
+        assert(g_ctx);
+        if(g_nest)
+        {
+            delete (g_nest);
+            g_nest = NULL;
+        }
+        OICFree(ctx);
+        g_ctx = NULL;
+        result = MPM_RESULT_OK;
+    }
+
+    OIC_LOG_V(INFO, TAG, "Plugin destroy's return value:%d", result);
+
+    return result;
+}
+
+NestThermostatSharedPtr getNestThermostatFromOCFResourceUri(std::string resourceUri)
+{
+    OIC_LOG_V(INFO, TAG, "Request for %s", resourceUri.c_str());
+
+    for (auto uriToNestPair : addedThermostats)
+    {
+        if (resourceUri.find(uriToNestPair.first) != std::string::npos)
+        {
+            return uriToNestPair.second;
+        }
+    }
+    throw std::runtime_error("Resource" + resourceUri + " not found");
+}
+
+OCRepPayload *addCommonNestProperties(const NestThermostatSharedPtr &t, OCRepPayload *payload)
+{
+    NestThermostat::THERMOSTAT data;
+    t->get(data);
+    OCRepPayloadSetPropString(payload, NEST_ID_TAG, data.devInfo.id.c_str());
+    OCRepPayloadSetPropString(payload, NEST_LAST_CONNECTION_TAG, data.devInfo.lastConnection.c_str());
+    return payload;
+}
+
+uint16_t getTemperatureAndUpdateMap(NestThermostatSharedPtr t)
+{
+    NestThermostat::THERMOSTAT targetThermostat;
+    t->get(targetThermostat);
+
+    std::vector<NestThermostatSharedPtr> thermostatsGot;
+    g_nest->getThermostats(thermostatsGot);
+
+    for (unsigned int i = 0; i < thermostatsGot.size(); ++i)
+    {
+        NestThermostat::THERMOSTAT fetchedThermostat;
+        thermostatsGot[i]->get(fetchedThermostat);
+        if (fetchedThermostat.devInfo.id == targetThermostat.devInfo.id)
+        {
+            if (targetThermostat.targetTempF != fetchedThermostat.targetTempF)
+            {
+                OIC_LOG(INFO, TAG, "temperature value has changed");
+                targetThermostat.targetTempF = fetchedThermostat.targetTempF;
+            }
+            break;
+        }
+    }
+
+    return targetThermostat.targetTempF;
+}
+
+OCRepPayload *getTemperaturePayload(NestThermostatSharedPtr t)
+{
+    std::unique_ptr<OCRepPayload, decltype(OCRepPayloadDestroy) *> payload {OCRepPayloadCreate(),
+            OCRepPayloadDestroy };
+
+    if (!payload)
+    {
+        throw std::runtime_error("payload cannot be NULL");
+    }
+
+    if (!OCRepPayloadSetPropDouble(payload.get(), TEMPERATURE_TAG,
+                                   (double) getTemperatureAndUpdateMap(t)))
+    {
+        throw std::runtime_error("failed to set temperature in the payload");
+    }
+
+    return addCommonNestProperties(t, payload.release());
+}
+
+OCRepPayload *processGetRequest(NestThermostatSharedPtr t)
+{
+    return getTemperaturePayload(t);
+}
+
+OCEntityHandlerResult processTemperatureUpdate(OCRepPayload *payload, NestThermostatSharedPtr t)
+{
+    double targetTemp = 0.0;
+    if (!OCRepPayloadGetPropDouble(payload, TEMPERATURE_TAG, &targetTemp))
+    {
+        throw std::runtime_error("Payload must contain \"temperature\"");
+    }
+    MPMResult result = g_nest->setAwayMode(Nest::eAWHome);
+    if (result == MPM_RESULT_OK)
+    {
+        result = t->setTemperature(targetTemp);
+
+        if (result != MPM_RESULT_OK)
+        {
+            throw std::runtime_error("Error setting temperature for PUT request");
+        }
+    }
+    else
+    {
+        throw std::runtime_error("Error setting away mode to home mode for PUT request");
+    }
+    return OC_EH_OK;
+}
+
+OCEntityHandlerResult processPutRequest(OCRepPayload *payload, NestThermostatSharedPtr t)
+{
+    if (!payload)
+    {
+        throw std::runtime_error("PUT payload cannot be NULL");
+    }
+
+    return processTemperatureUpdate(payload, t);
+}
+
+OCEntityHandlerResult resourceEntityHandlerCb(OCEntityHandlerFlag,
+        OCEntityHandlerRequest *entityHandlerRequest, void *)
+{
+    OCEntityHandlerResult result = OC_EH_OK;
+    MPMResult res = MPM_RESULT_OK;
+
+    try
+    {
+        std::string uri;
+        ConcurrentIotivityUtils::getUriFromHandle(entityHandlerRequest->resource, uri);
+
+        NestThermostatSharedPtr targetThermostat = getNestThermostatFromOCFResourceUri(uri);
+
+        switch (entityHandlerRequest->method)
+        {
+            case OC_REST_GET:
+                // Empty GET case as actual request will be processed after the switch case.
+                break;
+
+            case OC_REST_PUT:
+            case OC_REST_POST:
+
+                res = (MPMResult) processPutRequest((OCRepPayload *) entityHandlerRequest->payload,
+                                                    targetThermostat);
+                if (res != MPM_RESULT_OK)
+                    result = OC_EH_ERROR;
+                break;
+
+            default:
+                OIC_LOG_V(INFO, TAG, "Unsupported method (%d) recieved", entityHandlerRequest->method);
+                ConcurrentIotivityUtils::respondToRequestWithError(entityHandlerRequest,
+                        "Unsupported method received",
+                        OC_EH_METHOD_NOT_ALLOWED);
+                return OC_EH_OK;
+        }
+
+        OCRepPayload *responsePayload = processGetRequest(targetThermostat);
+        ConcurrentIotivityUtils::respondToRequest(entityHandlerRequest, responsePayload, result);
+        OCRepPayloadDestroy(responsePayload);
+    }
+    catch (const std::exception &exp)
+    {
+        ConcurrentIotivityUtils::respondToRequestWithError(entityHandlerRequest, exp.what(), OC_EH_ERROR);
+        result = OC_EH_OK;
+    }
+
+    return result;
+}