diff --git a/Actuator/lifx_actuator.py b/Actuator/lifx_actuator.py deleted file mode 100644 index 3cd2f4d..0000000 --- a/Actuator/lifx_actuator.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -import re -import urllib2 -import requests -from urllib import urlencode -import warnings -import json -import time -import sys -from BD_connect.connect_bd import get_json -from config.setting import Setting -warnings.filterwarnings("ignore") - -''' -About: -This module is a connector that connects any Lifx Bulb to the -Building DepotV3.1 with the help of the bd connect program. - -Configuration: -To be able to have the program accessing and actuating your Lifx bulb data, -you have to register yourself at https://community.lifx.com/. After registering, -All you have to do is go to https://cloud.lifx.com/settings and click on -generate new token to give it a name and you will be provided a token through -you will be able to access lifx servers.Enter the information of your Lifx Device in the -json file in config folder. -''' -class DeviceList: - '''Class definition of lifx to send API calls to - the Lifx Bulb to perform various operations''' - - def __init__(self): - '''Initialises the Client token and obtains the raw_data - of the devices''' - lifx_cred=Setting("lifx") - self.getclienttoken=lifx_cred.setting["client_auth"]["_CLIENT_TOKEN"] - self._DEVICE_REQ=lifx_cred.get("url")["_DEVICE_REQ"] - - def switch(self,cmd): - '''Switch on/off the bulb - Args as data: - 'on' : switch on the bulb - 'off': switch off the bulb - ''' - payload ={"power": cmd} - print self.postRequest(self._DEVICE_REQ,payload) - - def switch_state(self,cmd): - '''Switch various states of the bulb - Args as data: - Eg: { - "power": # "On/Off" - "color": # "blue saturation:0.5", - "brightness": #0.5, - "duration": #5 - } - - ''' - payload =cmd - print self.postRequest(self._DEVICE_REQ,payload) - - def postRequest(self,url,params): - '''Posts the actuation requests to the lifx bulb - Args as data: - url : url of lifx data to be posted - params: Actuation parameters - Returns: - {u'results': - [ - {u'status': u'ok', - u'id': #Id of the lifx bulb - u'label': #Name of the Lifx bulb - } - ] - } - - ''' - headers = { - "Authorization": "Bearer %s" % self.getclienttoken - } - resp= requests.put(url,data=params,headers=headers).json() - return resp - -def main(args): - ''' Accepts the command to either read or actuate the Lifx Bulb. - - Args as Data: - 'on/off': Switch on/off the bulb - OR - '{ - on/off: Switch on/off the bulb - blue saturation:0.5 :: Provide color and - Saturation percent - brightness: Provide brightness percent - duration: Duration to be on in mins - }' - Returns: - If the args is to Actuate the Lifx bulb, then - {u'results': - [ - {u'status': u'ok', - u'id': #Id of the lifx bulb - u'label': #Name of the Lifx bulb - } - ] - } else - - {"Device Not Found/Error in fetching data"} - ''' - from sys import exit, stdout, stderr - devList=DeviceList() - - if not devList.getclienttoken: - stderr.write("Please Enter the Client token from Lifx in the config\ - file") - exit(1) - - try: - if len(args)==2: - devList.switch(args[1]) - elif len(args)>2 : - state={"power": args[1],"color": args[2]+" "+args[3], - "brightness": args[4],"duration": args[5]} - devList.switch_state(state) - - except Exception as e: - print "Device Not Found/Error in fetching data" - print e - exit(0) - -if __name__ == "__main__": - main(sys.argv) diff --git a/Actuator/wemo_actuator.py b/Actuator/wemo_actuator.py deleted file mode 100644 index a4ce362..0000000 --- a/Actuator/wemo_actuator.py +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -import re -import urllib2 -import json -import time -import sys -from BD_connect.connect_bd import get_json -from config.setting import Setting -""" -About: -This module is a connector that connects any wemo switch to the -Building DepotV3.1 with the help of the bd connect program - -Configuration: -Enter the local IP address of your WeMo Switch in the json file in config -folder.You may have to check your router to see what local IP is -assigned to the WeMo.It is recommended that you assign a static local IP to -the WeMo to ensure the WeMo is always at that address.To run the code type -"python sens_wemo.py " To Actuate the Wemo Switch -""" - -class wemo_actuator: - """Class defintion of wemo_actuator to send SOAP calls to - the Wemo Switch to perform various operations""" - OFF_STATE = '0' - ON_STATES = ['1', '8'] - ip = None - ports = [49153, 49152, 49154, 49151, 49155] - - def __init__(self): - """Initialises the IP of the Wemo Switch""" - wemo_cred=Setting("wemo") - self.ip = wemo_cred.setting["ip"] - - def toggle(self): - """Toggle the switch - Switch status is on then switch off else - if the switch staus is off switch on""" - status = self.status() - if status in self.ON_STATES: - result = self.off() - #result = 'WeMo is now off.' - elif status == self.OFF_STATE: - result = self.on() - #result = 'WeMo is now on.' - else: - raise Exception("UnexpectedStatusResponse") - return result - - def on(self): - """Switch on the wemo switch""" - return self._send('Set', 'BinaryState', 1) - - def off(self): - """Switch off the wemo switch""" - return self._send('Set', 'BinaryState', 0) - - def status(self): - """Get the status of switch""" - return self._send('Get', 'BinaryState') - - def name(self): - """Get the name of the switch""" - return self._send('Get', 'FriendlyName') - - def signal(self): - """Get the Signal Strength of the wemo switch""" - return self._send('Get', 'SignalStrength') - - def _get_header_xml(self, method, obj): - """Defines Header of SOAP request - Args as data: - method: Set/Get the state of Wemo - obj : object of the wemo state - Returns: - Header of the Soap Request - """ - method = method + obj - return '"urn:Belkin:service:basicevent:1#%s"' % method - - def _get_body_xml(self, method, obj, value=0): - """Defines Body of the SOAP request - Args as data: - method: Set/Get the state of Wemo - obj : object of the wemo state - Returns: - Body of the Soap Request - """ - method = method + obj - return ''\ - '<%s>%s'%(method, obj, value, obj, method) - - def _send(self, method, obj, value=None): - """ - Send request over various wemo ports and since we cannot - guarantee that wemo accepts request over a single port. - Args as data: - method: Set/Get the state of Wemo - obj : object of the wemo state - value : 0/1 to set the state - Returns: - { - "success": "True" - "HTTP Error 400": "Bad Request" - } - - """ - body_xml = self._get_body_xml(method, obj, value) - #print "body: ", body_xml - header_xml = self._get_header_xml(method, obj) - #print "Header",header_xml - - for port in self.ports: - result = self._try_send(self.ip, port,\ - body_xml, header_xml, obj) - if result is not None: - self.ports = [port] - return result - raise Exception("TimeoutOnAllPorts") - - - def _try_send(self, ip, port, body, header, data): - """ - Send the Request to the Wemo Switch - Args as data: - ip : ip address of the wemo - port: port on which wemo accepts request - header: header of the Soap request - body: body of the Soap request - data: 0/1 to switch on/off - - Returns: - { - Energy information or Status of wemo - based on the request made - or - "HTTP Error 400": "Bad Request" - - } - """ - try: - - url='http://%s:%s/upnp/control/basicevent1'\ - %(ip, port) - request = urllib2.Request(url) - print "request: ",request - request.add_header('Content-type',\ - 'text/xml; charset="utf-8"') - request.add_header('SOAPACTION', header) - request_body='' - request_body+='' - request_body+='%s'%body - request_body+='' - request.add_data(request_body) - result = urllib2.urlopen(request, timeout=3) - return self._extract(result.read(), data) - except Exception as e: - print str(e) - return None - - def _extract(self, response, name): - """Extract information from the response""" - exp = '<%s>(.*?)<\/%s>' % (name, name) - g = re.search(exp, response) - if g: - return g.group(1) - return response - -def output(message): - print message - -def main(arguments): - """ - Accept the command to actuate the Wemo Switch. - Returns: - If the args is to read energy data from Wemo - { - "success": "True" - "HTTP Error 400": "Bad Request" - } - If the args is to Actuate the Wemo Switch, then - {on/off : success} else - {"Device Not Found/Error in fetching data"} - """ - global status,data - cmd=arguments[1] - switch = wemo_actuator() - try: - #Uncomment any of these to actuate Wemo Switch - #output(switch.on()) - #output(switch.off()) - output(switch.toggle()) - #output(switch.status()) - except Exception as e: - print str(e) - -if __name__ == "__main__": - main(sys.argv) diff --git a/BD_connect/__init__.py b/BD_connect/__init__.py deleted file mode 100644 index 034bbe5..0000000 --- a/BD_connect/__init__.py +++ /dev/null @@ -1 +0,0 @@ -import connect_bd diff --git a/BD_connect/connect_bd.py b/BD_connect/connect_bd.py deleted file mode 100644 index 71acbd8..0000000 --- a/BD_connect/connect_bd.py +++ /dev/null @@ -1,213 +0,0 @@ -#!/usr/bin/python -import json -import requests -import time -import urllib2 -from config.setting import Setting -''' -About: -This module is a connector that connects various sensors to the Building DepotV3.1 -Configuration: -Enter the Url of the Building Depot(BD) where its being hosted. -This Program requires the sensor connector program to send its sensor data in the -JSON format data={ - "sensor_data":{ - - }, - "client_data":{ - - } - } -Working: -Writes Sensor data of the sensor to the Building Depot -Accept Actuation information for the sensor from Building Depot -''' - -'''Class object has access to all function to create new sensors and -update the sensor values.''' -class BD_connect: - - def __init__(self,data): - '''Initialises the Sensor data and client data of the sensors''' - BDcredentials = Setting("BD_setting") - self.data = BDcredentials.setting - self.sensor_data = json.loads(data)['sensor_data'] - self.data["mac_id"]=self.sensor_data["mac_id"] - self.sensor_data.pop("mac_id") - self.url=BDcredentials.setting["url"] - self.metadata=[] - self.common_data = [] - - def _get_oauth_token(self): - '''Obtains the oauth token of the user from Building Depot - - Returns: - Oauth token of the user - ''' - url=self.url+"/oauth/access_token/client_id="+self.data['client_id']+\ - "/client_secret="+self.data['client_key'] - response=requests.get(url).json() - self.oauth_token=response['access_token'] - self.header={"Authorization": "bearer " + self.oauth_token} - return self.oauth_token - - def _get_sensor_list(self,call_type): - '''Gets the sensor list from Building depot - - Args as data: - call_type: "all_sensors":- Fetches all the list of sensors - "None":- Fetches sensor list based on the metadata - Returns: - Sensor List - ''' - if call_type=='all_sensors': - url=self.url+"/api/list" - response=json.loads(requests.get(url,headers=self.header).text) - else: - url=self.url+"/api/mac_id="+call_type+"/metadata" - response=json.loads(requests.get(url,headers=self.header).text) - return response - - def check_sensor(self): - '''Verification of the device with valid mac_id - Returns: - True : if valid mac_id - False: if invalid mac_id - ''' - sensor_list=str(self._get_sensor_list('all_sensors')) - if self.data['mac_id'] in sensor_list: - return True - else: - return False - - def create_metadata(self): - '''Create common metadata for the sensor points.''' - temp={} - for key,val in self.sensor_data.iteritems(): - temp['name']=key - temp['value']=val - self.metadata.append(temp) - temp={} - - for key,val in self.data.iteritems(): - check=['client_id','client_key','device_id','name',\ - 'identifier','building'] - if key not in check: - temp['name']=key - temp['value']=val - self.common_data.append(temp) - temp={} - - self.metadata+=self.common_data - - def _add_meta_data(self,call_type,uuid): - '''Updates the meta data of the sensor wrt to the uuid - and sensor points - Args as data: - call_type: "rest_post" updates the meta data - "None" updates the meta data of the - specific sensor points - uuid : uuid of the sensor point to updated - - Returns: - { - "success": "True" - "HTTP Error 400": "Bad Request" - } - ''' - if call_type=="rest_post": - payload ={'data':self.metadata} - else: - dic={"name":call_type,"value":self.sensor_data[call_type]} - payload = {"data":self.common_data+[dic]} - headers=self.header - headers['content-type']='application/json' - url=self.url+'/api/sensor/'+uuid+'/metadata' - return requests.post(url,data=json.dumps(payload),\ - headers=headers).json() - - def _create_sensor_points(self): - """ Create sensor points of a particular sensor and calls - _add_meta_data() to add the metadata values - Returns: - { - "success": "True" - "HTTP Error 400": "Bad Request" - } - """ - identifier=self.data['identifier'] - building=self.data['building'] - name=self.data['name'] - response='' - for key,val in self.sensor_data.iteritems(): - url=self.url+'/api/sensor_create/name=%s/identifier=%s/building=%s'\ - %(key+'_'+name,identifier,building) - iresponse=requests.post(url,headers=self.header).json() - temp=json.dumps(self._add_meta_data(key,iresponse['uuid'])) - response=response+temp - response=response+"\n"+key+' :'+json.dumps(iresponse) - return response - - def rest_post(self,mac_id): - '''Get the data of the sensor and calls _add_meta_data() - to add the metadata values - Returns: - { - "success": "True" - "HTTP Error 400": "Bad Request" - } - ''' - response='' - sensor_list=self._get_sensor_list(self.data['mac_id'])['data'] - #obtain the uuid of the sensor_points of the sensor and update - for key1,val1 in sensor_list.iteritems(): - for key,val in self.sensor_data.iteritems(): - if(key+'_'+self.data['name']==sensor_list[key1]["source_name"]): - uuid = sensor_list[key1]["name"] - response=self._add_meta_data(key,uuid) - return "Metadata updated\n",response - - -def get_json(data): - ''' - Function obtains a json object from Sensor connector in the format - Args as data: - { - "sensor_data":{ - - }, - "client_data":{ - - } - } - - Returns: - { - "success": "True" - "HTTP Error 400": "Bad Request" - } - ''' - info = BD_connect(data) - #Check Valid Client id and Client key - if 'Invalid credentials' not in info._get_oauth_token(): - #Client_id and details are correct and token Generate access token - print'Client id Verified' - '''Function to create the meta data to be updated/created - on Building Depot''' - info.create_metadata() - check=('name','identifier','building') - if (set(check).issubset(info.data)): - '''Check if UUID is given, if so update metadata else - create a new sensor and add metadata''' - if not info.check_sensor(): - return info._create_sensor_points() - else: - return info.rest_post(info.data['mac_id']) - else: - return '''Provide valid source_name,source_identifier, - email and building values''' - - else: - return 'Please Enter correct client_id/client_key Details' - - return True diff --git a/Sensor/sens_lifx.py b/Sensor/sens_lifx.py deleted file mode 100644 index 172ba6b..0000000 --- a/Sensor/sens_lifx.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -import re -import urllib2 -import requests -from urllib import urlencode -import warnings -import json -import time -import sys -from BD_connect.connect_bd import get_json -from config.setting import Setting -warnings.filterwarnings("ignore") -''' -About: -This module is a connector that connects any Lifx Bulb to the -Building DepotV3.1 with the help of the bd connect program. - -Configuration: -To be able to have the program accessing and reading your Lifx bulb data, -you have to register yourself at https://community.lifx.com/. After registering, -All you have to do is go to https://cloud.lifx.com/settings and click on -generate new token to give it a name and you will be provided a token through -you will be able to access lifx servers.Enter the information of your -Lifx Device in the json file in config folder. -''' -class DeviceList: - '''Class definition of lifx to send API calls to - the Lifx Bulb to perform various operations''' - - def __init__(self): - '''Initialises the Client token and obtains the raw_data - of the devices''' - lifx_cred=Setting("lifx") - self.getclienttoken=lifx_cred.setting["client_auth"]["_CLIENT_TOKEN"] - self.rawData=self.getRequest(lifx_cred.get("url")["_DEVICELIST_REQ"]) - - def stationData(self): - ''' - Obtains the Lifx bulb data and writes them to the Building depot - Returns: - { - "success": "True" - "HTTP Error 400": "Bad Request" - } - ''' - global mac_id - for device in self.rawData: - lifx_data={} - mac_id=device["id"] - name=device["label"].replace(" ","_") - for k in device["color"].keys(): - lifx_data[k+"_"+name]=device["color"][k] - lifx_data["brightness_"+name]=device["brightness"] - lifx_data["status_"+name]=device["power"] - return self.post_BD(lifx_data) - - def post_BD(self,stationdata): - ''' - Format of the JSON Object to be sent to bd_connect.py. - sensor_data contains all information from sensor to be - Read and sensor_points to be created in accordance with that. - client_data contains all information about client_id,client_keyetc - for authentication - data={"sensor_data":{}} - Args as data: - {,, etc.} - Returns: - { - "success": "True" - "HTTP Error 400": "Bad Request" - } - ''' - global mac_id - print stationdata - data={'sensor_data':{}} - data['sensor_data'].update(stationdata) - data['sensor_data'].update({"mac_id":mac_id}) - try: - resp=get_json(json.dumps(data)) - except Exception as e: - print e - return resp - - def getRequest(self,url): - ''' - Get Status of the Bulb and the device list of Lifx bulbs - Args as data: - url : Url of lifx bulb - Returns: - Lifx bulb device list and status - - ''' - headers = { - "Authorization": "Bearer %s" % self.getclienttoken - } - resp= requests.get(url,headers=headers).json() - return resp - -def main(args): - ''' Accepts the command to either read or actuate the Lifx Bulb. - - Args as Data: - '': Empty to Write data to the Building depot - Returns: - If the No args read status and write data - to building depot - { - "success": "True" - "HTTP Error 400": "Bad Request" - } else - - {"Device Not Found/Error in fetching data"} - ''' - from sys import exit, stdout, stderr - devList=DeviceList() - - if not devList.getclienttoken: - stderr.write("Please Enter the Client token from Lifx in\ - the config file") - exit(1) - - try: - resp=devList.stationData() #Get the Lifx Bulbs data and updates the BD - print resp - - except Exception as e: - print "Device Not Found/Error in fetching data" - exit(0) - - -if __name__ == "__main__": - main(sys.argv) diff --git a/Sensor/sens_mother.py b/Sensor/sens_mother.py deleted file mode 100644 index 668dfa2..0000000 --- a/Sensor/sens_mother.py +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -import pip -import json, time -from urllib import urlencode -import urllib2 -import requests -import datetime -from BD_connect.connect_bd import get_json -from config.setting import Setting -''' -About: -This module is a connector that connects any Sen.se Mother Device to the -Building DepotV3.1 with the help of the bd connect program. - -Configuration: -To be able to have the program accessing your Sen.se Mother data, you have -to register yourself in the Sen.se's Developer website with an email and -a password.To obtain an access to the Sen.se APIs you have to register at -https://sen.se/developers/ and fill up the form. Once you get a mail from -sen.se regarding your access to their Api(which may take a week) regarding -providing access to their servers you can start implementing the codes below. -Enter the information of your Netatmo Device in the json file in config folder. -''' - - -#BuildingDepot Specific Definitions -global mac_id -mac_id='00:04:A3:FF:BA:C0' - -class ClientAuth: - '''Class defintion of Sen.se Mother to request authentication and - keep access token available through token method.''' - def __init__(self): - mother_cred=Setting("mother") - self.username=mother_cred.setting['client_auth']['_USERNAME'] - self.password=mother_cred.setting['client_auth']['_PASSWORD'] - params= {'username': str(self.username),'password': str(self.password)} - params={'username': 'sud335', 'password': '469321#'} - self._AUTH_REQ=mother_cred.get("url")["_AUTH_REQ"] - self._NODE_REQ=mother_cred.get("url")["_NODE_REQ"] - resp= requests.post(self._AUTH_REQ,params).json() - if 'token' in resp: - self.api_token=resp["token"] - else: - print self.api_token,"Error" - exit(1) - -#Class for Client authentication and access token -class DeviceList: - ''' - Class defintion of Sen.se mother to request for the Device List , - obtain the cookie data and send itto the Building Depot so as to - update the metadata - ''' - def __init__(self,authData): - ''' - Initilize the auth token and obtain the cookie data of Sen.se Mother - Args as data: - "authData": class object of ClientAuth provides - access token. - ''' - self.getAuthToken = authData.api_token - params = {'resource__type': 'device', 'resource__slug': 'cookie'} - resp = self.getRequest(authData._NODE_REQ,params) - self.rawData = resp["objects"] - self.cookie_feeds={} - for cookie in self.rawData: - cok_id=cookie["label"]+"#"+cookie["uid"] - self.cookie_feeds[cok_id]=cookie["publishes"] - self.cookie_event_urls={} - #obtains the cookie feeds of the Sen.se mother - for key,value in self.cookie_feeds.items(): - self.cookie_event_urls[key]=[] - for app in value: - self.cookie_event_urls[key].append(app["url"]+"events/") - #If none of the apps for the respective cookies are not activated - if not self.cookie_event_urls[key]: - self.cookie_event_urls.pop(key,None) - - def getCookieAppData(self,eventdata,device): - ''' Obtain each Sen.se mother cookie's app data and return them - Args as data: - "eventdata": #The event information of each app - installed in the Sen.se mother - cookie - "device": #Device name of the Sen.se mother cookie - Returns: - Data readings of the cookie for the specific app - - ''' - print "DATA :",eventdata["type"] - for key,value in eventdata["data"].items(): - reading=device+"_"+eventdata["type"]+"_"+key - eventdata["data"][reading]=eventdata["data"].pop(key) - print eventdata["data"] - return eventdata["data"] - - def getMotherData(self): - '''Obtain the all app data of all cookie connected to the mother and get - each app's data installed on the cookie - Returns: - A json which consists all the Sen.se mothers cookie - recent data - ''' - app_data={} - for key,value in self.cookie_event_urls.items(): - device_name=key.split("#")[0] - print "Fetching:",key.split("#")[0],"'s data" - for url in value: - #Get the data of the apps installed on the cookie - latest_reading=self.getRequest(url)["objects"][0] - app_data.update(self.getCookieAppData(latest_reading,device_name)) - return app_data - - def getRequest(self,url,params=None): - ''' - Get Device List for Sen.se Mother and its cookies - Args as data: - url : Url of Sen.se mothers cookies data - params : cookie specific parameters - Returns: - Sen.se mothers cookie and app data - - ''' - header = {'Authorization': \ - u' Token '+self.getAuthToken} - resp = requests.get(url,headers=header,params=params).json() - return resp - - #Post Values to Building Depot. - def post_BD(self,mother_data): - ''' - Format of the JSON Object to be sent to bd_connect.py. - sensor_data contains all information from sensor to be Read - and sensor_points to be created in accordance with that. - client_data contains all information about client_id,client_key etc - for authentication - data={"sensor_data":{}} - - Args as data: - {} - Returns: - { - "success": "True" - "HTTP Error 400": "Bad Request" - } - ''' - data={'sensor_data':{}} - data['sensor_data'].update(mother_data) - data['sensor_data'].update({"mac_id":mac_id}) - resp=get_json(json.dumps(data)) - return resp - - -if __name__=='__main__': - - ''' - Reads the Sen.se mothers cookie data and writes it to the building depot. - - Returns: - Reads the data from Sen.se mother cookies - { - "success": "True" - "HTTP Error 400": "Bad Request" - } - else - {"Error in Device Connection"} - - - ''' - from sys import exit, stdout, stderr - try: - auth=ClientAuth() - if not auth.username or not auth.password: - stderr.write("Please Enter the Sen.se Mothers username and password") - exit(1) - devList = DeviceList(auth) #Obtain the Device List - mother_data = devList.getMotherData() #Get the Mother Sensor data - - try: - resp=devList.post_BD(mother_data) #Send Data to the BuildingDepot - print 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/Sensor/sens_netatmo.py b/Sensor/sens_netatmo.py deleted file mode 100644 index 4f65d97..0000000 --- a/Sensor/sens_netatmo.py +++ /dev/null @@ -1,266 +0,0 @@ -import json, time -from urllib import urlencode -import urllib2 -from BD_connect.connect_bd import get_json -from config.setting import Setting -""" -About: -This module is a connector that connects any Netatmo Weather station to the -Building DepotV3.1 with the help of the bd connect program. - -Configuration: -To be able to have the program accessing your netatmo data, you have -to register your program as a Netatmo app in your Netatmo account at -https://auth.netatmo.com/en-US/access/signup. All you have -to do is to give it a name and you will be returned a client_id and -secret that your app has to supply to access netatmo servers.Enter the -information of your Netatmo Device in the json file in config folder. -""" - -class ClientAuth: - """ - Class definition of Netatmo to request authentication and keep access - token available through token method.Renew it automatically if necessary - """ - def __init__(self): - """ - Initialises the Client Id and Client key and obtains the Access - token of the netatmo device - - Args as Data: - clientid: #netatmo id of the user - clientsecret: #netatmo secret key of the user - username: #username used in netatmo login - password: #password used in netatmo login - """ - Netatmo_cred=Setting("netatmo") - self.clientId=Netatmo_cred.setting['client_auth']['_CLIENT_ID'] - self.clientSecret=Netatmo_cred.setting['client_auth']['_CLIENT_SECRET'] - self.username=Netatmo_cred.setting['client_auth']['_USERNAME'] - self.password=Netatmo_cred.setting['client_auth']['_PASSWORD'] - postParams = { - "grant_type" : "password", - "client_id" : self.clientId, - "client_secret" : self.clientSecret, - "username" : self.username, - "password" : self.password, - "scope" : "read_station" - } - _AUTH_REQ=Netatmo_cred.get("url")["_AUTH_REQ"] - resp = getRequest(_AUTH_REQ, postParams) - self._clientId = self.clientId - self._clientSecret = self.clientSecret - self._accessToken = resp['access_token'] - self.refreshToken = resp['refresh_token'] - self._scope = resp['scope'] - self.expiration = int(resp['expire_in'] + time.time()) - - @property - def accessToken(self): - """ - Renew the access token if the token is expired - - Returns: - New Access token - """ - if self.expiration < time.time(): # Token should be renewed - - postParams = { - "grant_type" : "refresh_token", - "refresh_token" : self.refreshToken, - "client_id" : self._clientId, - "client_secret" : self._clientSecret - } - resp = getRequest(_AUTH_REQ, postParams) - - self._accessToken = resp['access_token'] - self.refreshToken = resp['refresh_token'] - self.expiration = int(resp['expire_in'] + time.time()) - - return self._accessToken - -class DeviceList: - """ - Class definition of Netatmo to obtain the Natatmo station data and - update the data in the Building depot. - - """ - - def __init__(self, authData): - """ - Initilize the auth token and obtain the device modules data - of Netatmo - Args as data: - "authData": class object of ClientAuth provides - access token. - - """ - self.getAuthToken = authData.accessToken - postParams = { - "access_token" : self.getAuthToken, - "app_type" : "app_station" - } - url_cred=Setting("netatmo") - _DEVICELIST_REQ=url_cred.get("url")["_DEVICELIST_REQ"] - resp = getRequest(_DEVICELIST_REQ, postParams) - #print "\nDeviceList: ",resp - self.rawData = resp['body'] - self.stations = { d['_id'] : d for d in self.rawData['devices'] } - #print "\nStations: ",self.stations - self.modules = { m['_id'] : m for m in self.rawData['modules'] } - #print "\nModules: ",self.modules - self.default_station = list(self.stations.values())[0]['station_name'] - #print "\nSelf Station: ",self.default_station - - def stationByName(self, station=None): - """ - Find the Netatmo station data by name if none given finds all the - data of the netatmo stations - Args as data: - "station": #Station name - """ - if not station : station = self.default_station - for i,s in self.stations.items(): - if s['station_name'] == station : return self.stations[i] - return None - - def getModuleData(self, module_id): - """ - Obtain the Netatmo outdoor module data - Args as data: - "module_id": #module_id of the outdoor module - Returns: - { - u'min_temp_NAModule1': #minimum temperature of - outdoor module, - u'Humidity_NAModule1': #Humidity of outdoor module, - u'Temperature_NAModule1': #current temp of outdoor - module, - u'max_temp_NAModule1': #maximum temperature of - outdoor module - } - """ - module_data=self.modules[module_id]['dashboard_data'] - for k in ('date_max_temp','date_min_temp','time_utc'): - module_data.pop(k,None) - for i in module_data.keys(): - module_data[i+"_"+self.modules[module_id]['type']]=module_data.pop(i) - return module_data - - - def getStationsData(self,station=None): - """ - Obtain the Netatmo indoor module data and form common dict of - all the module data - - Args as data: - "station": #station name of the Base station - Returns: - { - u'Noise_indoor': #noise in decibels - of indoor module, - u'max_temp_indoor': #maximum temperature of - indoor module, - u'Temperature_indoor': #current temp of indoor - module, - u'Pressure_indoor': #pressure in pascal of - indoor module, - u'Humidity_indoor': #humidity of the indoor - module, - u'CO2_indoor': #Co2 levels of indoor - module, - u'min_temp_indoor': #minimum temperature of - indoor module, - u'AbsolutePressure_indoor': #absolute pressure - of indoor module - } - """ - if not station: - station = self.default_station - s = self.stationByName(station) - global mac_id - #obtain the Netatmo indoor main module data - netatmo_data=s['dashboard_data'] - mac_id=s['_id'] - for k in ('date_max_temp','date_min_temp','time_utc'): - netatmo_data.pop(k,None) - for i in netatmo_data.keys(): - netatmo_data[i+"_indoor"]=netatmo_data.pop(i) - - #obtain the Netatmo other outdoor module data - module_data={} - for n in s['modules']: - module_data.update(self.getModuleData(n)) - - netatmo_data.update(module_data) - return netatmo_data - - def post_BD(self,stationdata): - """ - 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(stationdata) - data['sensor_data'].update({"mac_id":mac_id}) - resp=get_json(json.dumps(data)) - return resp - - -def getRequest(url, params): - """ - Get the Netatmo station data - Args as data: - url : Url of Netatmo station data - Returns: - Netatmo device and module list - - """ - - params = urlencode(params) - headers = {"Content-Type":"application/x-www-form-urlencoded;charset=utf-8"} - req = urllib2.Request(url=url, data=params, headers=headers) - resp = urllib2.urlopen(req).read() - return json.loads(resp) - -if __name__ == "__main__": - """ - Reads the netatmo station data and writes it to the building depot. - - Returns: - Reads the data from Netatmo station - { - "success": "True" - "HTTP Error 400": "Bad Request" - } - else - {"Error in Device Connection"} - """ - from sys import exit, stdout, stderr - - try: - auth = ClientAuth() #Get authentication key - if not auth.clientId or not auth.clientSecret or not auth.username or not auth.password : - stderr.write("Please Enter the Netatmo Clientid and ClientSecret") - exit(1) - - devList = DeviceList(auth) #Obtain the DEVICELIST - netatmo_data= devList.getStationsData() #Get the Stations data - try: - resp=devList.post_BD(netatmo_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 - - exit(0) diff --git a/Sensor/sens_wemo.py b/Sensor/sens_wemo.py deleted file mode 100644 index a08b629..0000000 --- a/Sensor/sens_wemo.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -import re -import urllib2 -import json -import time -import sys -from BD_connect.connect_bd import get_json -from config.setting import Setting -''' -About: -This module is a connector that connects any wemo switch to the -Building DepotV3.1 with the help of the bd connect program - -Configuration: -Enter the local IP address of your WeMo Switch in the json file in config -folder.You may have to check your router to see what local IP is -assigned to the WeMo.It is recommended that you assign a static local IP to -the WeMo to ensure the WeMo is always at that address.To run the code type -"python sens_wemo.py". -''' -class wemo_sense: - '''Class defintion of wemo_sense to send SOAP calls to - the Wemo Switch to perform various operations''' - ports = [49153, 49152, 49154, 49151, 49155] - - def __init__(self): - """Initialises the IP of the Wemo Switch""" - wemo_cred=Setting("wemo") - self.ip = wemo_cred.setting["ip"] - - def energy_info(self): - '''Obtains the energy info from the wemo which is in the format - 1|1457|2487|9042|34794|21824|29|293|424|137.0|8000 - 1. State (0 = off, 1 = on) - 2. Average power (W) - 3. Instantaneous power (mW) - 4. Energy used today in mW-minutes - 5. Energy used over past two weeks in mW-minutes. ''' - - res=self._send('Get','energy').split('|') - edata={'status':res[0],'avg_power':res[7], - 'instantaneous_power':res[8],'avg_energy_today':res[9], - 'avg_energy_2weeks':res[10]} - return edata - - def _send(self, method, obj, value=None): - ''' - Send request over various wemo ports and since we cannot - guarantee that wemo accepts request over a single port. - Args as data: - method: Set/Get the state of Wemo - obj : object of the wemo state - value : 0/1 to set the state - Returns: - { - "success": "True" - "HTTP Error 400": "Bad Request" - } - - ''' - - if obj=='energy': - '''Headers and body of xml to obtain device - energy information''' - body_xml=''' - - - - - - ''' - header_xml={ - "Accept":"", - "Content-Type": "text/xml; charset=\"utf-8\"", - "SOAPACTION": "\"urn:Belkin:service:insight:1\ - #GetInsightParams\"" - } - - for port in self.ports: - result = self._try_send(self.ip, port,\ - body_xml, header_xml, obj) - if result is not None: - self.ports = [port] - return result - raise Exception("TimeoutOnAllPorts") - - - def _try_send(self, ip, port, body, header, data): - ''' - Send the Request to the Wemo Switch - Args as data: - ip : ip address of the wemo - port: port on which wemo accepts request - header: header of the Soap request - body: body of the Soap request - data: 0/1 to switch on/off - - Returns: - { - Energy information or Status of wemo - based on the request made - or - "HTTP Error 400": "Bad Request" - - } - ''' - try: - url='http://%s:%s/upnp/control/insight1'\ - % (ip, port) - request = urllib2.Request(url,body,header) - print "request: ",request - data= urllib2.urlopen(request,None,5).read() - regex=r'\(.*)\' - params = re.search(regex,data).group(1) - return params - - except Exception as e: - print str(e) - return None - - def _extract(self, response, name): - '''Extract information from the response''' - exp = '<%s>(.*?)<\/%s>' % (name, name) - g = re.search(exp, response) - if g: - return g.group(1) - return response - -def get_devicedata(): - '''Get the device data information uncomment any of these to make - work if device is connected retrieve device information in the - format"status=switch.energyinfo()" - ''' - wemo_cred=Setting("wemo") - mac_id = wemo_cred.setting["mac_id"] - switch = wemo_sense() - edata=switch.energy_info() - data={'sensor_data':{}} - data['sensor_data'].update(edata) - data['sensor_data'].update({"mac_id":mac_id}) - -def main(arguments): - ''' - Accept the command to either read or actuate the Wemo Switch. - - Returns: - If the args is to read energy data from Wemo - { - "success": "True" - "HTTP Error 400": "Bad Request" - } - If the args is to Actuate the Wemo Switch, then - {on/off : success} else - {"Device Not Found/Error in fetching data"} - ''' - global status,data - cmd=arguments[1] - switch = wemo_sense() - try: - get_devicedata() - except Exception as e: - print "Device Not Found/Error in fetching data" - exit(0) - - '''Posts json object information to BD_Connect in this format - data={"sensor_data":{}, - "client_data":{}}''' - try: - print get_json(json.dumps(data)) - print "Response from bd_connnect.py" - except Exception as e: - print e - -if __name__ == "__main__": - main(sys.argv) diff --git a/Actuator/__init__.py b/__init__.py similarity index 100% rename from Actuator/__init__.py rename to __init__.py diff --git a/Sensor/__init__.py b/bd_connect/__init__.py similarity index 100% rename from Sensor/__init__.py rename to bd_connect/__init__.py diff --git a/bd_connect/connect_bd.py b/bd_connect/connect_bd.py new file mode 100644 index 0000000..07ed04a --- /dev/null +++ b/bd_connect/connect_bd.py @@ -0,0 +1,207 @@ +#!/usr/bin/python +import json +import requests +from config.setting import Setting + +""" +About: +This module is a connector that connects various sensors to the Building DepotV3.1 +Configuration: +Enter the Url of the Building Depot(BD) where its being hosted. +This Program requires the sensor connector program to send its sensor data in the +JSON format +data={ +"sensor_data": +{ + + } +} +Working: +Writes sensor data of the sensor to the Building Depot +Accept Actuation information for the sensor from Building Depot +""" + +"""Class object has access to all function to create new sensors and +update the sensor values.""" + + +class BdConnect: + def __init__(self, data): + """Initialises the sensor data and client data of the sensors""" + bdcredentials = Setting("bd_setting") + self.data = bdcredentials.setting + self.sensor_data = json.loads(data)['sensor_data'] + self.data["mac_id"] = self.sensor_data["mac_id"] + self.sensor_data.pop("mac_id") + self.url = bdcredentials.setting["url"] + self.metadata = [] + self.common_data = [] + + def get_oauth_token(self): + """Obtains the oauth token of the user from Building Depot + + Returns: + Oauth token of the user + """ + url = self.url + "/oauth/access_token/client_id=" + self.data['client_id'] + \ + "/client_secret=" + self.data['client_key'] + response = requests.get(url).json() + self.oauth_token = response['access_token'] + self.header = {"Authorization": "bearer " + self.oauth_token} + return self.oauth_token + + def get_sensor_list(self, call_type): + """Gets the sensor list from Building depot + + Args : + call_type: "all_sensors":- Fetches all the list of sensors + "None":- Fetches sensor list based on the metadata + Returns: + sensor List + """ + if call_type == 'all_sensors': + url = self.url + "/api/list" + response = json.loads(requests.get(url, headers=self.header).text) + else: + url = self.url + "/api/mac_id=" + call_type + "/metadata" + response = json.loads(requests.get(url, headers=self.header).text) + return response + + def check_sensor(self): + """Verification of the device with valid mac_id + Returns: + True : if valid mac_id + False: if invalid mac_id + """ + sensor_list = str(self.get_sensor_list('all_sensors')) + if self.data['mac_id'] in sensor_list: + return True + else: + return False + + def create_metadata(self): + """Create common metadata for the sensor points.""" + temp = {} + for key, val in self.sensor_data.iteritems(): + temp['name'] = key + temp['value'] = val + self.metadata.append(temp) + temp = {} + + for key, val in self.data.iteritems(): + check = ['client_id', 'client_key', 'device_id', 'name', \ + 'identifier', 'building'] + if key not in check: + temp['name'] = key + temp['value'] = val + self.common_data.append(temp) + temp = {} + + self.metadata += self.common_data + + def _add_meta_data(self, call_type, uuid): + """Updates the meta data of the sensor wrt to the uuid + and sensor points + Args : + call_type: "rest_post" updates the meta data + "None" updates the meta data of the + specific sensor points + uuid : uuid of the sensor point to updated + + Returns: + { + "success": "True" + "HTTP Error 400": "Bad Request" + } + """ + if call_type == "rest_post": + payload = {'data': self.metadata} + else: + dic = {"name": call_type, "value": self.sensor_data[call_type]} + payload = {"data": self.common_data + [dic]} + headers = self.header + headers['content-type'] = 'application/json' + url = self.url + '/api/sensor/' + uuid + '/metadata' + return requests.post(url, data=json.dumps(payload), \ + headers=headers).json() + + def create_sensor_points(self): + """ Create sensor points of a particular sensor and calls + _add_meta_data() to add the metadata values + Returns: + { + "success": "True" + "HTTP Error 400": "Bad Request" + } + """ + identifier = self.data['identifier'] + building = self.data['building'] + name = self.data['name'] + response = '' + for key, val in self.sensor_data.iteritems(): + url = self.url + '/api/sensor_create/name=%s/identifier=%s/building=%s' \ + % (key + '_' + name, identifier, building) + iresponse = requests.post(url, headers=self.header).json() + temp = json.dumps(self._add_meta_data(key, iresponse['uuid'])) + response = response + temp + response = response + "\n" + key + ' :' + json.dumps(iresponse) + return response + + def rest_post(self): + """Get the data of the sensor and calls _add_meta_data() + to add the metadata values + Returns: + { + "success": "True" + "HTTP Error 400": "Bad Request" + } + """ + response = '' + sensor_list = self.get_sensor_list(self.data['mac_id'])['data'] + # obtain the uuid of the sensor_points of the sensor and update + for key1, val1 in sensor_list.iteritems(): + for key, val in self.sensor_data.iteritems(): + if key + '_' + self.data['name'] == sensor_list[key1]["source_name"]: + uuid = sensor_list[key1]["name"] + response = self._add_meta_data(key, uuid) + return "Metadata updated\n", response + + +def get_json(data): + """ + Function obtains a json object from sensor connector in the format + Args : + { + "sensor_data":{ + + } + } + + Returns: + { + "success": "True" + "HTTP Error 400": "Bad Request" + } + """ + info = BdConnect(data) + # Check Valid Client id and Client key + if 'Invalid credentials' not in info.get_oauth_token(): + # Client_id and details are correct and token Generate access token + print'Client id Verified' + """Function to create the meta data to be updated/created + on Building Depot""" + info.create_metadata() + check = ('name', 'identifier', 'building') + if set(check).issubset(info.data): + """Check if UUID is given, if so update metadata else + create a new sensor and add metadata""" + if not info.check_sensor(): + return info.create_sensor_points() + else: + return info.rest_post() + else: + return """Provide valid source_name,source_identifier, + email and building values""" + + else: + return 'Please Enter correct client_id/client_key Details' \ No newline at end of file diff --git a/config/BD_setting.json b/config/bd_setting.json similarity index 100% rename from config/BD_setting.json rename to config/bd_setting.json diff --git a/config/setting.py b/config/setting.py index 8adb979..02ac1b4 100644 --- a/config/setting.py +++ b/config/setting.py @@ -1,10 +1,10 @@ import json + + class Setting: - def __init__(self, settingFileName): - settingFilePath="../config/"+settingFileName+".json" - self.setting = json.loads(open(settingFilePath,'r').read()) - - def get(self,settingName): - return self.setting[settingName] -test =Setting("netatmo") -print test.setting + def __init__(self, filename): + settingfilepath = "../config/" + filename + ".json" + self.setting = json.loads(open(settingfilepath, 'r').read()) + + def get(self, settingname): + return self.setting[settingname] diff --git a/lifx/__init__.py b/lifx/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lifx/sens_lifx.py b/lifx/sens_lifx.py new file mode 100644 index 0000000..fcaa69b --- /dev/null +++ b/lifx/sens_lifx.py @@ -0,0 +1,224 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +import requests +import warnings +import json +import sys +from bd_connect.connect_bd import get_json +from config.setting import Setting + +warnings.filterwarnings("ignore") +""" +About: +This module is a connector that connects any Lifx Bulb to the +Building DepotV3.1 with the help of the bd connect program. + +Configuration: +To be able to have the program accessing,reading and actuating your Lifx bulb data, +you have to register yourself at https://community.lifx.com/. After registering, +All you have to do is go to https://cloud.lifx.com/settings and click on +generate new token to give it a name and you will be provided a token through +you will be able to access lifx servers.Enter the information of your +Lifx Device in the json file in config folder. +""" + + +class LifxActuator: + """Class definition of lifx to send API calls to + the Lifx Bulb to perform various operations""" + + def __init__(self): + """Initialises the Client token and obtains the raw_data + of the devices""" + lifx_cred = Setting("lifx") + self.getclienttoken = lifx_cred.setting["client_auth"]["_CLIENT_TOKEN"] + self._DEVICE_REQ = lifx_cred.get("url")["_DEVICE_REQ"] + + def switch(self, cmd): + """Switch on/off the bulb + Args : + 'on' : switch on the bulb + 'off': switch off the bulb + """ + payload = {"power": cmd} + print self.post_request(self._DEVICE_REQ, payload) + + def switch_state(self, cmd): + """Switch various states of the bulb + Args : + Eg: { + "power": # "On/Off" + "color": # "blue saturation:0.5", + "brightness": #0.5, + "duration": #5 + } + + """ + payload = cmd + print self.post_request(self._DEVICE_REQ, payload) + + def post_request(self, url, params): + """Posts the actuation requests to the lifx bulb + Args : + url : url of lifx data to be posted + params: Actuation parameters + Returns: + {u'results': + [ + {u'status': u'ok', + u'id': #Id of the lifx bulb + u'label': #Name of the Lifx bulb + } + ] + } + + """ + headers = { + "Authorization": "Bearer %s" % self.getclienttoken + } + resp = requests.put(url, data=params, headers=headers).json() + return resp + + +class LifxSensor: + """Class definition of lifx to send API calls to + the Lifx Bulb to perform various operations + """ + + def __init__(self): + """Initialises the Client token and obtains the raw_data + of the devices + """ + lifx_cred = Setting("lifx") + self.getclienttoken = lifx_cred.setting["client_auth"]["_CLIENT_TOKEN"] + self.rawData = self.get_request(lifx_cred.get("url")["_DEVICELIST_REQ"]) + + def station_data(self): + """ + Obtains the Lifx bulb data and writes them to the Building depot + Returns: + { + "success": "True" + "HTTP Error 400": "Bad Request" + } + """ + global mac_id + for device in self.rawData: + lifx_data = {} + mac_id = device["id"] + name = device["label"].replace(" ", "_") + for k in device["color"].keys(): + lifx_data[k + "_" + name] = device["color"][k] + lifx_data["brightness_" + name] = device["brightness"] + lifx_data["status_" + name] = device["power"] + return self.post_to_bd(lifx_data) + + def post_to_bd(self, station_data): + """ + Format of the JSON Object to be sent to bd_connect.py. + sensor_data contains all information from sensor to be + Read and sensor_points to be created in accordance with that. + client_data contains all information about client_id,client_keyetc + for authentication + data={"sensor_data":{}} + Args : + {,, etc.} + Returns: + { + "success": "True" + "HTTP Error 400": "Bad Request" + } + """ + global mac_id + print station_data + data = {'sensor_data': {}} + data['sensor_data'].update(station_data) + data['sensor_data'].update({"mac_id": mac_id}) + try: + resp = get_json(json.dumps(data)) + except Exception as e: + print e + return resp + + def get_request(self, url): + """ + Get Status of the Bulb and the device list of Lifx bulbs + Args : + url : Url of lifx bulb + Returns: + Lifx bulb device list and status + + """ + headers = { + "Authorization": "Bearer %s" % self.getclienttoken + } + resp = requests.get(url, headers=headers).json() + return resp + + +def main(args): + """ Accepts the command to either read or actuate the Lifx Bulb. + + Args as Data: + 'on/off': Switch on/off the bulb + OR + '{ + on/off: Switch on/off the bulb + blue saturation:0.5 :: Provide color and + Saturation percent + brightness: Provide brightness percent + duration: Duration to be on in mins + }' + OR + '': Empty to Write data to the Building depot + Returns: + If the No args read status and write data + to building depot + { + "success": "True" + "HTTP Error 400": "Bad Request" + } + + If the args is to Actuate the Lifx bulb, then + {u'results': + [ + {u'status': u'ok', + u'id': #Id of the lifx bulb + u'label': #Name of the Lifx bulb + } + ] + } else + + {"Device Not Found/Error in fetching data"} + """ + from sys import exit, stderr + if len(args) == 1: + devlist = LifxSensor() + if not devlist.getclienttoken: + stderr.write("Please Enter the Client token from Lifx in\ + the config file") + exit(1) + try: + resp = devlist.station_data() # Get the Lifx Bulbs data and updates the BD + print resp + + except Exception as e: + print "Device Not Found/Error in fetching data" + exit(0) + else: + try: + devlist = LifxActuator() + if len(args) == 2: + devlist.switch(args[1]) + elif len(args) > 2: + print args, len(args) + state = {"power": args[1], "color": args[2] + " " + args[3], + "brightness": args[4], "duration": args[5]} + devlist.switch_state(state) + except Exception as e: + print "Device Not Found/Error in fetching data" + exit(0) + + +if __name__ == "__main__": + main(sys.argv) \ No newline at end of file diff --git a/mother/__init__.py b/mother/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mother/sens_mother.py b/mother/sens_mother.py new file mode 100644 index 0000000..44067ee --- /dev/null +++ b/mother/sens_mother.py @@ -0,0 +1,190 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +import json +import requests +from bd_connect.connect_bd import get_json +from config.setting import Setting + +""" +About: +This module is a connector that connects any Sen.se Mother Device to the +Building DepotV3.1 with the help of the bd connect program. + +Configuration: +To be able to have the program accessing your Sen.se Mother data, you have +to register yourself in the Sen.se's Developer website with an email and +a password.To obtain an access to the Sen.se APIs you have to register at +https://sen.se/developers/ and fill up the form. Once you get a mail from +sen.se regarding your access to their Api(which may take a week) regarding +providing access to their servers you can start implementing the codes below. +Enter the information of your Sen.se Mother Device in the json file +in config folder. +""" + + +class ClientAuth: + """Class definition of Sen.se Mother to request authentication and + keep access token available through token method.""" + + def __init__(self): + mother_cred = Setting("mother") + self.username = mother_cred.setting['client_auth']['_USERNAME'] + self.password = mother_cred.setting['client_auth']['_PASSWORD'] + self.mac_id = mother_cred.setting['mac_id'] + params = {'username': str(self.username), 'password': str(self.password)} + self._AUTH_REQ = mother_cred.get("url")["_AUTH_REQ"] + self._NODE_REQ = mother_cred.get("url")["_NODE_REQ"] + response = requests.post(self._AUTH_REQ, params).json() + if 'token' in response: + self.api_token = response["token"] + else: + print self.api_token, "Error" + exit(1) + + +# Class for Client authentication and access token +class DeviceList: + """ + Class defintion of Sen.se mother to request for the Device List , + obtain the cookie data and send itto the Building Depot so as to + update the metadata + """ + + def __init__(self, authdata): + """ + Initialize the auth token and obtain the cookie data of Sen.se Mother + Args: + "authData": class object of ClientAuth provides + access token. + """ + self.getAuthToken = authdata.api_token + self.mac_id = authdata.mac_id + params = {'resource__type': 'device', 'resource__slug': 'cookie'} + response = self.get_request(authdata._NODE_REQ, params) + self.rawData = response["objects"] + self.cookie_feeds = {} + for cookie in self.rawData: + cok_id = cookie["label"] + "#" + cookie["uid"] + self.cookie_feeds[cok_id] = cookie["publishes"] + self.cookie_event_urls = {} + # obtains the cookie feeds of the Sen.se mother + for key, value in self.cookie_feeds.items(): + self.cookie_event_urls[key] = [] + for app in value: + self.cookie_event_urls[key].append(app["url"] + "events/") + # If none of the apps for the respective cookies are not activated + if not self.cookie_event_urls[key]: + self.cookie_event_urls.pop(key, None) + + def get_cookie_app_data(self, eventdata, device): + """ Obtain each Sen.se mother cookie's app data and return them + Args: + "eventdata": #The event information of each app + installed in the Sen.se mother + cookie + "device": #Device name of the Sen.se mother cookie + Returns: + Data readings of the cookie for the specific app + + """ + print "DATA :", eventdata["type"] + for key, value in eventdata["data"].items(): + reading = device + "_" + eventdata["type"] + "_" + key + eventdata["data"][reading] = eventdata["data"].pop(key) + print eventdata["data"] + return eventdata["data"] + + def get_mother_data(self): + """Obtain the all app data of all cookie connected to the mother and get + each app's data installed on the cookie + Returns: + A json which consists all the Sen.se mothers cookie + recent data + """ + app_data = {} + for key, value in self.cookie_event_urls.items(): + device_name = key.split("#")[0] + print "Fetching:", key.split("#")[0], "'s data" + for url in value: + # Get the data of the apps installed on the cookie + latest_reading = self.get_request(url) + if len(latest_reading["objects"]): + latest_reading = latest_reading["objects"][0] + app_data.update(self.get_cookie_app_data(latest_reading, \ + device_name)) + return app_data + + def get_request(self, url, params=None): + """ + Get Device List for Sen.se Mother and its cookies + Args: + url : Url of Sen.se mothers cookies data + params : cookie specific parameters + Returns: + Sen.se mothers cookie and app data + + """ + header = {'Authorization': u' Token ' + self.getAuthToken} + response = requests.get(url, headers=header, params=params).json() + return response + + # Post Values to Building Depot. + def post_to_bd(self, mother_data): + """ + Format of the JSON Object to be sent to bd_connect.py. + sensor_data contains all information from sensor to be Read + and sensor_points to be created in accordance with that. + client_data contains all information about client_id,client_key etc + for authentication + data={"sensor_data":{}} + + Args: + {} + Returns: + { + "success": "True" + "HTTP Error 400": "Bad Request" + } + """ + data = {'sensor_data': {}} + data['sensor_data'].update(mother_data) + data['sensor_data'].update({"mac_id": self.mac_id}) + response = get_json(json.dumps(data)) + return response + + +if __name__ == '__main__': + + """ + Reads the Sen.se mothers cookie data and writes it to the building depot. + + Returns: + Reads the data from Sen.se mother cookies + { + "success": "True" + "HTTP Error 400": "Bad Request" + } + else + {"Error in Device Connection"} + + + """ + from sys import exit, stderr + + try: + auth = ClientAuth() + if not auth.username or not auth.password: + stderr.write("Please Enter the Sense Mothers username and password") + exit(1) + devList = DeviceList(auth) # Obtain the Device List + mother_data = devList.get_mother_data() # Get the Mother sensor data + + try: + resp = devList.post_to_bd(mother_data) # Send Data to the BuildingDepot + print 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/netatmo/__init__.py b/netatmo/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/netatmo/sens_netatmo.py b/netatmo/sens_netatmo.py new file mode 100644 index 0000000..42ffa86 --- /dev/null +++ b/netatmo/sens_netatmo.py @@ -0,0 +1,272 @@ +import json, time +from urllib import urlencode +import urllib2 +from bd_connect.connect_bd import get_json +from config.setting import Setting + +""" +About: +This module is a connector that connects any Netatmo Weather station to the +Building DepotV3.1 with the help of the bd connect program. + +Configuration: +To be able to have the program accessing your netatmo data, you have +to register your program as a Netatmo app in your Netatmo account at +https://auth.netatmo.com/en-US/access/signup. All you have +to do is to give it a name and you will be returned a client_id and +secret that your app has to supply to access netatmo servers.Enter the +information of your Netatmo Device in the json file in config folder. +""" + + +class ClientAuth: + """ + Class definition of Netatmo to request authentication and keep access + token available through token method.Renew it automatically if necessary + """ + + def __init__(self): + """ + Initialises the Client Id and Client key and obtains the Access + token of the netatmo device + + Args as Data: + clientid: #netatmo id of the user + clientsecret: #netatmo secret key of the user + username: #username used in netatmo login + password: #password used in netatmo login + """ + netatmo_cred = Setting("netatmo") + self.clientId = netatmo_cred.setting['client_auth']['_CLIENT_ID'] + self.clientSecret = netatmo_cred.setting['client_auth']['_CLIENT_SECRET'] + self.username = netatmo_cred.setting['client_auth']['_USERNAME'] + self.password = netatmo_cred.setting['client_auth']['_PASSWORD'] + postparams = { + "grant_type": "password", + "client_id": self.clientId, + "client_secret": self.clientSecret, + "username": self.username, + "password": self.password, + "scope": "read_station" + } + self._AUTH_REQ = netatmo_cred.get("url")["_AUTH_REQ"] + response = get_request(self._AUTH_REQ, postparams) + self._clientId = self.clientId + self._clientSecret = self.clientSecret + self._accessToken = response['access_token'] + self.refreshToken = response['refresh_token'] + self._scope = response['scope'] + self.expiration = int(response['expire_in'] + time.time()) + + @property + def access_token(self): + """ + Renew the access token if the token is expired + + Returns: + New Access token + """ + if self.expiration < time.time(): # Token should be renewed + + postparams = { + "grant_type": "refresh_token", + "refresh_token": self.refreshToken, + "client_id": self._clientId, + "client_secret": self._clientSecret + } + response = get_request(self._AUTH_REQ, postparams) + + self._accessToken = response['access_token'] + self.refreshToken = response['refresh_token'] + self.expiration = int(response['expire_in'] + time.time()) + + return self._accessToken + + +class DeviceList: + """ + Class definition of Netatmo to obtain the Natatmo station data and + update the data in the Building depot. + + """ + + def __init__(self, authdata): + """ + Initilize the auth token and obtain the device modules data + of Netatmo + Args as data: + "authdata": class object of ClientAuth provides + access token. + + """ + self.getAuthToken = authdata.access_token + postparams = { + "access_token": self.getAuthToken, + "app_type": "app_station" + } + url_cred = Setting("netatmo") + _DEVICELIST_REQ = url_cred.get("url")["_DEVICELIST_REQ"] + response = get_request(_DEVICELIST_REQ, postparams) + # print "\nDeviceList: ",resp + self.rawData = response['body'] + self.stations = {d['_id']: d for d in self.rawData['devices']} + # print "\nStations: ",self.stations + self.modules = {m['_id']: m for m in self.rawData['modules']} + # print "\nModules: ",self.modules + self.default_station = list(self.stations.values())[0]['station_name'] + # print "\nSelf Station: ",self.default_station + + def station_by_name(self, station=None): + """ + Find the Netatmo station data by name if none given finds all the + data of the netatmo stations + Args as data: + "station": #Station name + """ + if not station: + station = self.default_station + for i, s in self.stations.items(): + if s['station_name'] == station: + return self.stations[i] + return None + + def get_module_data(self, module_id): + """ + Obtain the Netatmo outdoor module data + Args as data: + "module_id": #module_id of the outdoor module + Returns: + { + u'min_temp_NAModule1': #minimum temperature of + outdoor module, + u'Humidity_NAModule1': #Humidity of outdoor module, + u'Temperature_NAModule1': #current temp of outdoor + module, + u'max_temp_NAModule1': #maximum temperature of + outdoor module + } + """ + module_data = self.modules[module_id]['dashboard_data'] + for k in ('date_max_temp', 'date_min_temp', 'time_utc'): + module_data.pop(k, None) + for i in module_data.keys(): + module_data[i + "_" + self.modules[module_id]['type']] = module_data.pop(i) + return module_data + + def get_stations_data(self, station=None): + """ + Obtain the Netatmo indoor module data and form common dict of + all the module data + + Args as data: + "station": #station name of the Base station + Returns: + { + u'Noise_indoor': #noise in decibels + of indoor module, + u'max_temp_indoor': #maximum temperature of + indoor module, + u'Temperature_indoor': #current temp of indoor + module, + u'Pressure_indoor': #pressure in pascal of + indoor module, + u'Humidity_indoor': #humidity of the indoor + module, + u'CO2_indoor': #Co2 levels of indoor + module, + u'min_temp_indoor': #minimum temperature of + indoor module, + u'AbsolutePressure_indoor': #absolute pressure + of indoor module + } + """ + if not station: + station = self.default_station + s = self.station_by_name(station) + global mac_id + # obtain the Netatmo indoor main module data + netatmo_data = s['dashboard_data'] + mac_id = s['_id'] + for k in ('date_max_temp', 'date_min_temp', 'time_utc'): + netatmo_data.pop(k, None) + for i in netatmo_data.keys(): + netatmo_data[i + "_indoor"] = netatmo_data.pop(i) + + # obtain the Netatmo other outdoor module data + module_data = {} + for n in s['modules']: + module_data.update(self.get_module_data(n)) + + netatmo_data.update(module_data) + return netatmo_data + + def post_to_bd(self, stationdata): + """ + 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(stationdata) + data['sensor_data'].update({"mac_id": mac_id}) + response = get_json(json.dumps(data)) + return response + + +def get_request(url, params): + """ + Get the Netatmo station data + Args as data: + url : Url of Netatmo station data + Returns: + Netatmo device and module list + + """ + + params = urlencode(params) + headers = {"Content-Type": "application/x-www-form-urlencoded;charset=utf-8"} + req = urllib2.Request(url=url, data=params, headers=headers) + response = urllib2.urlopen(req).read() + return json.loads(response) + + +if __name__ == "__main__": + """ + Reads the netatmo station data and writes it to the building depot. + + Returns: + Reads the data from Netatmo station + { + "success": "True" + "HTTP Error 400": "Bad Request" + } + else + { "Error in Device Connection" + } + """ + from sys import exit, stderr + + try: + auth = ClientAuth() # Get authentication key + if not auth.clientId or not auth.clientSecret or not auth.username \ + or not auth.password: + stderr.write("Please Enter the Netatmo Clientid and ClientSecret") + exit(1) + + devList = DeviceList(auth) # Obtain the DEVICELIST + netatmo_data = devList.get_stations_data() # Get the Stations data + try: + resp = devList.post_to_bd(netatmo_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/wemo/__init__.py b/wemo/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wemo/sens_wemo.py b/wemo/sens_wemo.py new file mode 100644 index 0000000..42dfc07 --- /dev/null +++ b/wemo/sens_wemo.py @@ -0,0 +1,357 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +import re +import urllib2 +import json +import time +import sys +from bd_connect.connect_bd import get_json +from config.setting import Setting + +""" +About: +This module is a connector that connects any wemo switch to the +Building DepotV3.1 with the help of the bd_connect program + +Configuration: +Enter the local IP address of your WeMo Switch in the json file in config +folder.You may have to check your router to see what local IP is +assigned to the WeMo.It is recommended that you assign a static local IP to +the WeMo to ensure the WeMo is always at that address.To run the code type +"python sens_wemo.py" on the terminal +""" + + +class WemoSensor: + """Class definition of WemoSensor to send SOAP calls to + the Wemo Switch to perform various operations""" + ports = [49153, 49152, 49154, 49151, 49155] + + def __init__(self): + """Initialises the IP of the Wemo Switch""" + wemo_cred = Setting("wemo") + self.ip = wemo_cred.setting["ip"] + + def energy_info(self): + """Obtains the energy info from the wemo which is in the format + 1|1457|2487|9042|34794|21824|29|293|424|137.0|8000 + 1. State (0 = off, 1 = on) + 2. Average power (W) + 3. Instantaneous power (mW) + 4. Energy used today in mW-minutes + 5. Energy used over past two weeks in mW-minutes. """ + + res = self._send('Get', 'energy').split('|') + edata = {'status': res[0], 'avg_power': res[7], + 'instantaneous_power': res[8], 'avg_energy_today': res[9], + 'avg_energy_2weeks': res[10]} + return edata + + def _send(self, obj, value=None): + """ + Send request over various wemo ports and since we cannot + guarantee that wemo accepts request over a single port. + Args as data: + method: Set/Get the state of Wemo + obj : object of the wemo state + value : 0/1 to set the state + Returns: + { + "success": "True" + "HTTP Error 400": "Bad Request" + } + + """ + + if value == 'energy': + """Headers and body of xml to obtain device + energy information""" + body_xml = """ + + + + + + """ + header_xml = { + "Accept": "", + "Content-Type": "text/xml; charset=\"utf-8\"", + "SOAPACTION": "\"urn:Belkin:service:insight:1\ + #GetInsightParams\"" + } + + for port in self.ports: + result = self._try_send(self.ip, port, \ + body_xml, header_xml, obj) + if result is not None: + self.ports = [port] + return result + raise Exception("TimeoutOnAllPorts") + + def _try_send(self, ip, port, body, header, data): + """ + Send the Request to the Wemo Switch + Args as data: + ip : ip address of the wemo + port: port on which wemo accepts request + header: header of the Soap request + body: body of the Soap request + data: 0/1 to switch on/off + + Returns: + { + Energy information or Status of wemo + based on the request made + or + "HTTP Error 400": "Bad Request" + + } + """ + try: + url = 'http://%s:%s/upnp/control/insight1' \ + % (ip, port) + request = urllib2.Request(url, body, header) + print "request: ", request + wemo_data = urllib2.urlopen(request, None, 5).read() + regex = r'\(.*)\' + params = re.search(regex, wemo_data).group(1) + return params + + except Exception as e: + print str(e) + return None + + def _extract(self, response, name): + """Extract information from the response""" + exp = '<%s>(.*?)<\/%s>' % (name, name) + g = re.search(exp, response) + if g: + return g.group(1) + return response + + def get_device_data(self): + """Get the device data information uncomment any of these to make + work if device is connected retrieve device information in the + format"status=switch.energyinfo()" + """ + wemo_cred = Setting("wemo") + mac_id = wemo_cred.setting["mac_id"] + edata = self.energy_info() + data = {'sensor_data': {}} + data['sensor_data'].update(edata) + data['sensor_data'].update({"mac_id": mac_id}) + + +class WemoActuator: + """Class definition of WemoActuator to send SOAP calls to + the Wemo Switch to perform various operations""" + OFF_STATE = '0' + ON_STATES = ['1', '8'] + ip = None + ports = [49153, 49152, 49154, 49151, 49155] + + def __init__(self): + """Initialises the IP of the Wemo Switch""" + wemo_cred = Setting("wemo") + self.ip = wemo_cred.setting["ip"] + + def toggle(self): + """Toggle the switch + Switch status is on then switch off else + if the switch status is off switch on""" + wemo_status = self.status() + if wemo_status in self.ON_STATES: + result = self.off() + # result = 'WeMo is now off.' + elif wemo_status == self.OFF_STATE: + result = self.on() + # result = 'WeMo is now on.' + else: + raise Exception("UnexpectedStatusResponse") + return result + + def on(self): + """Switch on the wemo switch""" + return self._send('Set', 'BinaryState', 1) + + def off(self): + """Switch off the wemo switch""" + return self._send('Set', 'BinaryState', 0) + + def status(self): + """Get the status of switch""" + return self._send('Get', 'BinaryState') + + def name(self): + """Get the name of the switch""" + return self._send('Get', 'FriendlyName') + + def signal(self): + """Get the Signal Strength of the wemo switch""" + return self._send('Get', 'SignalStrength') + + def _get_header_xml(self, method, obj): + """Defines Header of SOAP request + Args as data: + method: Set/Get the state of Wemo + obj : object of the wemo state + Returns: + Header of the Soap Request + """ + method = method + obj + return '"urn:Belkin:service:basicevent:1#%s"' % method + + def _get_body_xml(self, method, obj, value=0): + """Defines Body of the SOAP request + Args as data: + method: Set/Get the state of Wemo + obj : object of the wemo state + Returns: + Body of the Soap Request + """ + method = method + obj + return '' \ + '<%s>%s' % (method, obj, value, obj, method) + + def _send(self, method, obj, value=None): + """ + Send request over various wemo ports and since we cannot + guarantee that wemo accepts request over a single port. + Args as data: + method: Set/Get the state of Wemo + obj : object of the wemo state + value : 0/1 to set the state + Returns: + { + "success": "True" + "HTTP Error 400": "Bad Request" + } + + """ + body_xml = self._get_body_xml(method, obj, value) + # print "body: ", body_xml + header_xml = self._get_header_xml(method, obj) + # print "Header",header_xml + + for port in self.ports: + result = self._try_send(self.ip, port, \ + body_xml, header_xml, obj) + if result is not None: + self.ports = [port] + return result + raise Exception("TimeoutOnAllPorts") + + def _try_send(self, ip, port, body, header, data): + """ + Send the Request to the Wemo Switch + Args as data: + ip : ip address of the wemo + port: port on which wemo accepts request + header: header of the Soap request + body: body of the Soap request + data: 0/1 to switch on/off + + Returns: + { + Energy information or Status of wemo + based on the request made + or + "HTTP Error 400": "Bad Request" + + } + """ + try: + + url = 'http://%s:%s/upnp/control/basicevent1' \ + % (ip, port) + request = urllib2.Request(url) + print "request: ", request + request.add_header('Content-type', \ + 'text/xml; charset="utf-8"') + request.add_header('SOAPACTION', header) + request_body = '' + request_body += '' + request_body += '%s' % body + request_body += '' + request.add_data(request_body) + result = urllib2.urlopen(request, timeout=3) + return self._extract(result.read(), data) + except Exception as e: + print str(e) + return None + + def _extract(self, response, name): + """Extract information from the response""" + exp = '<%s>(.*?)<\/%s>' % (name, name) + g = re.search(exp, response) + if g: + return g.group(1) + return response + + def output(self, message): + print message + + +def main(arguments): + """ + Accept the command to either read or actuate the Wemo Switch. + + Args as Data: + 'r': Read the energy data from the Switch and + update the metadata on the Building Depot. + 'w': Actuate the Wemo Switch to switch on + and off. + Returns: + If the args is to read energy data from Wemo + { + "success": "True" + "HTTP Error 400": "Bad Request" + } + If the args is to Actuate the Wemo Switch, then + {on/off : success} else + {"Device Not Found/Error in fetching data"} + """ + global status, data + cmd = arguments[1] + if 'r' == cmd: + switch = WemoSensor() + try: + switch = WemoSensor() + switch.get_device_data() + + except Exception as e: + print "Device Not Found/Error in fetching data" + print e + exit(0) + + """Posts json object information to BD_Connect in this format + data={"sensor_data":{}, + "client_data":{}}""" + try: + print get_json(json.dumps(data)) + print "Response from bd_connnect.py" + except Exception as e: + print e + elif 'w' == cmd: + switch = WemoActuator() + try: + # Uncomment any of these to actuate Wemo Switch + switch.output(switch.on()) + # output(switch.off()) + # output(switch.toggle()) + # output(switch.status()) + except Exception as e: + print "Device Not Found" + print str(e) + + +if __name__ == "__main__": + main(sys.argv)