Skip to content

Commit

Permalink
Merge PR smbaker#9 by @rbpasker
Browse files Browse the repository at this point in the history
Make pynest installable via pip, split pynest.py into an main program and a library, and move from urllibX to requests
  • Loading branch information
FiloSottile committed Mar 8, 2014
2 parents 0f62fd9 + 016b1d1 commit e357808
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 119 deletions.
120 changes: 1 addition & 119 deletions nest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,125 +21,7 @@
import urllib2
import sys
from optparse import OptionParser

try:
import json
except ImportError:
try:
import simplejson as json
except ImportError:
print "No json library available. I recommend installing either python-json"
print "or simpejson."
sys.exit(-1)

class Nest:
def __init__(self, username, password, serial=None, index=0, units="F"):
self.username = username
self.password = password
self.serial = serial
self.units = units
self.index = index

def loads(self, res):
if hasattr(json, "loads"):
res = json.loads(res)
else:
res = json.read(res)
return res

def login(self):
data = urllib.urlencode({"username": self.username, "password": self.password})

req = urllib2.Request("https://home.nest.com/user/login",
data,
{"user-agent":"Nest/1.1.0.10 CFNetwork/548.0.4"})

res = urllib2.urlopen(req).read()

res = self.loads(res)

self.transport_url = res["urls"]["transport_url"]
self.access_token = res["access_token"]
self.userid = res["userid"]

def get_status(self):
req = urllib2.Request(self.transport_url + "/v2/mobile/user." + self.userid,
headers={"user-agent":"Nest/1.1.0.10 CFNetwork/548.0.4",
"Authorization":"Basic " + self.access_token,
"X-nl-user-id": self.userid,
"X-nl-protocol-version": "1"})

res = urllib2.urlopen(req).read()

res = self.loads(res)

self.structure_id = res["structure"].keys()[0]

if (self.serial is None):
self.device_id = res["structure"][self.structure_id]["devices"][self.index]
self.serial = self.device_id.split(".")[1]

self.status = res

#print "res.keys", res.keys()
#print "res[structure][structure_id].keys", res["structure"][self.structure_id].keys()
#print "res[device].keys", res["device"].keys()
#print "res[device][serial].keys", res["device"][self.serial].keys()
#print "res[shared][serial].keys", res["shared"][self.serial].keys()

def temp_in(self, temp):
if (self.units == "F"):
return (temp - 32.0) / 1.8
else:
return temp

def temp_out(self, temp):
if (self.units == "F"):
return temp*1.8 + 32.0
else:
return temp

def show_status(self):
shared = self.status["shared"][self.serial]
device = self.status["device"][self.serial]

allvars = shared
allvars.update(device)

for k in sorted(allvars.keys()):
print k + "."*(32-len(k)) + ":", allvars[k]

def show_curtemp(self):
temp = self.status["shared"][self.serial]["current_temperature"]
temp = self.temp_out(temp)

print "%0.1f" % temp

def set_temperature(self, temp):
temp = self.temp_in(temp)

data = '{"target_change_pending":true,"target_temperature":' + '%0.1f' % temp + '}'
req = urllib2.Request(self.transport_url + "/v2/put/shared." + self.serial,
data,
{"user-agent":"Nest/1.1.0.10 CFNetwork/548.0.4",
"Authorization":"Basic " + self.access_token,
"X-nl-protocol-version": "1"})

res = urllib2.urlopen(req).read()

print res

def set_fan(self, state):
data = '{"fan_mode":"' + str(state) + '"}'
req = urllib2.Request(self.transport_url + "/v2/put/device." + self.serial,
data,
{"user-agent":"Nest/1.1.0.10 CFNetwork/548.0.4",
"Authorization":"Basic " + self.access_token,
"X-nl-protocol-version": "1"})

res = urllib2.urlopen(req).read()

print res
from pynest.pynest import Nest

