From 919ed43b138a02a007296ca5f1a7efbce54bb320 Mon Sep 17 00:00:00 2001 From: sudershan Date: Fri, 19 Aug 2016 14:42:59 +0530 Subject: [PATCH] added nest connector --- config/nest.json | 12 ++ nest/NEST-CLASS.py | 267 +++++++++++++++++++++++++++++++++++++++++++++ nest/__init__.py | 0 3 files changed, 279 insertions(+) create mode 100644 config/nest.json create mode 100644 nest/NEST-CLASS.py create mode 100644 nest/__init__.py diff --git a/config/nest.json b/config/nest.json new file mode 100644 index 0000000..590a0ab --- /dev/null +++ b/config/nest.json @@ -0,0 +1,12 @@ +{ + "credentials": { + "PIN": "NBCFUAG4", + "Product_id": "89d2d205-3297-419f-9405-9115c33e7881", + "Product_secret": "01XrOHyiXTLzgCX0e5VTcebGe", + "access_token": "c.Msbr1TYGaNit1p6qPdklCuAH4usOM64JWH8hgKrEIByEdOzpDyesA5JiEU12m4NmGSaJ0IOldRzcY6qdOieCX54bC51tqAcFGaSCQiPgLdbvudy5WvAjjSd01u4QKP5nUeJY0gbhbatD4og6" + }, + "url": { + "_AUTH_REQ": "https://api.home.nest.com/oauth2/access_token", + "_DEVICELIST_REQ": "https://developer-api.nest.com/devices?auth=" + } +} diff --git a/nest/NEST-CLASS.py b/nest/NEST-CLASS.py new file mode 100644 index 0000000..ce4b447 --- /dev/null +++ b/nest/NEST-CLASS.py @@ -0,0 +1,267 @@ +import json +import requests +from bd_connect.connect_bd import get_json +from config.setting import Setting +from pprint import pprint + +''' +About: +This module is a connector that connects any Nest device to the +Building DepotV3.1 with the help of the bd connect program. + +Configuration: +To be able to have the program access your Nest data, you have +to register your program as a Nest app in your Netatmo account at +https://develper.nest.com. You have to go on create new product, choose +the appropriate permissions and enter the required details in the +nest_config.json file. The pin code can be received by clicking on +the authorization url. + +''' + +class ClientAuth(object): + ''' + Class definition to get the access token, it does not need to be + refreshed + ''' + def __init__(self, credentials_file = "nest"): + ''' + Recieves the credentials from the config file and executes the + setup function to get the access token + + Args: + credentials_file: The json file containing the Product ID, Product + Secret, PIN and the access token (which is + obtained from the other information if not + present). It is in the config folder with the + default name "nest". + ''' + self.credentials_file = credentials_file + self.setup() + + def setup(self): + ''' + Obtains the access token if present and calls the access_token() + function if the access_token is not present, using the Product Id, + Product Secret, and PIN. + + Args as Data: + Product_id: #product id of the registered product + Product_secret: #product secret of the registered product + PIN: #generated by clicking on the authorization url, + must be updated after every use + access_token: #needed for access. If it is not present, the + other three args are used to generate it. + ''' + credentials = Setting(self.credentials_file) + self.urls = credentials.setting["url"] + self._accessToken = credentials.setting["credentials"]["access_token"] + # get the access token + + if (self._accessToken == ""): + # generate access token if it is not present + self.code, self.client_id, self.client_secret = ( + credentials.setting["credentials"]["PIN"], + credentials.setting["credentials"]["Product_id"], + credentials.setting["credentials"]["Product_secret"]) + self.access_token() + self.devices = dict() # generate an empty dictionary for all NEST + # devices linked to the account + + def access_token(self): + ''' + Update the access_token if it is not present in the config file + ''' + + url = self.urls["_AUTH_REQ"] + params = {"code": self.code, "client_id": self.client_id, + "client_secret": self.client_secret, + "grant_type": "authorization_code"} + + response = requests.post(url, params = params) + try: + self._accessToken = response.json()["access_token"] + except: + self._accessToken = None + print(response) + +class DeviceList(object): + ''' + Class definition of Nest to obtain the Nest device data and + update the data in the Building depot. + ''' + + def __init__(self, authData): + ''' + Initilize the auth token and obtain the device modules data + of NEST + Args as data: + "authData": class object of ClientAuth provides + access token. + ''' + self.getAuthToken = authData._accessToken + url = (authData.urls["_DEVICELIST_REQ"] + self.getAuthToken) + header = {"Authorization": "Bearer " + self.getAuthToken} + response = requests.get(url, headers = header, allow_redirects=True) + try: + self.devices = response.json() + except: + print(response) + + def deviceByType(self, deviceType = "smoke_co_alarms"): + ''' + Find the Nest data by device type (eg. thermostats, + smoke_co_alarms). If none given finds all the data of the each + Nest device connected to the given account + + Args as data: + "deviceType": device type (eg. thermostats, smoke_co_alarms) + ''' + if deviceType == None: return self.devices + try: + return self.devices[deviceType] + except: + print("No devices of such type") + return None + + def deviceByID(self, deviceType = "smoke_co_alarms", deviceID = None): + ''' + Find the Nest data by device ID, i.e. a specific device. + If none given finds all the data of the each + Nest device connected to the given account + + Args as data: + "deviceType": device type + "deviceID": unique ID to identify the device + ''' + if deviceType == None or deviceID == None: return self.devices + try: + return self.devices[deviceType][deviceID] + except: + print("Device ID does not exist or is not connected to this \ + account") + return None + + def deviceByName(self, deviceName = None): + ''' + Find the Nest data by device Name, i.e. the name give to + a specific Nest Device.If none given finds all the data of the each + Nest device connected to the given account. + + Nest has two names, name_long and name, we recommend using + name_long as multiple devices of different types can have the + same name. + + Args as data: + "deviceName": Name given to the specific device + ''' + if deviceName == None: return self.devices + for deviceType in self.devices: + typeList = self.devices[deviceType] + for deviceID in typeList: + deviceData = typeList[deviceID] + if (deviceData["name"] == deviceName or + deviceData["name_long"] == deviceName): + return deviceData + print("No devices with given name") + return None + + def get_device_data(self, deviceType = "smoke_co_alarms", + deviceID = None, deviceName = None): + ''' + Obtain the data of all NEST devices of a particular device in the + form of a dictionary. + + Args as data: + deviceType: the type of nest device (eg. thermostats, + smoke_co_alarms) + deviceID: unique ID of the device. The device type must be + that of the device. + deviceName: Name given to the device. + + Returns: + + { + u"co_alarm_state": #whether the state is ok or not. + + u"smoke_alarm_state": #whether the state is ok or not + + U"battery_health": #whether the state is ok or not + } + + ''' + + if (deviceID != None and deviceType != None): + data = self.deviceByID(deviceType, deviceID) + elif (deviceName != None): + data = self.deviceByName(deviceName) + else: + print("Please enter a valid deviceID or deviceName") + data = None + + if (data == None): + return + + nest_data = {} + nest_data["co_alarm_state"] = data["co_alarm_state"] + nest_data["smoke_alarm_state"] = data["smoke_alarm_state"] + nest_data["battery_health"] = data["battery_health"] + nest_data["mac_id"] = data["device_id"] + return nest_data + + def post_to_bd(self, deviceData): + """ + Post json object information to BD_Connect in this format + data={"sensor_data":{}} + + Args as data: + {} + Returns: + { + "success": "True" + "HTTP Error 400": "Bad Request" + } + """ + data = {'sensor_data': {}} + data['sensor_data'].update(deviceData) + response = get_json(json.dumps(data)) + return response + +if __name__ == "__main__": + """ + Reads the data from Nest Protect Devices and writes it to the building + depot. + + Returns: + { + "success": "True" + "HTTP Error 400": "Bad Request" + } + else + { + "Error in Device Connection" + } + """ + from sys import exit, stderr + + try: + auth = ClientAuth() # Get authentication key + if (auth._accessToken == None): + stderr.write("Please Enter the valid credentials to get\ + the access token") + exit(1) + + devList = DeviceList(auth) # Obtain the DEVICELIST + nest_data = devList.deviceByType() # Get the data for the type of + # devices + for device_id in nest_data: + try: + device_data = devList.get_device_data(deviceID = device_id) + resp = devList.post_to_bd(device_data) + # Send Data to the BuildingDepot + print "Response from connect_bd.py:\n", resp + except Exception as e: + print "Error in Sending data to connect_bd.py", e + + except Exception as e: + print "Error in Device Connection", e diff --git a/nest/__init__.py b/nest/__init__.py new file mode 100644 index 0000000..e69de29