ecobee 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 33dcdf5851524cede3343a450c2af5914571141b
4
+ data.tar.gz: 4d142c7539b33ab29a1c019831d64e600786597c
5
+ SHA512:
6
+ metadata.gz: f6372f328df92b21c7da563126019b7f61e663fac9d28c933def8b9e6706aad77422f9c3a6a76c2a8e7fa82e343156c717c0491879f8da031c76ce9c4f033ce4
7
+ data.tar.gz: 770ce9ccde8db9ad2575e5ecc0e2fe7cf2b23870297e3a1c7265439cdd8980ff4da37e22cf3f4b39ad349d81b041b0121235ca0b8bd3131da5a32a02f67776b7
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .sw[p-z]
11
+ .*.sw[p-z]
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.0
5
+ before_install: gem install bundler -v 1.12.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ecobee.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Rob Zwissler
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # Ecobee
2
+
3
+ Ecobee API Ruby Gem. Handles OAuth PIN-based token registration & renewal, and methods for working with Ecobee GET & PUSH.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'ecobee'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install ecobee
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Development
26
+
27
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
28
+
29
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
30
+
31
+ ## Contributing
32
+
33
+ Bug reports and pull requests are welcome on GitHub at https://github.com/robzr/ecobee.
34
+
35
+ ## License
36
+
37
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
38
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "ecobee"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/ecobee.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ecobee/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ecobee"
8
+ spec.version = Ecobee::VERSION
9
+ spec.authors = ["Rob Zwissler"]
10
+ spec.email = ["rob@zwissler.org"]
11
+
12
+ spec.summary = %q{Ecobee API - token registration, persistent HTTP GET/PUSH }
13
+ spec.description = %q{Implements Ecobee API with PIN-based token registration & renewal, persistent HTTP connections }
14
+ spec.homepage = "https://github.com/robzr/ecobee"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.12"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec", "~> 3.0"
25
+ end
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pp'
4
+ require_relative '../lib/ecobee'
5
+
6
+ HVAC_MODES = ['auto', 'auxHeatOnly', 'cool', 'heat', 'off', 'quit']
7
+
8
+ class TestFunctions
9
+ def initialize(client)
10
+ @client = client
11
+ end
12
+
13
+ def print_summary
14
+ http_response = @client.get(
15
+ 'thermostat',
16
+ {
17
+ 'selection' => {
18
+ 'selectionType' => 'registered',
19
+ 'selectionMatch' => '',
20
+ 'includeEquipmentStatus' => 'true',
21
+ 'includeSettings' => 'true'
22
+ }
23
+ }
24
+ )
25
+ response = JSON.parse(http_response.body)
26
+
27
+ puts "Found %d thermostats." % response['thermostatList'].length
28
+
29
+ response['thermostatList'].each do |stat|
30
+ printf(
31
+ " -> %s (%s %s) Mode: %s Status: %s\n",
32
+ stat['name'],
33
+ stat['brand'],
34
+ Ecobee::Model(stat['modelNumber']),
35
+ stat['settings']['hvacMode'],
36
+ stat['equipmentStatus']
37
+ )
38
+ end
39
+ end
40
+
41
+ def update_mode(mode)
42
+ http_response = @client.post(
43
+ 'thermostat',
44
+ body: {
45
+ 'selection' => {
46
+ 'selectionType' => 'registered',
47
+ 'selectionMatch' => '',
48
+ },
49
+ 'thermostat' => {
50
+ 'settings' => {
51
+ 'hvacMode' => mode
52
+ }
53
+ }
54
+ }
55
+ )
56
+ response = JSON.parse(http_response.body)
57
+ end
58
+
59
+ end
60
+
61
+
62
+ token = Ecobee::Token.new(
63
+ api_key: ENV['ECOBEE_API_KEY'],
64
+ scope: :smartWrite,
65
+ token_file: '~/.ecobee_token'
66
+ )
67
+
68
+ puts token.pin_message if token.pin
69
+ token.wait
70
+
71
+ test_functions = TestFunctions.new(Ecobee::Client.new(token: token))
72
+
73
+ loop do
74
+ test_functions.print_summary
75
+
76
+ answer = -1
77
+ while !answer.between?(0, HVAC_MODES.length)
78
+ puts
79
+ (1..HVAC_MODES.length).each do |num|
80
+ printf "%d) %s\n", num, HVAC_MODES[num - 1]
81
+ end
82
+ print "Enter mode: "
83
+ answer = gets.to_i - 1
84
+ abort if answer == (HVAC_MODES.length - 1)
85
+ end
86
+ puts
87
+
88
+ result = test_functions.update_mode(HVAC_MODES[answer])
89
+
90
+ unless result.key?('status') && (result['status']['code'] == 0)
91
+ puts "Unknown result: #{result.to_s}\n"
92
+ end
93
+ end
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pp'
4
+
5
+ #require 'ecobee'
6
+ require_relative '../lib/ecobee'
7
+
8
+ token = Ecobee::Token.new(
9
+ api_key: ENV['ECOBEE_API_KEY'],
10
+ token_file: '~/.ecobee_token'
11
+ )
12
+
13
+ puts token.pin_message if token.pin
14
+ token.wait
15
+
16
+ puts "Access Token: #{token.access_token}"
17
+ puts "Refresh Token: #{token.refresh_token}"
18
+ puts "Expires At: #{token.expires_at}"
19
+ puts "Scope: #{token.scope}"
20
+ puts "Type: #{token.type}"
data/lib/ecobee.rb ADDED
@@ -0,0 +1,62 @@
1
+ require 'pp'
2
+ require 'json'
3
+ require 'net/http'
4
+
5
+ require_relative "ecobee/client"
6
+ require_relative "ecobee/register"
7
+ require_relative "ecobee/token"
8
+ require_relative "ecobee/version"
9
+
10
+ module Ecobee
11
+ API_HOST = 'api.ecobee.com'
12
+ API_PORT = 443
13
+ CONTENT_TYPE = ['application/json', { 'charset' => 'UTF-8' }]
14
+
15
+ DEFAULT_APP_NAME = 'ecobee-gem'
16
+
17
+ REFRESH_INTERVAL_PAD = 60
18
+ REFRESH_TOKEN_CHECK = 10
19
+
20
+ SCOPES = [:smartRead, :smartWrite]
21
+
22
+ URL_BASE= "https://#{API_HOST}:#{API_PORT}"
23
+
24
+ URL_API = "#{URL_BASE}/1/"
25
+ URL_GET_PIN = "#{URL_BASE}/authorize?response_type=ecobeePin&client_id=%s&scope=%s"
26
+ URL_TOKEN = "#{URL_BASE}/token"
27
+
28
+ def self.Model(model)
29
+ { 'idtSmart' => 'ecobee Smart',
30
+ 'idtEms' => 'ecobee Smart EMS',
31
+ 'siSmart' => 'ecobee Si Smart',
32
+ 'siEms' => 'ecobee Si EMS',
33
+ 'athenaSmart' => 'ecobee3 Smart',
34
+ 'athenaEms' => 'ecobee3 EMS',
35
+ 'corSmart' => 'Carrier or Bryant Cor',
36
+ }[model] || 'Unknown'
37
+ end
38
+
39
+ def self.ResponseCode(code)
40
+ { 0 => 'Success',
41
+ 1 => 'Authentication failed.',
42
+ 2 => 'Not authorized.',
43
+ 3 => 'Processing error.',
44
+ 4 => 'Serialization error.',
45
+ 5 => 'Invalid request format.',
46
+ 6 => 'Too many thermostat in selection match criteria.',
47
+ 7 => 'Validation error.',
48
+ 8 => 'Invalid function.',
49
+ 9 => 'Invalid selection.',
50
+ 10 => 'Invalid page.',
51
+ 11 => 'Function error.',
52
+ 12 => 'Post not supported for request.',
53
+ 13 => 'Get not supported for request.',
54
+ 14 => 'Authentication token has expired. Refresh your tokens.',
55
+ 15 => 'Duplicate data violation.',
56
+ 16 => 'Invalid token. Token has been deauthorized by user. You must ' +
57
+ 're-request authorization.'
58
+ }[core] || 'Unknown Error.'
59
+ end
60
+
61
+ end
62
+
@@ -0,0 +1,42 @@
1
+ module Ecobee
2
+
3
+ class Client
4
+
5
+ def initialize(token: nil)
6
+ raise ArgumentError.new('Missing token') unless token
7
+ @token = token
8
+ end
9
+
10
+ def get(arg, options = nil)
11
+ new_uri = URL_API + arg.sub(/^\//, '')
12
+ new_uri += '?json=' + options.to_json if options
13
+ request = Net::HTTP::Get.new(URI new_uri)
14
+ request['Content-Type'] = *CONTENT_TYPE
15
+ request['Authorization'] = @token.authorization
16
+ http.request(request)
17
+ end
18
+
19
+ def post(arg, options: {}, body: nil)
20
+ new_uri = URL_API + arg.sub(/^\//, '')
21
+ request = Net::HTTP::Post.new(URI new_uri)
22
+ request.set_form_data({ 'format': 'json' }.merge(options))
23
+ request.body = JSON.generate(body) if body
24
+ request['Content-Type'] = *CONTENT_TYPE
25
+ request['Authorization'] = @token.authorization
26
+ http.request(request)
27
+ end
28
+
29
+ private
30
+
31
+ def http
32
+ @http ||= Net::HTTP.new(API_HOST, API_PORT)
33
+ unless @http.active?
34
+ @http.use_ssl = true
35
+ Net::HTTP.start(API_HOST, API_PORT)
36
+ end
37
+ @http
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,47 @@
1
+ module Ecobee
2
+
3
+ class Register
4
+ attr_reader :result
5
+
6
+ def initialize(api_key: nil, scope: SCOPES[0])
7
+ raise ArgumentError.new('Missing api_key') unless api_key
8
+
9
+ @result = get_pin(api_key: api_key, scope: scope)
10
+ end
11
+
12
+ def code
13
+ @result['code']
14
+ end
15
+
16
+ def interval
17
+ @result['interval']
18
+ end
19
+
20
+ def pin
21
+ @result['ecobeePin']
22
+ end
23
+
24
+ private
25
+
26
+ def get_pin(api_key: nil, scope: nil)
27
+ uri_pin = URI(URL_GET_PIN % [api_key, scope.to_s])
28
+ result = JSON.parse Net::HTTP.get(uri_pin)
29
+ if result.key? 'error'
30
+ raise Ecobee::RegisterError.new(
31
+ "Result Error: (%s) %s" % [result['error'], result['error_description']]
32
+ )
33
+ end
34
+ result
35
+ rescue SocketError => msg
36
+ raise Ecobee::RegisterError.new("GET failed: #{msg}")
37
+ rescue JSON::ParserError => msg
38
+ raise Ecobee::RegisterError.new("Result parsing: #{msg}")
39
+ rescue Exception => msg
40
+ raise Ecobee::RegisterError.new("Unknown Error: #{msg}")
41
+ end
42
+ end
43
+
44
+ class RegisterError < StandardError
45
+ end
46
+
47
+ end
@@ -0,0 +1,169 @@
1
+ module Ecobee
2
+
3
+ class Token
4
+ attr_reader :access_token,
5
+ :expires_at,
6
+ :pin,
7
+ :pin_message,
8
+ :refresh_token,
9
+ :result,
10
+ :status,
11
+ :scope,
12
+ :type
13
+
14
+ def initialize(
15
+ api_key: nil,
16
+ app_name: DEFAULT_APP_NAME,
17
+ code: nil,
18
+ refresh_token: nil,
19
+ scope: SCOPES[0],
20
+ token_file: nil
21
+ )
22
+ @api_key = api_key
23
+ @app_name = app_name
24
+ @code = code
25
+ @access_token, @expires_at, @pin, @type = nil
26
+ @refresh_token = refresh_token
27
+ @scope = scope
28
+ @status = :authorization_pending
29
+ @token_file = File.expand_path(token_file)
30
+ read_token_file unless @refresh_token
31
+ if @refresh_token
32
+ refresh
33
+ else
34
+ register unless code
35
+ check_for_token
36
+ launch_monitor_thread unless @status == :ready
37
+ end
38
+ end
39
+
40
+ def access_token
41
+ refresh if Time.now + REFRESH_INTERVAL_PAD > @expires_at
42
+ @access_token
43
+ end
44
+
45
+ def authorization
46
+ "#{@type} #{@access_token}"
47
+ end
48
+
49
+ def pin_message
50
+ "Log into Ecobee web portal, select My Apps widget, Add Application, " +
51
+ "enter the PIN #{@pin || ''}"
52
+ end
53
+
54
+ def refresh
55
+ response = Net::HTTP.post_form(
56
+ URI(URL_TOKEN),
57
+ 'grant_type' => 'refresh_token',
58
+ 'refresh_token' => @refresh_token,
59
+ 'client_id' => @api_key
60
+ )
61
+ result = JSON.parse(response.body)
62
+ if result.key? 'error'
63
+ # pp result
64
+ raise Ecobee::TokenError.new(
65
+ "Result Error: (%s) %s" % [result['error'],
66
+ result['error_description']]
67
+ )
68
+ else
69
+ @access_token = result['access_token']
70
+ @expires_at = Time.now + result['expires_in']
71
+ @refresh_token = result['refresh_token']
72
+ @scope = result['scope']
73
+ @type = result['token_type']
74
+ @status = :ready
75
+ write_token_file
76
+ end
77
+ rescue SocketError => msg
78
+ raise Ecobee::TokenError.new("POST failed: #{msg}")
79
+ rescue JSON::ParserError => msg
80
+ raise Ecobee::TokenError.new("Result parsing: #{msg}")
81
+ rescue Exception => msg
82
+ raise Ecobee::TokenError.new("Unknown Error: #{msg}")
83
+ end
84
+
85
+ def wait
86
+ sleep 0.05 while @status == :authorization_pending
87
+ @status
88
+ end
89
+
90
+ private
91
+
92
+ def check_for_token
93
+ response = Net::HTTP.post_form(
94
+ URI(URL_TOKEN),
95
+ 'grant_type' => 'ecobeePin',
96
+ 'code' => @code,
97
+ 'client_id' => @api_key
98
+ )
99
+ result = JSON.parse(response.body)
100
+ if result.key? 'error'
101
+ unless ['slow_down', 'authorization_pending'].include? result['error']
102
+ pp result
103
+ raise Ecobee::TokenError.new(
104
+ "Result Error: (%s) %s" % [result['error'],
105
+ result['error_description']]
106
+ )
107
+ end
108
+ else
109
+ @status = :ready
110
+ @access_token = result['access_token']
111
+ @type = result['token_type']
112
+ @expires_at = Time.now + result['expires_in']
113
+ @refresh_token = result['refresh_token']
114
+ @scope = result['scope']
115
+ write_token_file
116
+ end
117
+ rescue SocketError => msg
118
+ raise Ecobee::TokenError.new("POST failed: #{msg}")
119
+ rescue JSON::ParserError => msg
120
+ raise Ecobee::TokenError.new("Result parsing: #{msg}")
121
+ rescue Exception => msg
122
+ raise Ecobee::TokenError.new("Unknown Error: #{msg}")
123
+ end
124
+
125
+ def launch_monitor_thread
126
+ Thread.new {
127
+ loop do
128
+ sleep REFRESH_TOKEN_CHECK
129
+ break if @status == :ready
130
+ check_for_token
131
+ end
132
+ }
133
+ end
134
+
135
+ def read_token_file
136
+ tf = File.open(@token_file, 'r').read(16 * 1024)
137
+ config = JSON.parse(tf)
138
+ if config.key? @app_name
139
+ @app_key ||= config[@app_name]['app_key']
140
+ @refresh_token = config[@app_name]['refresh_token']
141
+ end
142
+ rescue Errno::ENOENT
143
+ end
144
+
145
+ def register
146
+ result = Register.new(api_key: @api_key, scope: @scope)
147
+ @pin = result.pin
148
+ @code = result.code
149
+ result
150
+ end
151
+
152
+ def write_token_file
153
+ return unless @token_file
154
+ File.open(@token_file, 'w') do |tf|
155
+ tf.puts JSON.pretty_generate({
156
+ @app_name => {
157
+ 'app_key' => @app_key,
158
+ 'refresh_token' => @refresh_token
159
+ }
160
+ })
161
+ end
162
+ end
163
+
164
+ end
165
+
166
+ class TokenError < StandardError
167
+ end
168
+
169
+ end
@@ -0,0 +1,3 @@
1
+ module Ecobee
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ecobee
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Rob Zwissler
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-08-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: 'Implements Ecobee API with PIN-based token registration & renewal, persistent
56
+ HTTP connections '
57
+ email:
58
+ - rob@zwissler.org
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - ".rspec"
65
+ - ".travis.yml"
66
+ - Gemfile
67
+ - LICENSE.txt
68
+ - README.md
69
+ - Rakefile
70
+ - bin/console
71
+ - bin/setup
72
+ - ecobee.gemspec
73
+ - examples/set_mode.rb
74
+ - examples/test_token.rb
75
+ - lib/ecobee.rb
76
+ - lib/ecobee/client.rb
77
+ - lib/ecobee/register.rb
78
+ - lib/ecobee/token.rb
79
+ - lib/ecobee/version.rb
80
+ homepage: https://github.com/robzr/ecobee
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 2.5.1
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: Ecobee API - token registration, persistent HTTP GET/PUSH
104
+ test_files: []