# 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'))
#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
--- /dev/null
+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.
--- /dev/null
+#******************************************************************
+#
+# 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')
--- /dev/null
+//******************************************************************
+//
+// 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;
+}
--- /dev/null
+//******************************************************************
+//
+// 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__ */
--- /dev/null
+//******************************************************************
+//
+// 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__ */
--- /dev/null
+//******************************************************************
+//
+// 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;
+}
--- /dev/null
+//******************************************************************
+//
+// 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__ */
--- /dev/null
+//******************************************************************
+//
+// 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;
+}