ecobee 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ecobee.gemspec +2 -0
- data/examples/set_mode.rb +8 -9
- data/examples/test_token.rb +4 -7
- data/lib/ecobee.rb +14 -12
- data/lib/ecobee/client.rb +25 -2
- data/lib/ecobee/register.rb +1 -0
- data/lib/ecobee/token.rb +67 -33
- data/lib/ecobee/version.rb +1 -1
- metadata +3 -4
- data/examples/bitbar_plugin.rb +0 -252
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f15c138cad29178c3935acfdffc88446ca994d35
|
4
|
+
data.tar.gz: 58581cf67d84e8e0e43b83b2375069aa70d41a9b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6cb824a3793ecd6582479f00c94fad5d3819d4a7da373abdadbed6fdf7bb108a9072e7091780fb6f42842d60c648e827750a53e4a77c9455d3b59bc09faf325d
|
7
|
+
data.tar.gz: 5f8afab66b9bb155caf5b6f4691a72a8ada13765cfe64d3f83555bfcd910473d6f9d51115d87e81fbb8804593f90ed17d5f01c5ef0ecc04cd06c2d994358e9ed
|
data/ecobee.gemspec
CHANGED
@@ -19,6 +19,8 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
+
spec.required_ruby_version = '>= 2.0.0'
|
23
|
+
|
22
24
|
spec.add_development_dependency "bundler", "~> 1.12"
|
23
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
24
26
|
spec.add_development_dependency "rspec", "~> 3.0"
|
data/examples/set_mode.rb
CHANGED
@@ -4,7 +4,9 @@
|
|
4
4
|
# to change the mode. -- @robzr
|
5
5
|
|
6
6
|
require 'pp'
|
7
|
-
|
7
|
+
|
8
|
+
#require 'ecobee'
|
9
|
+
require_relative '../lib/ecobee.rb'
|
8
10
|
|
9
11
|
@hvac_modes = Ecobee::HVAC_MODES + ['quit']
|
10
12
|
|
@@ -14,7 +16,7 @@ class TestFunctions
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def print_summary
|
17
|
-
|
19
|
+
response = @client.get(
|
18
20
|
'thermostat',
|
19
21
|
{
|
20
22
|
'selection' => {
|
@@ -25,7 +27,6 @@ class TestFunctions
|
|
25
27
|
}
|
26
28
|
}
|
27
29
|
)
|
28
|
-
response = JSON.parse(http_response.body)
|
29
30
|
|
30
31
|
puts "Found %d thermostats." % response['thermostatList'].length
|
31
32
|
|
@@ -42,7 +43,7 @@ class TestFunctions
|
|
42
43
|
end
|
43
44
|
|
44
45
|
def update_mode(mode)
|
45
|
-
|
46
|
+
@client.post(
|
46
47
|
'thermostat',
|
47
48
|
body: {
|
48
49
|
'selection' => {
|
@@ -56,18 +57,16 @@ class TestFunctions
|
|
56
57
|
}
|
57
58
|
}
|
58
59
|
)
|
59
|
-
response = JSON.parse(http_response.body)
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
63
|
token = Ecobee::Token.new(
|
64
64
|
app_key: ENV['ECOBEE_APP_KEY'],
|
65
|
-
app_name: '
|
66
|
-
scope: :smartWrite,
|
67
|
-
token_file: '~/.ecobee_token'
|
65
|
+
app_name: 'ecobee-gem'
|
68
66
|
)
|
69
67
|
|
70
|
-
puts token.pin_message if token.pin
|
68
|
+
puts token.pin_message if token.pin
|
69
|
+
token.wait
|
71
70
|
test_functions = TestFunctions.new(
|
72
71
|
Ecobee::Client.new(token: token)
|
73
72
|
)
|
data/examples/test_token.rb
CHANGED
@@ -3,14 +3,11 @@
|
|
3
3
|
# Refreshes token; displays details on saved token. -- @robzr
|
4
4
|
|
5
5
|
require 'pp'
|
6
|
-
|
7
|
-
require_relative '/Users/robzr/GitHub/ecobee/lib/ecobee/token.rb'
|
8
|
-
require_relative '/Users/robzr/GitHub/ecobee/lib/ecobee/register.rb'
|
6
|
+
require_relative '../lib/ecobee.rb'
|
9
7
|
|
10
8
|
token = Ecobee::Token.new(
|
11
|
-
app_key: ENV['
|
12
|
-
app_name: 'ecobee-gem'
|
13
|
-
token_file: '~/.ecobee_token'
|
9
|
+
app_key: ENV['ECOBEE_APP_KEY'],
|
10
|
+
app_name: 'ecobee-gem'
|
14
11
|
)
|
15
12
|
|
16
13
|
puts token.pin_message if token.pin
|
@@ -18,6 +15,6 @@ token.wait
|
|
18
15
|
|
19
16
|
puts "Access Token: #{token.access_token}"
|
20
17
|
puts "Refresh Token: #{token.refresh_token}"
|
21
|
-
puts "Expires At: #{token.
|
18
|
+
puts "Expires At: #{token.access_expires_at}"
|
22
19
|
puts "Scope: #{token.scope}"
|
23
20
|
puts "Type: #{token.type}"
|
data/lib/ecobee.rb
CHANGED
@@ -2,31 +2,33 @@ require 'pp'
|
|
2
2
|
require 'json'
|
3
3
|
require 'net/http'
|
4
4
|
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
5
|
+
require 'ecobee/client'
|
6
|
+
require 'ecobee/register'
|
7
|
+
require 'ecobee/token'
|
8
|
+
require 'ecobee/version'
|
9
|
+
#require_relative 'ecobee/client'
|
10
|
+
#require_relative 'ecobee/register'
|
11
|
+
#require_relative 'ecobee/token'
|
12
|
+
#require_relative 'ecobee/version'
|
9
13
|
|
10
14
|
module Ecobee
|
11
15
|
API_HOST = 'api.ecobee.com'
|
12
16
|
API_PORT = 443
|
13
17
|
CONTENT_TYPE = ['application/json', { 'charset' => 'UTF-8' }]
|
14
|
-
|
18
|
+
DEFAULT_FILES = [
|
19
|
+
'~/Library/Mobile Documents/com~apple~CloudDocs/.ecobee_token',
|
20
|
+
'~/.ecobee_token'
|
21
|
+
]
|
15
22
|
HVAC_MODES = ['auto', 'auxHeatOnly', 'cool', 'heat', 'off']
|
16
|
-
|
17
|
-
REFRESH_INTERVAL_PAD = 60
|
23
|
+
REFRESH_INTERVAL_PAD = 120
|
18
24
|
REFRESH_TOKEN_CHECK = 10
|
19
|
-
|
20
|
-
SCOPES = [:smartRead, :smartWrite]
|
21
|
-
|
25
|
+
SCOPES = [:smartWrite, :smartRead]
|
22
26
|
URL_BASE= "https://#{API_HOST}:#{API_PORT}"
|
23
|
-
|
24
27
|
URL_API = "#{URL_BASE}/1/"
|
25
28
|
URL_GET_PIN = URL_BASE +
|
26
29
|
'/authorize?response_type=ecobeePin&client_id=%s&scope=%s'
|
27
30
|
URL_TOKEN = "#{URL_BASE}/token"
|
28
31
|
|
29
|
-
|
30
32
|
def self.Model(model)
|
31
33
|
{ 'idtSmart' => 'ecobee Smart',
|
32
34
|
'idtEms' => 'ecobee Smart EMS',
|
data/lib/ecobee/client.rb
CHANGED
@@ -14,7 +14,10 @@ module Ecobee
|
|
14
14
|
request = Net::HTTP::Get.new(URI(URI.escape(new_uri)))
|
15
15
|
request['Content-Type'] = *CONTENT_TYPE
|
16
16
|
request['Authorization'] = @token.authorization
|
17
|
-
http.request
|
17
|
+
http_response = http.request request
|
18
|
+
validate_status JSON.parse(http_response.body)
|
19
|
+
rescue JSON::ParserError => msg
|
20
|
+
raise ClientError.new("JSON::ParserError => #{msg}")
|
18
21
|
end
|
19
22
|
|
20
23
|
def post(arg, options: {}, body: nil)
|
@@ -24,7 +27,25 @@ module Ecobee
|
|
24
27
|
request.body = JSON.generate(body) if body
|
25
28
|
request['Content-Type'] = *CONTENT_TYPE
|
26
29
|
request['Authorization'] = @token.authorization
|
27
|
-
http.request
|
30
|
+
http_response = http.request request
|
31
|
+
validate_status JSON.parse http_response.body
|
32
|
+
rescue JSON::ParserError => msg
|
33
|
+
raise ClientError.new("JSON::ParserError => #{msg}")
|
34
|
+
end
|
35
|
+
|
36
|
+
def validate_status(response)
|
37
|
+
if !response.key? 'status'
|
38
|
+
raise ClientError.new('Missing Status')
|
39
|
+
elsif !response['status'].key? 'code'
|
40
|
+
raise ClientError.new('Missing Status Code')
|
41
|
+
elsif response['status']['code'] != 0
|
42
|
+
raise ClientError.new(
|
43
|
+
"GET Error: #{response['status']['code']} " +
|
44
|
+
"Message: #{response['status']['message']}"
|
45
|
+
)
|
46
|
+
else
|
47
|
+
response
|
48
|
+
end
|
28
49
|
end
|
29
50
|
|
30
51
|
private
|
@@ -40,4 +61,6 @@ module Ecobee
|
|
40
61
|
|
41
62
|
end
|
42
63
|
|
64
|
+
class ClientError < StandardError ; end
|
65
|
+
|
43
66
|
end
|
data/lib/ecobee/register.rb
CHANGED
data/lib/ecobee/token.rb
CHANGED
@@ -2,8 +2,8 @@ module Ecobee
|
|
2
2
|
require 'date'
|
3
3
|
|
4
4
|
class Token
|
5
|
-
attr_reader :
|
6
|
-
:
|
5
|
+
attr_reader :access_expires_at,
|
6
|
+
:access_token,
|
7
7
|
:pin,
|
8
8
|
:pin_message,
|
9
9
|
:refresh_token,
|
@@ -13,22 +13,28 @@ module Ecobee
|
|
13
13
|
:type
|
14
14
|
|
15
15
|
def initialize(
|
16
|
-
|
16
|
+
access_expires_at: nil,
|
17
|
+
access_token: nil,
|
18
|
+
app_key: nil,
|
17
19
|
app_name: nil,
|
18
20
|
code: nil,
|
19
21
|
refresh_token: nil,
|
20
22
|
scope: SCOPES[0],
|
21
|
-
token_file:
|
23
|
+
token_file: DEFAULT_FILES
|
22
24
|
)
|
25
|
+
@access_expires_at = access_expires_at
|
26
|
+
@access_token = access_token
|
23
27
|
@app_key = app_key
|
24
28
|
@app_name = app_name
|
25
29
|
@code = code
|
26
|
-
@access_token, @code_expires_at, @expires_at, @pin, @type = nil
|
27
30
|
@refresh_token = refresh_token
|
28
31
|
@scope = scope
|
29
|
-
@
|
30
|
-
|
31
|
-
|
32
|
+
@token_file = expand_files token_file
|
33
|
+
|
34
|
+
@code_expires_at, @pin, @type = nil
|
35
|
+
parse_token_file
|
36
|
+
@status = @refresh_token ? :ready : :authorization_pending
|
37
|
+
|
32
38
|
if @refresh_token
|
33
39
|
refresh
|
34
40
|
else
|
@@ -39,7 +45,7 @@ module Ecobee
|
|
39
45
|
end
|
40
46
|
|
41
47
|
def access_token
|
42
|
-
refresh
|
48
|
+
refresh
|
43
49
|
@access_token
|
44
50
|
end
|
45
51
|
|
@@ -61,6 +67,7 @@ module Ecobee
|
|
61
67
|
end
|
62
68
|
|
63
69
|
def refresh
|
70
|
+
return if Time.now.to_i + REFRESH_INTERVAL_PAD < @access_expires_at
|
64
71
|
response = Net::HTTP.post_form(
|
65
72
|
URI(URL_TOKEN),
|
66
73
|
'grant_type' => 'refresh_token',
|
@@ -69,14 +76,14 @@ module Ecobee
|
|
69
76
|
)
|
70
77
|
result = JSON.parse(response.body)
|
71
78
|
if result.key? 'error'
|
72
|
-
|
79
|
+
pp result
|
73
80
|
raise Ecobee::TokenError.new(
|
74
81
|
"Result Error: (%s) %s" % [result['error'],
|
75
82
|
result['error_description']]
|
76
83
|
)
|
77
84
|
else
|
78
85
|
@access_token = result['access_token']
|
79
|
-
@
|
86
|
+
@access_expires_at = Time.now.to_i + result['expires_in']
|
80
87
|
@refresh_token = result['refresh_token']
|
81
88
|
@pin, @code, @code_expires_at = nil
|
82
89
|
@scope = result['scope']
|
@@ -120,7 +127,7 @@ module Ecobee
|
|
120
127
|
@status = :ready
|
121
128
|
@access_token = result['access_token']
|
122
129
|
@type = result['token_type']
|
123
|
-
@
|
130
|
+
@access_expires_at = Time.now.to_i + result['expires_in']
|
124
131
|
@refresh_token = result['refresh_token']
|
125
132
|
@scope = result['scope']
|
126
133
|
@pin, @code, @code_expires_at = nil
|
@@ -134,6 +141,14 @@ module Ecobee
|
|
134
141
|
raise Ecobee::TokenError.new("Unknown Error: #{msg}")
|
135
142
|
end
|
136
143
|
|
144
|
+
def expand_files(token_file)
|
145
|
+
if token_file.is_a? Array
|
146
|
+
token_file.map { |tf| File.expand_path tf }
|
147
|
+
else
|
148
|
+
expand_files [token_file]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
137
152
|
def launch_monitor_thread
|
138
153
|
Thread.new {
|
139
154
|
loop do
|
@@ -145,34 +160,41 @@ module Ecobee
|
|
145
160
|
end
|
146
161
|
|
147
162
|
def parse_token_file
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
end
|
163
|
+
return unless (all_config = read_token_file).is_a? Hash
|
164
|
+
section = (@app_name && all_config.key?(@app_name)) ? @app_name : @app_key
|
165
|
+
return unless all_config.key?(section)
|
166
|
+
config = all_config[section]
|
167
|
+
@app_key ||= config.key?('app_key') ? config['app_key'] : @app_name
|
168
|
+
if config.key?('refresh_token')
|
169
|
+
@access_expires_at ||= config['access_expires_at'].to_i
|
170
|
+
@access_token ||= config['access_token']
|
171
|
+
@refresh_token ||= config['refresh_token']
|
172
|
+
@scope ||= config['scope']
|
173
|
+
@type ||= config['token_type']
|
174
|
+
elsif config.key?('pin')
|
175
|
+
@code ||= config['code']
|
176
|
+
@code_expires_at ||= config['code_expires_at'].to_i
|
177
|
+
@pin ||= config['pin']
|
164
178
|
end
|
165
|
-
#puts "After Parse: app_key:#{@app_key} refresh_token:#{@refresh_token} pin:#{pin}"
|
166
179
|
end
|
167
180
|
|
168
|
-
def
|
181
|
+
def read_json_file(file)
|
169
182
|
JSON.parse(
|
170
|
-
File.open(
|
183
|
+
File.open(file, 'r').read(16 * 1024)
|
171
184
|
)
|
185
|
+
rescue JSON::ParserError => msg
|
186
|
+
raise Ecobee::TokenError.new("Result parsing: #{msg}")
|
172
187
|
rescue Errno::ENOENT
|
173
188
|
{}
|
174
189
|
end
|
175
190
|
|
191
|
+
def read_token_file
|
192
|
+
@token_file.each do |tf|
|
193
|
+
result = read_json_file(tf)
|
194
|
+
return result if result.length > 0
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
176
198
|
def register
|
177
199
|
result = Register.new(app_key: @app_key, scope: @scope)
|
178
200
|
@pin = result.pin
|
@@ -184,7 +206,12 @@ module Ecobee
|
|
184
206
|
end
|
185
207
|
|
186
208
|
def write_token_file
|
187
|
-
|
209
|
+
@token_file.each do |file|
|
210
|
+
return if write_json_file file
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def write_json_file(file)
|
188
215
|
if config = read_token_file
|
189
216
|
config.delete(@app_name)
|
190
217
|
config.delete(@app_key)
|
@@ -193,15 +220,22 @@ module Ecobee
|
|
193
220
|
config[section] = {}
|
194
221
|
config[section]['app_key'] = @app_key if @app_key && section != @app_key
|
195
222
|
if @refresh_token
|
223
|
+
config[section]['access_token'] = @access_token
|
224
|
+
config[section]['access_expires_at'] = @access_expires_at
|
196
225
|
config[section]['refresh_token'] = @refresh_token
|
226
|
+
config[section]['token_type'] = @type
|
227
|
+
config[section]['scope'] = @scope
|
197
228
|
elsif @pin
|
198
229
|
config[section]['pin'] = @pin
|
199
230
|
config[section]['code'] = @code
|
200
231
|
config[section]['code_expires_at'] = @code_expires_at
|
201
232
|
end
|
202
|
-
File.open(
|
233
|
+
File.open(file, 'w') do |tf|
|
203
234
|
tf.puts JSON.pretty_generate(config)
|
204
235
|
end
|
236
|
+
true
|
237
|
+
rescue Errno::ENOENT
|
238
|
+
nil
|
205
239
|
end
|
206
240
|
|
207
241
|
end
|
data/lib/ecobee/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ecobee
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob Zwissler
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -70,7 +70,6 @@ files:
|
|
70
70
|
- bin/console
|
71
71
|
- bin/setup
|
72
72
|
- ecobee.gemspec
|
73
|
-
- examples/bitbar_plugin.rb
|
74
73
|
- examples/set_mode.rb
|
75
74
|
- examples/test_token.rb
|
76
75
|
- lib/ecobee.rb
|
@@ -90,7 +89,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
90
89
|
requirements:
|
91
90
|
- - ">="
|
92
91
|
- !ruby/object:Gem::Version
|
93
|
-
version:
|
92
|
+
version: 2.0.0
|
94
93
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
94
|
requirements:
|
96
95
|
- - ">="
|
data/examples/bitbar_plugin.rb
DELETED
@@ -1,252 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
#
|
3
|
-
# Allows for display and control of your Ecobee in the Mac OS X
|
4
|
-
# menubar, using BitBar (http://getbitbar.com). -- @robzr
|
5
|
-
#
|
6
|
-
# <bitbar.title>EcobeeStat</bitbar.title>
|
7
|
-
# <bitbar.version>v1.0</bitbar.version>
|
8
|
-
# <bitbar.author>Rob Zwissler</bitbar.author>
|
9
|
-
# <bitbar.author.github>robzr</bitbar.author.github>
|
10
|
-
# <bitbar.desc>Ecobee Thermostat Control</bitbar.desc>
|
11
|
-
# <bitbar.image>http://github.com/robzr/ecobee</bitbar.image>
|
12
|
-
# <bitbar.dependencies>ruby</bitbar.dependencies>
|
13
|
-
# <bitbar.abouturl>http://github.com/robzr/ecobee</bitbar.abouturl>
|
14
|
-
|
15
|
-
require 'pp'
|
16
|
-
require 'ecobee'
|
17
|
-
#require_relative '/Users/robzr/GitHub/ecobee/lib/ecobee.rb'
|
18
|
-
#require_relative '/Users/robzr/GitHub/ecobee/lib/ecobee/client.rb'
|
19
|
-
#require_relative '/Users/robzr/GitHub/ecobee/lib/ecobee/token.rb'
|
20
|
-
#require_relative '/Users/robzr/GitHub/ecobee/lib/ecobee/register.rb'
|
21
|
-
|
22
|
-
API_KEY = 'u2Krw0OumeliB0OnwiaogySvgExhy2K4'
|
23
|
-
HVAC_MODES = ['auto', 'auxHeatOnly', 'cool', 'heat', 'off', 'quit']
|
24
|
-
DEG = '°'
|
25
|
-
|
26
|
-
module Ecobee
|
27
|
-
class ResponseError < StandardError ; end
|
28
|
-
|
29
|
-
class BitBar
|
30
|
-
def initialize(client)
|
31
|
-
@client = client
|
32
|
-
end
|
33
|
-
|
34
|
-
def get_thermostat(args = {})
|
35
|
-
index = args.delete(:index) || 0
|
36
|
-
http_response = @client.get('thermostat',
|
37
|
-
Ecobee::Selection(args))
|
38
|
-
response = JSON.parse(http_response.body)
|
39
|
-
get_thermostat_list_index(index: index,
|
40
|
-
response: validate_status(response))
|
41
|
-
rescue JSON::ParserError => msg
|
42
|
-
raise ResponseError.new("JSON::ParserError => #{msg}")
|
43
|
-
end
|
44
|
-
|
45
|
-
def get_thermostat_list_index(index: 0, response: nil)
|
46
|
-
if !response.key? 'thermostatList'
|
47
|
-
raise ResponseError.new('Missing thermostatList')
|
48
|
-
elsif index >= response['thermostatList'].length
|
49
|
-
raise ResponseError.new(
|
50
|
-
"Missing thermostatList Index #{index} (Max Found: " +
|
51
|
-
"#{response['thermostatList'].length - 1})"
|
52
|
-
)
|
53
|
-
else
|
54
|
-
response['thermostatList'][index]
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def validate_status(response)
|
59
|
-
if !response.key? 'status'
|
60
|
-
raise ResponseError.new('Missing Status')
|
61
|
-
elsif !response['status'].key? 'code'
|
62
|
-
raise ResponseError.new('Missing Status Code')
|
63
|
-
elsif response['status']['code'] != 0
|
64
|
-
raise ResponseError.new(
|
65
|
-
"GET Error: #{response['status']['code']} " +
|
66
|
-
"Message: #{response['status']['message']}"
|
67
|
-
)
|
68
|
-
else
|
69
|
-
response
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# puts "Heat: #{info['runtime']['desiredHeat'] / 10}#{DEG} | color=red"
|
74
|
-
|
75
|
-
def set_hold(cool_hold: nil, heat_hold: nil)
|
76
|
-
functions = [{
|
77
|
-
'type' => 'setHold',
|
78
|
-
'params' => {
|
79
|
-
'holdType' => 'nextTransition',
|
80
|
-
}
|
81
|
-
}]
|
82
|
-
functions[0]['params']['coolHoldTemp'] = cool_hold
|
83
|
-
functions[0]['params']['heatHoldTemp'] = heat_hold
|
84
|
-
http_response = @client.post(
|
85
|
-
'thermostat',
|
86
|
-
body: {
|
87
|
-
'selection' => {
|
88
|
-
'selectionType' => 'registered',
|
89
|
-
'selectionMatch' => '',
|
90
|
-
},
|
91
|
-
'functions' => functions
|
92
|
-
}
|
93
|
-
)
|
94
|
-
response = JSON.parse(http_response.body)
|
95
|
-
end
|
96
|
-
|
97
|
-
def update_mode(mode)
|
98
|
-
http_response = @client.post(
|
99
|
-
'thermostat',
|
100
|
-
body: {
|
101
|
-
'selection' => {
|
102
|
-
'selectionType' => 'registered',
|
103
|
-
'selectionMatch' => '',
|
104
|
-
},
|
105
|
-
'thermostat' => {
|
106
|
-
'settings' => {
|
107
|
-
'hvacMode' => mode
|
108
|
-
}
|
109
|
-
}
|
110
|
-
}
|
111
|
-
)
|
112
|
-
response = JSON.parse(http_response.body)
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
def header(info)
|
118
|
-
puts "#{info['runtime']['actualTemperature'] / 10.0}#{DEG}"
|
119
|
-
puts '---'
|
120
|
-
end
|
121
|
-
|
122
|
-
def cool_menu(info)
|
123
|
-
present_mode = info['settings']['hvacMode']
|
124
|
-
return unless ['auto', 'cool'].include? present_mode
|
125
|
-
puts "Cool: #{info['runtime']['desiredCool'] / 10}#{DEG} | color=blue"
|
126
|
-
cool_low = info['settings']['coolRangeLow'] / 10
|
127
|
-
cool_high = info['settings']['coolRangeHigh'] / 10
|
128
|
-
(cool_low..cool_high).reverse_each do |temp|
|
129
|
-
flag, color = ''
|
130
|
-
flag = ' :arrow_left:' if temp == info['runtime']['actualTemperature'] / 10
|
131
|
-
color = ' color=blue' if temp == info['runtime']['desiredCool'] / 10
|
132
|
-
puts("--#{temp}#{DEG}#{flag}|#{color} bash=\"#{$0}\" " +
|
133
|
-
"param1=\"set_cool=#{temp}\" refresh=true terminal=false")
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def heat_menu(info)
|
138
|
-
present_mode = info['settings']['hvacMode']
|
139
|
-
return unless ['auto', 'auxHeatOnly', 'heat'].include? present_mode
|
140
|
-
puts "Heat: #{info['runtime']['desiredHeat'] / 10}#{DEG} | color=red"
|
141
|
-
heat_low = info['settings']['heatRangeLow'] / 10
|
142
|
-
heat_high = info['settings']['heatRangeHigh'] / 10
|
143
|
-
(heat_low..heat_high).reverse_each do |temp|
|
144
|
-
flag, color = ''
|
145
|
-
flag = ' :arrow_left:' if temp == info['runtime']['actualTemperature'] / 10
|
146
|
-
color = ' color=red' if temp == info['runtime']['desiredHeat'] / 10
|
147
|
-
puts("--#{temp}#{DEG}#{flag}|#{color} bash=\"#{$0}\" " +
|
148
|
-
"param1=\"set_heat=#{temp}\" refresh=true terminal=false")
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
def mode_menu(info)
|
153
|
-
puts "Mode: #{info['settings']['hvacMode']}"
|
154
|
-
Ecobee::HVAC_MODES.reject { |mode| mode == info['settings']['hvacMode'] }
|
155
|
-
.each do |mode|
|
156
|
-
puts("--#{mode} | bash=\"#{$0}\" param1=\"set_mode=#{mode}\" " +
|
157
|
-
"refresh=true terminal=false")
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
def separator
|
162
|
-
puts '---'
|
163
|
-
end
|
164
|
-
|
165
|
-
def stat_info(info)
|
166
|
-
puts info['name']
|
167
|
-
info['remoteSensors'].each do |sensor|
|
168
|
-
temp = sensor['capability'].select do |cap|
|
169
|
-
cap['type'] == 'temperature'
|
170
|
-
end
|
171
|
-
temp = temp[0]['value'].to_i / 10.0
|
172
|
-
puts "--#{sensor['name']}: #{temp}#{DEG}"
|
173
|
-
end
|
174
|
-
puts "#{info['brand']} #{Ecobee::Model(info['modelNumber'])}"
|
175
|
-
puts "Status: #{info['equipmentStatus']}"
|
176
|
-
end
|
177
|
-
|
178
|
-
def website
|
179
|
-
puts 'Ecobee Web Portal|href="https://www.ecobee.com/consumerportal/index.html"'
|
180
|
-
end
|
181
|
-
|
182
|
-
token = Ecobee::Token.new(
|
183
|
-
app_key: API_KEY, app_name: API_KEY,
|
184
|
-
scope: :smartWrite,
|
185
|
-
token_file: '~/.ecobee_token'
|
186
|
-
)
|
187
|
-
if token.pin
|
188
|
-
puts "Ecobee | color=red"
|
189
|
-
puts "---"
|
190
|
-
puts "Registration Needed | color=red"
|
191
|
-
puts "---"
|
192
|
-
puts 'Login to Ecobee | href=\'https://www.ecobee.com/consumerportal/index.html\''
|
193
|
-
puts 'Select \'My Apps\' from the drop-down menu'
|
194
|
-
puts 'Press the \'Add Application\' button'
|
195
|
-
puts "Enter authorization code: #{token.pin}"
|
196
|
-
exit
|
197
|
-
end
|
198
|
-
|
199
|
-
ecobar = Ecobee::BitBar.new Ecobee::Client.new(token: token)
|
200
|
-
|
201
|
-
case arg = ARGV.shift
|
202
|
-
when /^dump/
|
203
|
-
pp ecobar.get_thermostat(
|
204
|
-
:includeRuntime => true,
|
205
|
-
:includeExtendedRuntime => true,
|
206
|
-
:includeElectricity => true,
|
207
|
-
:includeSettings => true,
|
208
|
-
:includeLocation => true,
|
209
|
-
:includeProgram => true,
|
210
|
-
:includeEvents => true,
|
211
|
-
:includeDevice => true,
|
212
|
-
:includeTechnician => true,
|
213
|
-
:includeUtility => true,
|
214
|
-
:includeAlerts => true,
|
215
|
-
:includeWeather => true,
|
216
|
-
:includeOemConfig => true,
|
217
|
-
:includeEquipmentStatus => true,
|
218
|
-
:includeNotificationSettings => true,
|
219
|
-
:includeVersion => true,
|
220
|
-
:includeSensors => true
|
221
|
-
)
|
222
|
-
when /^set_mode=/
|
223
|
-
mode = arg.sub(/^.*=/, '')
|
224
|
-
ecobar.update_mode mode
|
225
|
-
when /^set_cool=/
|
226
|
-
info = ecobar.get_thermostat(includeRuntime: true,
|
227
|
-
includeSettings: true)
|
228
|
-
cool_hold = arg.sub(/^.*=/, '').to_i * 10
|
229
|
-
heat_hold = info['runtime']['desiredHeat']
|
230
|
-
|
231
|
-
ecobar.set_hold(cool_hold: cool_hold, heat_hold: heat_hold)
|
232
|
-
when /^set_heat=/
|
233
|
-
info = ecobar.get_thermostat(includeRuntime: true,
|
234
|
-
includeSettings: true)
|
235
|
-
cool_hold = info['runtime']['desiredCool']
|
236
|
-
heat_hold = arg.sub(/^.*=/, '').to_i * 10
|
237
|
-
|
238
|
-
ecobar.set_hold(cool_hold: cool_hold, heat_hold: heat_hold)
|
239
|
-
else
|
240
|
-
info = ecobar.get_thermostat(includeRuntime: true,
|
241
|
-
includeSettings: true,
|
242
|
-
includeEquipmentStatus: true,
|
243
|
-
includeSensors: true)
|
244
|
-
header info
|
245
|
-
cool_menu info
|
246
|
-
heat_menu info
|
247
|
-
separator
|
248
|
-
stat_info info
|
249
|
-
mode_menu info
|
250
|
-
separator
|
251
|
-
website
|
252
|
-
end
|