ecobee 0.2.3 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6e5259700881085205481c34338397602ab0c167
4
- data.tar.gz: 6595615983c9aa45463c4ecc6b38edb1c049852e
3
+ metadata.gz: 48dab0a8186e1abe5f05115a744bf06a6167b718
4
+ data.tar.gz: d76e8de7fbf97ae44095436197f1d7085a324d9d
5
5
  SHA512:
6
- metadata.gz: 6bfd85b6e3e227d5366585fc467d97593eeaba79147b69fdbca978e9f7f3023a9c1ea62a5b93615c3a2ca6e7ede24d5d9ba95df9ed902a1389918f8f6f938371
7
- data.tar.gz: ad7c7b641b5262038958ca581a396d75e40468366e4c79ae688fa9ee778a67665f1225e27a13392559117b41cf6a48e2c581c5a8bd2585d04de104c6e24ca949
6
+ metadata.gz: 7e5ca066357160e4f43a67d90f6186437cf98f7a1105f1dba652eac447c588cec03c43f8c2e44f3caf791a0eabca9fc4b64f213f4a588e32c0ad9411cade0c05
7
+ data.tar.gz: 0bbc4eb430f37858787f915b10ab4af799fac335651344357e41becf7a883e3dd090d9359438e04ab0b9de50671df6cd4ab084b051b8ed244be8b777142c157a
data/README.md CHANGED
@@ -5,17 +5,14 @@ Ecobee API Ruby Gem. Implements:
5
5
  - Persistent HTTP connection management
6
6
  - Methods for GET & POST requests w/ JSON parsing & error handling
7
7
  - Persistent storage for API key & refresh tokens
8
+ - Storage uses iCloud Drive if available for shared computer use
8
9
  - Block/Proc hooks for token storage load/save to add app config data
9
- - Abstraction class for simple thermostat interaction
10
+ - Thermostat abstraction class for simple thermostat interaction
10
11
  - Example usage scripts (see /examples/\*)
11
12
 
12
13
  TODO:
13
- - Add dedicated symbol class to Thermostat
14
14
  - Add RDoc documentation
15
- - Add timeout to Ecobee::Token#wait
16
15
  - Add redirect based registration
17
- - Implement throttling algorithm based on API feedback
18
- - Create examples of proper error handling
19
16
 
20
17
  ## Installation
21
18
 
@@ -1,25 +1,25 @@
1
1
  #!/usr/bin/env ruby
2
2
  #
3
+ # Example of using the Ecobee::HTTP API directly (bypassing the
4
+ # Ecobee::Thermostat abstraction class).
5
+ #
3
6
  # Loops through menu showing current thermostat mode, with option
4
- # to change the mode. -- @robzr
7
+ # to change the mode.
5
8
 
6
9
  require 'pp'
7
-
8
10
  require_relative '../lib/ecobee'
9
-
10
11
  @hvac_modes = Ecobee::HVAC_MODES + ['quit']
11
12
 
12
13
  class TestFunctions
13
- def initialize(client)
14
- @client = client
14
+ def initialize(token)
15
+ @http = token.http
15
16
  end
16
17
 
17
18
  def print_summary
18
- response = @client.get(:thermostat,
19
- Ecobee::Selection(
20
- includeEquipmentStatus: true,
21
- includeSettings: true
22
- ))
19
+ response = @http.get(arg: :thermostat,
20
+ options: Ecobee::Selection(
21
+ includeEquipmentStatus: true,
22
+ includeSettings: true))
23
23
 
24
24
  puts "Found %d thermostats." % response['thermostatList'].length
25
25
 
@@ -36,12 +36,12 @@ class TestFunctions
36
36
  end
37
37
 
38
38
  def update_mode(mode)
39
- @client.post('thermostat',
40
- body: {
41
- 'thermostat' => {
42
- 'settings' => { 'hvacMode' => mode }
43
- }
44
- }.merge(Ecobee::Selection()))
39
+ @http.post(arg: :thermostat,
40
+ body: {
41
+ 'thermostat' => {
42
+ 'settings' => { 'hvacMode' => mode }
43
+ }
44
+ }.merge(Ecobee::Selection()))
45
45
  end