def create_parser():
parser = OptionParser(usage="nest [options] command [command_options] [command_args]",
Expand Down
Empty file added pynest/__init__.py
Empty file.
132 changes: 132 additions & 0 deletions pynest/pynest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#! /usr/bin/python

# nest.py -- a python interface to the Nest Thermostat
# by Scott M Baker, [email protected], http://www.smbaker.com/
# updated by Bob Pasker [email protected] http://pasker.net
#
# Licensing:
# This is distributed unider the Creative Commons 3.0 Non-commecrial,
# Attribution, Share-Alike license. You can use the code for noncommercial
# purposes. You may NOT sell it. If you do use it, then you must make an
# attribution to me (i.e. Include my name and thank me for the hours I spent
# on this)
#
# Acknowledgements:
# Chris Burris's Siri Nest Proxy was very helpful to learn the nest's
# authentication and some bits of the protocol.

import urllib
import urllib2
import requests

try:
import json
except ImportError:
import simplejson as json

class Nest:
def __init__(self, username, password, serial=None, index=0, units="F", debug=False):
self.username = username
self.password = password
self.serial = serial
self.units = units
self.index = index
self.debug = debug

def login(self):

response = requests.post("https://home.nest.com/user/login",
data = {"username":self.username, "password" : self.password},
headers = {"user-agent":"Nest/1.1.0.10 CFNetwork/548.0.4"})

response.raise_for_status()

res = response.json()
self.transport_url = res["urls"]["transport_url"]
self.access_token = res["access_token"]
self.userid = res["userid"]
# print self.transport_url, self.access_token, self.userid

def get_status(self):
response = requests.get(self.transport_url + "/v2/mobile/user." + self.userid,
headers={"user-agent":"Nest/1.1.0.10 CFNetwork/548.0.4",
"Authorization":"Basic " + self.access_token,
"X-nl-user-id": self.userid,
"X-nl-protocol-version": "1"})

response.raise_for_status()
res = response.json()

self.structure_id = res["structure"].keys()[0]

if (self.serial is None):
self.device_id = res["structure"][self.structure_id]["devices"][self.index]
self.serial = self.device_id.split(".")[1]

self.status = res

#print "res.keys", res.keys()
#print "res[structure][structure_id].keys", res["structure"][self.structure_id].keys()
#print "res[device].keys", res["device"].keys()
#print "res[device][serial].keys", res["device"][self.serial].keys()
#print "res[shared][serial].keys", res["shared"][self.serial].keys()

def temp_in(self, temp):
if (self.units == "F"):
return (temp - 32.0) / 1.8
else:
return temp

def temp_out(self, temp):
if (self.units == "F"):
return temp*1.8 + 32.0
else:
return temp

def show_status(self):
shared = self.status["shared"][self.serial]
device = self.status["device"][self.serial]

allvars = shared
allvars.update(device)

for k in sorted(allvars.keys()):
print k + "."*(32-len(k)) + ":", allvars[k]

def show_curtemp(self):
temp = self.status["shared"][self.serial]["current_temperature"]
temp = self.temp_out(temp)

print "%0.1f" % temp

def _set(self, data, which):
if (self.debug): print json.dumps(data)
url = "%s/v2/put/%s.%s" % (self.transport_url, which, self.serial)
if (self.debug): print url
response = requests.post(url,
data = json.dumps(data),
headers = {"user-agent":"Nest/1.1.0.10 CFNetwork/548.0.4",
"Authorization":"Basic " + self.access_token,
"X-nl-protocol-version": "1"})

if response.status_code > 200:
if (self.debug): print response.content
response.raise_for_status()
return response

def _set_shared(self, data):
self._set(data, "shared")

def _set_device(self, data):
self._set(data, "device")

def set_temperature(self, temp):
return self._set_shared({
"target_change_pending": True,
"target_temperature" : self.temp_in(temp)
})

def set_fan(self, state):
return self._set_device({
"fan_mode": str(state)
})
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@
maintainer_email='[email protected]',
url='https://github.com/FiloSottile/nest_thermostat/',
scripts=['nest.py'],
packages=['pynest'],
install_requires = ['requests']
)

0 comments on commit e357808

Please sign in to comment.