ecobee 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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