46
46
  end
47
47
 
@@ -52,9 +52,7 @@ if token.pin
52
52
  token.wait
53
53
  end
54
54
 
55
- test_functions = TestFunctions.new(
56
- Ecobee::Client.new(token: token)
57
- )
55
+ test_functions = TestFunctions.new(token)
58
56
 
59
57
  loop do
60
58
  test_functions.print_summary
@@ -2,18 +2,26 @@ require 'pp'
2
2
  require 'json'
3
3
  require 'net/http'
4
4
 
5
- require_relative 'ecobee/client'
5
+ require_relative 'ecobee/http'
6
6
  require_relative 'ecobee/register'
7
7
  require_relative 'ecobee/thermostat'
8
8
  require_relative 'ecobee/token'
9
9
  require_relative 'ecobee/version'
10
10
 
11
11
  module Ecobee
12
+
13
+ class HTTPError < StandardError ; end
14
+
15
+ class AuthError < HTTPError ; end
16
+
12
17
  API_HOST = 'api.ecobee.com'
13
18
  API_PORT = 443
19
+ API_URI_BASE= "https://#{API_HOST}:#{API_PORT}"
14
20
 
15
21
  CONTENT_TYPE = ['application/json', { 'charset' => 'UTF-8' }]
16
22
 
23
+ DEFAULT_POLL_INTERVAL = 30
24
+
17
25
  DEFAULT_FILES = [
18
26
  '~/Library/Mobile Documents/com~apple~CloudDocs/.ecobee_token',
19
27
  '~/.ecobee_token'
@@ -30,16 +38,12 @@ module Ecobee
30
38
 
31
39
  HVAC_MODES = %w{auto auxHeatOnly cool heat off}
32
40
 
41
+ MAX_LOG_LENGTH = 1200
42
+
33
43
  REFRESH_PAD = 30
34
- REFRESH_TOKEN_CHECK = 10
35
44
 
36
45
  SCOPES = [:smartWrite, :smartRead]
37
-
38
- URL_BASE= "https://#{API_HOST}:#{API_PORT}"
39
- URL_API = "#{URL_BASE}/1/"
40
- URL_GET_PIN = URL_BASE +
41
- '/authorize?response_type=ecobeePin&client_id=%s&scope=%s'
42
- URL_TOKEN = "#{URL_BASE}/token"
46
+ DEFAULT_SCOPE = SCOPES[1]
43
47
 
44
48
  def self.FanMode(mode)
45
49
  { 'auto' => 'Auto',
@@ -0,0 +1,143 @@
1
+ module Ecobee
2
+
3
+ class HTTPError < StandardError ; end
4
+ class AuthError < HTTPError ; end
5
+
6
+ class HTTP
7
+
8
+ def initialize(log_file: nil, token: nil)
9
+ raise ArgumentError.new('Missing token') unless token
10
+ @token = token
11
+ open_log log_file
12
+ http
13
+ end
14
+
15
+ def get(
16
+ arg: nil,
17
+ no_auth: false,
18
+ resource_prefix: '1/',
19
+ retries: 3,
20
+ options: nil,
21
+ validate_status: true
22
+ )
23
+ uri = URI.escape(sprintf("#{Ecobee::API_URI_BASE}/%s%s%s",
24
+ resource_prefix,
25
+ arg.to_s.sub(/^\//, ''),
26
+ options ? "?json=#{options.to_json}" : ''))
27
+ log "http.get uri=#{uri}"
28
+ request = Net::HTTP::Get.new(URI(uri))
29
+ request['Content-Type'] = *CONTENT_TYPE
30
+ request['Authorization'] = @token.authorization unless no_auth
31
+ response = nil
32
+ retries.times do
33
+ http_response = http.request request
34
+ response = JSON.parse(http_response.body)
35
+ log "http.get response=#{response.pretty_inspect}"
36
+ response = validate_status(response) if validate_status
37
+ break unless response == :retry
38
+ sleep 3
39
+ end
40
+ case response
41
+ when :retry
42
+ raise Ecobee::HTTPError.new('HTTP.get: retries exhausted')
43
+ else
44
+ response
45
+ end
46
+ rescue SocketError => msg
47
+ raise Ecobee::HTTPError.new("HTTP.get SocketError => #{msg}")
48
+ rescue JSON::ParserError => msg
49
+ raise Ecobee::HTTPError.new("HTTP.get JSON::ParserError => #{msg}")
50
+ end
51
+
52
+ def log(arg)
53
+ return unless @log_fh
54
+ if arg.length > MAX_LOG_LENGTH
55
+ arg = arg.slice(0, MAX_LOG_LENGTH).chomp + "\n ...truncated..."
56
+ end
57
+ @log_fh.puts "#{Time.now} #{arg.chomp}"
58
+ @log_fh.flush
59
+ end
60
+
61
+ def post(
62
+ arg: nil,
63
+ body: nil,
64
+ no_auth: false,
65
+ resource_prefix: '1/',
66
+ retries: 3,
67
+ options: {},
68
+ validate_status: true
69
+ )
70
+ uri = URI.escape(sprintf("#{Ecobee::API_URI_BASE}/%s%s%s",
71
+ resource_prefix,
72
+ arg.to_s.sub(/^\//, ''),
73
+ options.length > 0 ? "?json=#{options.to_json}" : ''))
74
+ log "http.post uri=#{uri}"
75
+ request = Net::HTTP::Post.new(URI(uri))
76
+ request['Content-Type'] = *CONTENT_TYPE
77
+ request['Authorization'] = @token.authorization unless no_auth
78
+ if body
79
+ log "http.post body=#{body.pretty_inspect}"
80
+ request.body = JSON.generate(body)
81
+ elsif options.length > 0
82
+ request.set_form_data({ 'format' => 'json' }.merge(options))
83
+ end
84
+ response = nil
85
+ retries.times do
86
+ http_response = http.request request
87
+ response = JSON.parse(http_response.body)
88
+ log "http.post response=#{response.pretty_inspect}"
89
+ response = validate_status(response) if validate_status
90
+ break unless response == :retry
91
+ sleep 3
92
+ end
93
+ case response
94
+ when :retry
95
+ raise Ecobee::HTTPError.new('HTTP.get: retries exhausted')
96
+ else
97
+ response
98
+ end
99
+ rescue SocketError => msg
100
+ raise Ecobee::HTTPError.new("HTTP.get SocketError => #{msg}")
101
+ rescue JSON::ParserError => msg
102
+ raise Ecobee::HTTPError.new("HTTP.get JSON::ParserError => #{msg}")
103
+ end
104
+
105
+ private
106
+
107
+ def http
108
+ @http ||= Net::HTTP.new(API_HOST, API_PORT)
109
+ unless @http.active?
110
+ @http.use_ssl = true
111
+ Net::HTTP.start(API_HOST, API_PORT)
112
+ end
113
+ @http
114
+ end
115
+
116
+ def open_log(log_file)
117
+ return unless log_file
118
+ log_file = File.expand_path log_file
119
+ @log_fh = File.new(log_file, 'a')
120
+ rescue Exception => msg
121
+ raise Ecobee::HTTPError.new("open_log: #{msg}")
122
+ end
123
+
124
+ def validate_status(response)
125
+ if !response.key? 'status'
126
+ raise Ecobee::HTTPError.new('Validate Error: Missing Status')
127
+ elsif !response['status'].key? 'code'
128
+ raise Ecobee::HTTPError.new('Validate Error: Missing Status Code')
129
+ elsif response['status']['code'] == 14
130
+ :retry
131
+ elsif response['status']['code'] != 0
132
+ raise Ecobee::HTTPError.new(
133
+ "Validate Error: (Code #{response['status']['code']}) " +
134
+ "#{response['status']['message']}"
135
+ )
136
+ else
137
+ response
138
+ end
139
+ end
140
+
141
+ end
142
+
143
+ end
@@ -1,12 +1,15 @@
1
1
  module Ecobee
2
2
 
3
3
  class Register
4
- attr_reader :expires_at, :result
5
-
6
- def initialize(app_key: nil, scope: SCOPES[0])
7
- raise ArgumentError.new('Missing app_key') unless app_key
8
- @result = get_pin(app_key: app_key, scope: scope)
9
- @expires_at = Time.now.to_i + result['expires_in'] * 60
4
+ attr_reader :expire, :result
5
+
6
+ def initialize(
7
+ app_key: nil,
8
+ http: nil,
9
+ scope: DEFAULT_SCOPE
10
+ )
11
+ @result = get_pin(app_key: app_key, http: http, scope: scope)
12
+ @expire = Time.now.to_i + result['expires_in'] * 60
10
13
  end
11
14
 
12
15
  def code
@@ -25,26 +28,22 @@ module Ecobee
25
28
  @result['scope']
26
29
  end
27
30
 
28
- private
31
+ private
29
32
 
30
- def get_pin(app_key: nil, scope: nil)
31
- uri_pin = URI(URL_GET_PIN % [app_key, scope.to_s])
32
- result = JSON.parse Net::HTTP.get(uri_pin)
33
+ def get_pin(app_key: nil, http: nil, scope: nil)
34
+ scope = scope.to_s if scope.is_a? Symbol
35
+ arg = "?response_type=ecobeePin&client_id=#{app_key}&scope=#{scope}"
36
+ result = http.get(arg: arg,
37
+ no_auth: true,
38
+ resource_prefix: 'authorize',
39
+ validate_status: false)
33
40
  if result.key? 'error'
34
- raise Ecobee::TokenError.new(
35
- sprintf("Register Error: (%s) %s",
36
- result['error'],
37
- result['error_description'])
41
+ raise Ecobee::AuthError.new(
42
+ "Register Error: (#{result['error']}) #{result['error_description']}"
38
43
  )
39
44
  else
40
45
  result
41
46
  end
42
- rescue SocketError => msg
43
- raise Ecobee::TokenError.new("GET failed: #{msg}")
44
- rescue JSON::ParserError => msg
45
- raise Ecobee::TokenError.new("Parse Error: #{msg}")
46
- # rescue Exception => msg
47
- # raise Ecobee::TokenError.new("Unknown Error: #{msg}")
48
47
  end
49
48
 
50
49
  end
@@ -24,12 +24,10 @@ module Ecobee
24
24
  includeSensors: true
25
25
  }
26
26
 
27
- attr_accessor :client
28
- attr_reader :auto_refresh
27
+ attr_reader :auto_refresh, :http
29
28
 
30
29
  def initialize(
31
30
  auto_refresh: 0,
32
- client: nil,
33
31
  fake_index: nil,
34
32
  index: 0,
35
33
  fake_max_index: 0,
@@ -37,9 +35,11 @@ module Ecobee
37
35
  selection_args: {},
38
36
  token: nil
39
37
  )
40
- @auto_refresh = auto_refresh
41
38
  # TODO: add auto-refresh thread handling
42
- @client = client || Ecobee::Client.new(token: token)
39
+ @auto_refresh = auto_refresh
40
+
41
+ raise ArgumentError.new('No token: specified') unless token
42
+ @http = token.http
43
43
  @fake_index = fake_index
44
44
  @fake_max_index = fake_max_index
45
45
  @index = index
@@ -156,7 +156,7 @@ module Ecobee
156
156
  end
157
157
 
158
158
  def refresh
159
- response = @client.get(:thermostat, @selection)
159
+ response = @http.get(arg: :thermostat, options: @selection)
160
160
  if @index + 1 > response['thermostatList'].length
161
161
  raise ThermostatError.new('No such thermostat')
162
162
  end
@@ -240,7 +240,7 @@ module Ecobee
240
240
  body = my_selection
241
241
  body.merge!({ 'functions' => functions }) if functions
242
242
  body.merge!({ 'thermostat' => thermostat }) if thermostat
243
- @client.post(:thermostat, body: body)
243
+ @http.post(:thermostat, body: body)
244
244
  end
245
245
 
246
246
  end
@@ -5,15 +5,21 @@ module Ecobee
5
5
  :access_token_expire,
6
6
  :app_key,
7
7
  :callbacks,
8
+ :http,
8
9
  :pin,
9
10
  :refresh_token,
10
11
  :result,
11
12
  :status,
12
13
  :scope,
14
+ :token_file,
13
15
  :token_type
14
16
 
15
17
  #AUTH_ERRORS = %w(slow_down authorization_pending authorization_expired)
16
- #VALID_STATUS = [:authorization_pending, :ready]
18
+
19
+ @STATUSES = {
20
+ authorization_pending: 'Registration begun but has not been approved.',
21
+ ready: 'Registration approved and valid token received.'
22
+ }
17
23
 
18
24
  def initialize(
19
25
  access_token: nil,
@@ -33,6 +39,8 @@ module Ecobee
33
39
  @token_file = expand_files token_file
34
40
 
35
41
  @authorization_thread, @pin, @status, @token_type = nil
42
+ @poll_interval = DEFAULT_POLL_INTERVAL
43
+ @http = Ecobee::HTTP.new(log_file: "/tmp/token.log", token: self)
36
44
 
37
45
  @refresh_pad = REFRESH_PAD + rand(REFRESH_PAD)
38
46
 
@@ -41,15 +49,33 @@ module Ecobee
41
49
  end
42
50
 
43
51
  def access_token
44
- if(@access_token && @access_token_expire && !access_token_expired?)
45
- @status = (@refresh_token ? :ready : :authorization_pending)
46
- @access_token
47
- else
48
- refresh_access_token
49
- end
52
+ if @access_token
53
+ if access_token_expired?
54
+ if @refresh_token
55
+ refresh_access_token
56
+ else
57
+ token_register
58
+ end
59
+ else
60
+ desired_status = (@refresh_token ? :ready : :authorization_pending)
61
+ if @refresh_token
62
+ if @status != desired_status
63
+ puts "Status: MISMATCH: #{@status} vs #{desired_status}" if @status
64
+ @status = desired_status
65
+ end
66
+ @access_token
67
+ else
68
+ check_for_authorization
69
+ end
70
+ end
71
+ else
72
+ @status = :authorization_pending
73
+ token_register
74
+ end
50
75
  end
51
76
 
52
77
  def access_token_expired?
78
+ return true unless @access_token_expire
53
79
  Time.now.to_i > @access_token_expire - @refresh_pad
54
80
  end
55
81
 
@@ -85,27 +111,20 @@ module Ecobee
85
111
  end
86
112
 
87
113
  def refresh_access_token
88
- response = Net::HTTP.post_form(
89
- URI(URL_TOKEN),
90
- 'grant_type' => 'refresh_token',
91
- 'refresh_token' => @refresh_token || 0,
92
- 'client_id' => @app_key
93
- )
94
- result = JSON.parse(response.body)
114
+ arg = sprintf("?grant_type=refresh_token&refresh_token=%s&client_id=%s",
115
+ @refresh_token,
116
+ @app_key)
117
+ result = @http.post(arg: arg,
118
+ no_auth: true,
119
+ resource_prefix: 'token',
120
+ validate_status: false)
95
121
  if result.key? 'error'
96
- if result['error'] == 'invalid_grant'
97
- if access_token_expired?
98
- register
99
- else
100
- check_for_authorization
101
- end
102
- else
103
- puts "DUMPING(result): #{result.pretty_inspect}"
104
- raise Ecobee::TokenError.new(
105
- "Result Error: (%s) %s" % [result['error'],
106
- result['error_description']]
107
- )
108
- end
122
+ @access_token, @access_token_expire, @pin, @scope, @refresh_token = nil
123
+ config_save
124
+ raise Ecobee::AuthError.new(
125
+ "Result Error: (%s) %s" % [result['error'],
126
+ result['error_description']]
127
+ )
109
128
  else
110
129
  @access_token = result['access_token']
111
130
  @access_token_expire = Time.now.to_i + result['expires_in']
@@ -117,12 +136,6 @@ module Ecobee
117
136
  config_save
118
137
  @access_token
119
138
  end
120
- rescue SocketError => msg
121
- raise Ecobee::TokenError.new("POST failed: #{msg}")
122
- rescue JSON::ParserError => msg
123
- raise Ecobee::TokenError.new("Result parsing: #{msg}")
124
- # rescue Exception => msg
125
- # raise Ecobee::TokenError.new("Unknown Error: #{msg}")
126
139
  end
127
140
 
128
141
  def register_callback(type, *callback, &block)
@@ -134,8 +147,14 @@ module Ecobee
134
147
  end
135
148
  end
136
149
 
137
- def wait
138
- sleep 0.05 while @status == :authorization_pending
150
+ def wait(timeout: nil)
151
+ if timeout
152
+ Timeout::timeout(timeout) { wait(timeout: nil) }
153
+ else
154
+ sleep 0.01 while @status == :authorization_pending
155
+ end
156
+ rescue Timeout::Error
157
+ ensure
139
158
  @status
140
159
  end
141
160
 
@@ -148,9 +167,10 @@ module Ecobee
148
167
  unless @authorization_thread && @authorization_thread.alive?
149
168
  @authorization_thread = Thread.new {
150
169
  loop do
151
- # TODO: consider some intelligent throttling
152
- sleep REFRESH_TOKEN_CHECK
170
+ puts "Sleeping for #{@poll_interval}"
171
+ sleep @poll_interval
153
172
  break if @status == :ready
173
+ puts "check_for_authorization_single"
154
174
  check_for_authorization_single
155
175
  end
156
176
  }
@@ -159,21 +179,23 @@ module Ecobee
159
179
  end
160
180
 
161
181
  def check_for_authorization_single
162
- response = Net::HTTP.post_form(
163
- URI(URL_TOKEN),
164
- 'grant_type' => 'ecobeePin',
165
- 'code' => @access_token,
166
- 'client_id' => @app_key
167
- )
168
- result = JSON.parse(response.body)
182
+ arg = sprintf("?grant_type=ecobeePin&code=%s&client_id=%s",
183
+ @access_token,
184
+ @app_key)
185
+ result = @http.post(arg: arg,
186
+ no_auth: true,
187
+ resource_prefix: 'token',
188
+ validate_status: false)
169
189
  if result.key? 'error'
170
190
  @status = :authorization_pending
171
191
  if result['error'] == 'invalid_client'
172
- register
173
- elsif !['slow_down', 'authorization_pending'].include? result['error']
174
- # TODO: throttle or just ignore...?
175
- pp result
176
- raise Ecobee::TokenError.new(
192
+ token_register
193
+ elsif ['slow_down', 'authorization_pending'].include? result['error']
194
+ nil
195
+ else
196
+ @access_token, @access_token_expire, @pin, @scope, @refresh_token = nil
197
+ config_save
198
+ raise Ecobee::AuthError.new(
177
199
  "Result Error: (%s) %s" % [result['error'],
178
200
  result['error_description']]
179
201
  )
@@ -189,12 +211,6 @@ module Ecobee
189
211
  config_save
190
212
  @access_token
191
213
  end
192
- rescue SocketError => msg
193
- raise Ecobee::TokenError.new("POST failed: #{msg}")
194
- rescue JSON::ParserError => msg
195
- raise Ecobee::TokenError.new("Result parsing: #{msg}")
196
- # rescue Exception => msg
197
- # raise Ecobee::TokenError.new("Unknown Error: #{msg}")
198
214
  end
199
215
 
200
216
  def config_load_to_memory(config)
@@ -246,7 +262,7 @@ module Ecobee
246
262
  File.open(file, 'r').read(16 * 1024)
247
263
  )
248
264
  rescue JSON::ParserError => msg
249
- raise Ecobee::TokenError.new("Result parsing: #{msg}")
265
+ raise Ecobee::AuthError.new("Result parsing: #{msg}")
250
266
  rescue Errno::ENOENT
251
267
  {}
252
268
  end
@@ -295,21 +311,22 @@ module Ecobee
295
311
  end
296
312
  end
297
313
 
298
- def register
314
+ def token_register
299
315
  @status = :authorization_pending
300
- result = Register.new(app_key: @app_key, scope: @scope)
316
+ result = Ecobee::Register.new(app_key: @app_key,
317
+ http: @http,
318
+ scope: @scope)
319
+ @poll_interval = result.interval
301
320
  @pin = result.pin
302
321
  @access_token = result.code
303
- @access_token_expire = result.expires_at
322
+ @access_token_expire = result.expire
304
323
  @refresh_token = nil
305
324
  @scope = result.scope
306
325
  check_for_authorization
307
326
  config_save
308
- @access_token if @status == :ready
327
+ @access_token
309
328
  end
310
329
 
311
330
  end
312
331
 
313
- class TokenError < StandardError ; end
314
-
315
332
  end
@@ -1,3 +1,3 @@
1
1
  module Ecobee
2
- VERSION = "0.2.3"
2
+ VERSION = "0.3.0"
3
3
  end
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.2.3
4
+ version: 0.3.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-18 00:00:00.000000000 Z
11
+ date: 2016-08-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -75,7 +75,7 @@ files:
75
75
  - examples/test_thermostat_object.rb
76
76
  - examples/test_token.rb
77
77
  - lib/ecobee.rb
78
- - lib/ecobee/client.rb
78
+ - lib/ecobee/http.rb
79
79
  - lib/ecobee/register.rb
80
80
  - lib/ecobee/thermostat.rb
81
81
  - lib/ecobee/token.rb
@@ -1,75 +0,0 @@
1
- module Ecobee
2
-
3
- class Client
4
- def initialize(token: nil)
5
- raise ArgumentError.new('Missing token') unless token
6
- @token = token
7
- end
8
-
9
- def get(arg, options = nil)
10
- new_uri = URL_API + arg.to_s.sub(/^\//, '')
11
- new_uri += '?json=' + options.to_json if options
12
- request = Net::HTTP::Get.new(URI(URI.escape(new_uri)))
13
- request['Content-Type'] = *CONTENT_TYPE
14
- request['Authorization'] = @token.authorization
15
- http_response = http.request request
16
- response = validate_status JSON.parse(http_response.body)
17
- # if response == :retry
18
- # get(arg, options)
19
- # else
20
- # response
21
- # end
22
- rescue JSON::ParserError => msg
23
- raise ClientError.new("JSON::ParserError => #{msg}")
24
- end
25
-
26
- def post(arg, options: {}, body: nil)
27
- new_uri = URL_API + arg.to_s.sub(/^\//, '')
28
- request = Net::HTTP::Post.new(URI new_uri)
29
- request.set_form_data({ 'format' => 'json' }.merge(options))
30
- request.body = JSON.generate(body) if body
31
- request['Content-Type'] = *CONTENT_TYPE
32
- request['Authorization'] = @token.authorization
33
- http_response = http.request request
34
- response = validate_status JSON.parse http_response.body
35
- # if response == :retry
36
- # post(arg, options: options, body: body)
37
- # else
38
- # response
39
- # end
40
- rescue JSON::ParserError => msg
41
- raise ClientError.new("JSON::ParserError => #{msg}")
42
- end
43
-
44
- def validate_status(response)
45
- if !response.key? 'status'
46
- raise ClientError.new('Missing Status')
47
- elsif !response['status'].key? 'code'
48
- raise ClientError.new('Missing Status Code')
49
- elsif response['status']['code'] == 14
50
- :retry
51
- elsif response['status']['code'] != 0
52
- raise ClientError.new(
53
- "GET Error: #{response['status']['code']} " +
54
- "Message: #{response['status']['message']}"
55
- )
56
- else
57
- response
58
- end
59
- end
60
-
61
- private
62
-
63
- def http
64
- @http ||= Net::HTTP.new(API_HOST, API_PORT)
65
- unless @http.active?
66
- @http.use_ssl = true
67
- Net::HTTP.start(API_HOST, API_PORT)
68
- end
69
- @http
70
- end
71
- end
72
-
73
- class ClientError < StandardError ; end
74
-
75
- end