nest_thermostat 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +32 -0
- data/LICENSE +22 -0
- data/README.md +78 -0
- data/Rakefile +2 -0
- data/lib/nest_thermostat/nest.rb +155 -0
- data/lib/nest_thermostat/version.rb +3 -0
- data/lib/nest_thermostat.rb +2 -0
- data/nest_thermostat.gemspec +21 -0
- data/spec/nest_thermostat_spec.rb +74 -0
- metadata +90 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
nest_thermostat (0.0.1)
|
5
|
+
httparty (~> 0.8.3)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
awesome_print (1.0.2)
|
11
|
+
diff-lcs (1.1.3)
|
12
|
+
httparty (0.8.3)
|
13
|
+
multi_json (~> 1.0)
|
14
|
+
multi_xml
|
15
|
+
multi_json (1.3.6)
|
16
|
+
multi_xml (0.5.1)
|
17
|
+
rspec (2.10.0)
|
18
|
+
rspec-core (~> 2.10.0)
|
19
|
+
rspec-expectations (~> 2.10.0)
|
20
|
+
rspec-mocks (~> 2.10.0)
|
21
|
+
rspec-core (2.10.1)
|
22
|
+
rspec-expectations (2.10.0)
|
23
|
+
diff-lcs (~> 1.1.3)
|
24
|
+
rspec-mocks (2.10.1)
|
25
|
+
|
26
|
+
PLATFORMS
|
27
|
+
ruby
|
28
|
+
|
29
|
+
DEPENDENCIES
|
30
|
+
awesome_print
|
31
|
+
nest_thermostat!
|
32
|
+
rspec (~> 2.10)
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Eric Boehs
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# NestThermostat
|
2
|
+
|
3
|
+
This gem allows you to get and set the temperature of your Nest
|
4
|
+
thermostat. You can also get and set the away status and get the
|
5
|
+
current temperature and target temperature time.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'nest_thermostat'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install nest_thermostat
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
Get some useful info:
|
23
|
+
```ruby
|
24
|
+
@nest = NestThermostat::Nest.new({email: ENV['NEST_EMAIL'], password: ENV['NEST_PASS']})
|
25
|
+
puts @nest.current_temperature # => 75.00
|
26
|
+
puts @nest.current_temp # => 75.00
|
27
|
+
puts @nest.temperature # => 73.00
|
28
|
+
puts @nest.temp # => 73.00
|
29
|
+
puts @nest.target_temperature_at # => 2012-06-05 14:28:48 +0000 # Ruby date object or false
|
30
|
+
puts @nest.target_temp_at # => 2012-06-05 14:28:48 +0000 # Ruby date object or false
|
31
|
+
puts @nest.away # => false
|
32
|
+
puts @nest.leaf # => true # May take a few seconds after a temp change
|
33
|
+
puts @nest.humidity # => 54 # Relative humidity in percent
|
34
|
+
```
|
35
|
+
|
36
|
+
Change the temperature or away status:
|
37
|
+
```ruby
|
38
|
+
puts @nest.temperature # => 73.0
|
39
|
+
puts @nest.temperature = 74.0
|
40
|
+
puts @nest.temperature # => 74.0
|
41
|
+
|
42
|
+
puts @nest.away # => false
|
43
|
+
puts @nest.away = true
|
44
|
+
puts @nest.away # => true
|
45
|
+
```
|
46
|
+
|
47
|
+
Default temperatures are in fahrenheit but you can change to celsius or kelvin:
|
48
|
+
```ruby
|
49
|
+
@nest = NestThermostat::Nest.new({..., temperature_scale: 'c'}) # Or C, Celsius or celsius
|
50
|
+
@nest.temperature_scale = 'k' # or K, Kelvin or kelvin
|
51
|
+
```
|
52
|
+
|
53
|
+
And of course if you want to get LOTS of other goodies like (schedule and every diag piece of info you'd ever want):
|
54
|
+
```ruby
|
55
|
+
p @nest.status
|
56
|
+
|
57
|
+
# -- OR --
|
58
|
+
|
59
|
+
require 'yaml'
|
60
|
+
yaml @nest.status
|
61
|
+
|
62
|
+
# -- OR my favorite --
|
63
|
+
|
64
|
+
require 'ap' # gem install awesome_print
|
65
|
+
ap @nest.status
|
66
|
+
```
|
67
|
+
Feel free to implement anything you see useful and submit a pull
|
68
|
+
request. I'd love to see other information like scheduling or multiple
|
69
|
+
device/location support added.
|
70
|
+
|
71
|
+
|
72
|
+
## Contributing
|
73
|
+
|
74
|
+
1. Fork it
|
75
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
76
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
77
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
78
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'httparty'
|
3
|
+
require 'json'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
module NestThermostat
|
7
|
+
class Nest
|
8
|
+
attr_accessor :email, :password, :login_url, :user_agent, :auth,
|
9
|
+
:temperature_scale, :login, :token, :user_id, :transport_url,
|
10
|
+
:transport_host, :structure_id, :device_id, :headers
|
11
|
+
|
12
|
+
def initialize(config = {})
|
13
|
+
raise 'Please specify your nest email' unless config[:email]
|
14
|
+
raise 'Please specify your nest password' unless config[:password]
|
15
|
+
|
16
|
+
# User specified information
|
17
|
+
self.email = config[:email]
|
18
|
+
self.password = config[:password]
|
19
|
+
self.temperature_scale = config[:temperature_scale] || config[:temp_scale] || 'f'
|
20
|
+
self.login_url = config[:login_url] || 'https://home.nest.com/user/login'
|
21
|
+
self.user_agent = config[:user_agent] ||'Nest/1.1.0.10 CFNetwork/548.0.4'
|
22
|
+
|
23
|
+
# Login and get token, user_id and URLs
|
24
|
+
login
|
25
|
+
self.token = login["access_token"]
|
26
|
+
self.user_id = login["userid"]
|
27
|
+
self.transport_url = login["urls"]["transport_url"]
|
28
|
+
self.transport_host = URI.parse(self.transport_url).host
|
29
|
+
self.headers = {
|
30
|
+
'Host' => self.transport_host,
|
31
|
+
'User-Agent' => self.user_agent,
|
32
|
+
'Authorization' => 'Basic ' + self.token,
|
33
|
+
'X-nl-user-id' => self.user_id,
|
34
|
+
'X-nl-protocol-version' => '1',
|
35
|
+
'Accept-Language' => 'en-us',
|
36
|
+
'Connection' => 'keep-alive',
|
37
|
+
'Accept' => '*/*'
|
38
|
+
}
|
39
|
+
|
40
|
+
# Set device and structure id
|
41
|
+
status
|
42
|
+
end
|
43
|
+
|
44
|
+
def status
|
45
|
+
request = HTTParty.get("#{self.transport_url}/v2/mobile/user.#{self.user_id}", headers: self.headers) rescue nil
|
46
|
+
result = JSON.parse(request.body) rescue nil
|
47
|
+
|
48
|
+
self.structure_id = result['user'][user_id]['structures'][0].split('.')[1]
|
49
|
+
self.device_id = result['structure'][structure_id]['devices'][0].split('.')[1]
|
50
|
+
|
51
|
+
result
|
52
|
+
end
|
53
|
+
|
54
|
+
def leaf
|
55
|
+
status["device"][self.device_id]["leaf"]
|
56
|
+
end
|
57
|
+
|
58
|
+
def humidity
|
59
|
+
status["device"][self.device_id]["current_humidity"]
|
60
|
+
end
|
61
|
+
|
62
|
+
def current_temperature
|
63
|
+
convert_temp_for_get(status["shared"][self.device_id]["current_temperature"])
|
64
|
+
end
|
65
|
+
alias_method :current_temp, :current_temperature
|
66
|
+
|
67
|
+
def temperature
|
68
|
+
convert_temp_for_get(status["shared"][self.device_id]["target_temperature"])
|
69
|
+
end
|
70
|
+
alias_method :temp, :temperature
|
71
|
+
|
72
|
+
def temperature=(degrees)
|
73
|
+
degrees = convert_temp_for_set(degrees)
|
74
|
+
|
75
|
+
request = HTTParty.post(
|
76
|
+
"#{self.transport_url}/v2/put/shared.#{self.device_id}",
|
77
|
+
body: %Q({"target_change_pending":true,"target_temperature":#{degrees}}),
|
78
|
+
headers: self.headers
|
79
|
+
) rescue nil
|
80
|
+
end
|
81
|
+
alias_method :temp=, :temperature=
|
82
|
+
|
83
|
+
def target_temperature_at
|
84
|
+
epoch = status["device"][self.device_id]["time_to_target"]
|
85
|
+
epoch != 0 ? Time.at(epoch) : false
|
86
|
+
end
|
87
|
+
alias_method :target_temp_at, :target_temperature_at
|
88
|
+
|
89
|
+
def away
|
90
|
+
status["structure"][structure_id]["away"]
|
91
|
+
end
|
92
|
+
|
93
|
+
def away=(state)
|
94
|
+
request = HTTParty.post(
|
95
|
+
"#{self.transport_url}/v2/put/structure.#{self.structure_id}",
|
96
|
+
body: %Q({"away_timestamp":#{Time.now.to_i},"away":#{!!state},"away_setter":0}),
|
97
|
+
headers: self.headers
|
98
|
+
) rescue nil
|
99
|
+
end
|
100
|
+
|
101
|
+
def temp_scale=(scale)
|
102
|
+
self.temperature_scale = scale
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
def login
|
107
|
+
login_request = HTTParty.post(
|
108
|
+
self.login_url,
|
109
|
+
body: { username: self.email, password: self.password },
|
110
|
+
headers: { 'User-Agent' => self.user_agent }
|
111
|
+
)
|
112
|
+
|
113
|
+
self.auth = JSON.parse(login_request.body) rescue nil
|
114
|
+
end
|
115
|
+
|
116
|
+
def convert_temp_for_get(degrees)
|
117
|
+
case self.temperature_scale
|
118
|
+
when /f|F|(F|f)ahrenheit/
|
119
|
+
c2f(degrees).round(3)
|
120
|
+
when /k|K|(K|k)elvin/
|
121
|
+
c2k(degrees).round(3)
|
122
|
+
else
|
123
|
+
degrees
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def convert_temp_for_set(degrees)
|
128
|
+
case self.temperature_scale
|
129
|
+
when /f|F|(F|f)ahrenheit/
|
130
|
+
f2c(degrees).round(5)
|
131
|
+
when /k|K|(K|k)elvin/
|
132
|
+
k2c(degrees).round(5)
|
133
|
+
else
|
134
|
+
degrees
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def k2c(degrees)
|
139
|
+
degrees.to_f - 273.15
|
140
|
+
end
|
141
|
+
|
142
|
+
def c2k(degrees)
|
143
|
+
degrees.to_f + 273.15
|
144
|
+
end
|
145
|
+
|
146
|
+
def c2f(degrees)
|
147
|
+
degrees.to_f * 9.0 / 5 + 32
|
148
|
+
end
|
149
|
+
|
150
|
+
def f2c(degrees)
|
151
|
+
(degrees.to_f - 32) * 5 / 9
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/nest_thermostat/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Eric Boehs"]
|
6
|
+
gem.email = ["ericboehs@gmail.com"]
|
7
|
+
gem.description = %q{Control your nest thermostat}
|
8
|
+
gem.summary = %q{View and set temperature and away status for your Nest}
|
9
|
+
gem.homepage = "http://github.com/ericboehs/nest-ruby"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "nest_thermostat"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = NestThermostat::VERSION
|
17
|
+
gem.add_dependency "httparty", "~> 0.8.3"
|
18
|
+
|
19
|
+
gem.add_development_dependency "rspec", "~> 2.10"
|
20
|
+
gem.add_development_dependency "awesome_print"
|
21
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'nest_thermostat'
|
2
|
+
|
3
|
+
RSpec.configure do |c|
|
4
|
+
c.filter_run focus: true
|
5
|
+
c.run_all_when_everything_filtered = true
|
6
|
+
end
|
7
|
+
|
8
|
+
describe NestThermostat::Nest do
|
9
|
+
before(:all) do
|
10
|
+
@nest = NestThermostat::Nest.new({email: ENV['NEST_EMAIL'], password: ENV['NEST_PASS'], temperature_scale: 'F'})
|
11
|
+
end
|
12
|
+
|
13
|
+
it "logs in to home.nest.com" do
|
14
|
+
@nest.transport_url.should match /transport.nest.com/
|
15
|
+
end
|
16
|
+
|
17
|
+
it "gets the status" do
|
18
|
+
@nest.status['device'].first[1]['mac_address'].should match /(\d|[a-f]|[A-F])+/
|
19
|
+
end
|
20
|
+
|
21
|
+
it "gets the leaf status" do
|
22
|
+
@nest.leaf.should_not be_nil
|
23
|
+
end
|
24
|
+
|
25
|
+
it "gets away status" do
|
26
|
+
@nest.away.should_not be_nil
|
27
|
+
end
|
28
|
+
|
29
|
+
it "sets away status" do
|
30
|
+
@nest.away = true
|
31
|
+
@nest.away.should == true
|
32
|
+
@nest.away = false
|
33
|
+
@nest.away.should == false
|
34
|
+
end
|
35
|
+
|
36
|
+
it "gets the current temperature" do
|
37
|
+
@nest.current_temperature.should be_a_kind_of(Numeric)
|
38
|
+
@nest.current_temp.should be_a_kind_of(Numeric)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "gets the relative humidity" do
|
42
|
+
@nest.humidity.should be_a_kind_of(Numeric)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "gets the temperature" do
|
46
|
+
@nest.temperature.should be_a_kind_of(Numeric)
|
47
|
+
@nest.temp.should be_a_kind_of(Numeric)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "sets the temperature" do
|
51
|
+
@nest.temp = '74'
|
52
|
+
@nest.temp.round.should eq(74)
|
53
|
+
|
54
|
+
@nest.temperature = '73'
|
55
|
+
@nest.temperature.round.should eq(73)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "sets the temperature in celsius" do
|
59
|
+
@nest.temperature_scale = 'c'
|
60
|
+
@nest.temperature = '22'
|
61
|
+
@nest.temperature.should eq(22.0)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "sets the temperature in kelvin" do
|
65
|
+
@nest.temp_scale = 'k'
|
66
|
+
@nest.temperature = '296'
|
67
|
+
@nest.temperature.should eq(296.0)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "gets the target temperature time" do
|
71
|
+
@nest.target_temp_at.should_not be_nil # (DateObject or false)
|
72
|
+
@nest.target_temperature_at.should_not be_nil # (DateObject or false)
|
73
|
+
end
|
74
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nest_thermostat
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Eric Boehs
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-05 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: httparty
|
16
|
+
requirement: &16513040 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.8.3
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *16513040
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &16511520 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2.10'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *16511520
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: awesome_print
|
38
|
+
requirement: &16526700 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *16526700
|
47
|
+
description: Control your nest thermostat
|
48
|
+
email:
|
49
|
+
- ericboehs@gmail.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- .gitignore
|
55
|
+
- Gemfile
|
56
|
+
- Gemfile.lock
|
57
|
+
- LICENSE
|
58
|
+
- README.md
|
59
|
+
- Rakefile
|
60
|
+
- lib/nest_thermostat.rb
|
61
|
+
- lib/nest_thermostat/nest.rb
|
62
|
+
- lib/nest_thermostat/version.rb
|
63
|
+
- nest_thermostat.gemspec
|
64
|
+
- spec/nest_thermostat_spec.rb
|
65
|
+
homepage: http://github.com/ericboehs/nest-ruby
|
66
|
+
licenses: []
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options: []
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 1.8.10
|
86
|
+
signing_key:
|
87
|
+
specification_version: 3
|
88
|
+
summary: View and set temperature and away status for your Nest
|
89
|
+
test_files:
|
90
|
+
- spec/nest_thermostat_spec.rb
|