class Ecobee::Token

Attributes

access_token[R]
access_token_expire[R]
app_key[R]
callbacks[R]
http[R]
pin[R]
result[R]
scope[R]
status[R]
token_file[R]
token_type[R]

Public Class Methods

new( access_token: nil, access_token_expire: nil, app_key: nil, callbacks: {}, log_file: nil, refresh_token: nil, scope: SCOPES[0], token_file: DEFAULT_FILES ) click to toggle source
# File lib/ecobee/token.rb, line 23
def initialize(
  access_token: nil,
  access_token_expire: nil,
  app_key: nil,
  callbacks: {},
  log_file: nil,
  refresh_token: nil,
  scope: SCOPES[0],
  token_file: DEFAULT_FILES
)
  @access_token = access_token
  @access_token_expire = access_token_expire
  @app_key = app_key
  @callbacks = callbacks
  @refresh_token = refresh_token
  @scope = scope
  @token_file = expand_files token_file

  @authorization_thread, @pin, @status, @token_type = nil
  @poll_interval = DEFAULT_POLL_INTERVAL

  @http = Ecobee::HTTP.new(log_file: log_file, token: self)

  @refresh_pad = REFRESH_PAD + rand(REFRESH_PAD)

  config_load
  access_token()
end

Public Instance Methods

access_token_expired?() click to toggle source
# File lib/ecobee/token.rb, line 83
def access_token_expired?
  return true unless @access_token_expire
  Time.now.to_i > @access_token_expire - @refresh_pad
end
authorization() click to toggle source
# File lib/ecobee/token.rb, line 88
def authorization
  "#{@token_type} #{access_token}"
end
config_load() click to toggle source
# File lib/ecobee/token.rb, line 92
def config_load
  # TODO: track file mod time & last_read to avoid excessive rereads
  config = config_read_our_section
  if @callbacks[:load].respond_to? :call
    config = @callbacks[:load].call(config)
  end
  config_load_to_memory config
end
config_save() click to toggle source
# File lib/ecobee/token.rb, line 101
def config_save
  config = config_dump()
  if @callbacks[:save].respond_to? :call
    config = @callbacks[:save].call(config)
  end
  config_write_section config
end
pin_is_valid() click to toggle source
# File lib/ecobee/token.rb, line 109
def pin_is_valid
  if @pin && @access_token && @access_token_expire
    @access_token_expire.to_i >= Time.now.to_i
  end
end
pin_message() click to toggle source
# File lib/ecobee/token.rb, line 115
def pin_message
  "Log into Ecobee web portal, select My Apps widget, Add Application, " +
  "enter the PIN #{@pin || ''}"
end
refresh_access_token(retries: 0) click to toggle source
# File lib/ecobee/token.rb, line 120
def refresh_access_token(retries: 0)
  arg = sprintf("?grant_type=refresh_token&refresh_token=%s&client_id=%s",
                refresh_token,
                @app_key)
  result = @http.post(arg: arg,
                      no_auth: true,
                      resource_prefix: 'token',
                      validate_status: false)
  if result.key? 'error'
    if retries > 0 && result['error'] == 'invalid_grant'
      raise Ecobee::RetryAuthError.new
    end
    @access_token, @access_token_expire, @pin, @scope, @refresh_token = nil
    config_save
    raise Ecobee::AuthError.new("Result Error: (%s) %s" % [
                                result['error'],
                                result['error_description']])
  else
    result_load_to_memory result
  end 
end
refresh_access_token_wrapper() click to toggle source
# File lib/ecobee/token.rb, line 142
def refresh_access_token_wrapper
  retries = 3
  retries.times do |attempt|
    begin
      refresh_access_token(retries: retries - attempt)
    rescue Ecobee::RetryAuthError.new
      sleep 10
    end
  end
end
refresh_token() click to toggle source
# File lib/ecobee/token.rb, line 153
def refresh_token
  config_load
  @refresh_token
end
register_callback(type, *callback, &block) click to toggle source
# File lib/ecobee/token.rb, line 158
def register_callback(type, *callback, &block)
  if block_given?
    puts "Registering #{type}"
    @callbacks[type] = block
  else
    @callbacks[type] = callback[0] if callback.length > 0
  end
end
wait(timeout: nil) click to toggle source
# File lib/ecobee/token.rb, line 167
def wait(timeout: nil)
  if timeout
    Timeout::timeout(timeout) { wait(timeout: nil) }
  else
    sleep 0.01 while @status == :authorization_pending
  end
rescue Timeout::Error
ensure
  @status
end

Private Instance Methods

check_for_authorization() click to toggle source

arrives here, expired

# File lib/ecobee/token.rb, line 181
def check_for_authorization
  do_check_for_authorization
  if @status == :authorization_pending
    unless @authorization_thread && @authorization_thread.alive?
      @authorization_thread = Thread.new {
        loop do
          sleep @poll_interval
          break if @status == :ready
          do_check_for_authorization
        end
      }
    end
  end
end
config_dump() click to toggle source
# File lib/ecobee/token.rb, line 289
def config_dump
  config = {}
  config['access_token'] = @access_token
  config['access_token_expire'] = @access_token_expire
  if @refresh_token
    config['refresh_token'] = @refresh_token 
    config['scope'] = @scope
    config['token_type'] = @token_type
  elsif @pin
    config['pin'] = @pin
  end
  config
end
config_load_to_memory(config) click to toggle source
# File lib/ecobee/token.rb, line 235
def config_load_to_memory(config)
  @app_key ||= config['app_key']
  if !@access_token
    @access_token = config['access_token']
    @access_token_expire = config['access_token_expire'].to_i
  elsif(config.key?('access_token') && 
        config['access_token_expire'].to_i > @access_token_expire)
    @access_token = config['access_token']
    @access_token_expire = config['access_token_expire'].to_i
    if config['refresh_token']
      @refresh_token = config['refresh_token'] 
      @scope = config['scope']
      @token_type = config['token_type']
    elsif config.key?('pin')
      @pin = config['pin']
    end
  end
  if config.key?('refresh_token')
    @refresh_token ||= config['refresh_token']
    @scope ||= config['scope']
    @token_type ||= config['token_type']
  elsif config.key?('pin')
    @pin ||= config['pin']
  end
end
config_read_all_sections() click to toggle source
# File lib/ecobee/token.rb, line 271
def config_read_all_sections
  @token_file.each do |tf|
     result = config_read_file(tf)
     return result if result.length > 0
  end
  {}
end
config_read_file(file) click to toggle source
# File lib/ecobee/token.rb, line 279
def config_read_file(file)
  JSON.parse(
    File.open(file, 'r').read(16 * 1024)
  )
rescue JSON::ParserError => msg
  raise Ecobee::AuthError.new("Result parsing: #{msg}")
rescue Errno::ENOENT
  {}
end
config_read_our_section() click to toggle source
# File lib/ecobee/token.rb, line 261
def config_read_our_section
  all_config = config_read_all_sections
  if @app_name && all_config.key?(@app_name)
    our_section = @app_name
  else
    our_section = @app_key
  end
  return all_config[our_section] || {}
end
config_write_file(config: nil, file_name: nil) click to toggle source
# File lib/ecobee/token.rb, line 314
def config_write_file(config: nil, file_name: nil)
  File.open(file_name, 'w') do |file|
    file.puts JSON.pretty_generate(config)
  end
  true
rescue Errno::ENOENT
  nil
end
config_write_section(config) click to toggle source
# File lib/ecobee/token.rb, line 303
def config_write_section(config)
  all_config = config_read_all_sections
  all_config.delete(@app_key)
  all_config[@app_key] = config

  @token_file.each do |file_name|
    return true if config_write_file(config: all_config, file_name: file_name)
  end
  nil
end
do_check_for_authorization() click to toggle source
# File lib/ecobee/token.rb, line 196
def do_check_for_authorization
  arg = sprintf("?grant_type=ecobeePin&code=%s&client_id=%s",
                @access_token,
                @app_key)
  result = @http.post(arg: arg,
                      no_auth: true,
                      resource_prefix: 'token',
                      validate_status: false)
  if result.key? 'error'
    @status = :authorization_pending
    if result['error'] == 'invalid_client'
      token_register 
    elsif ['slow_down', 'authorization_pending'].include? result['error']
      nil
    else
      @access_token, @access_token_expire, @pin, @scope, @refresh_token = nil
      config_save
      raise Ecobee::AuthError.new(
        "Result Error: (%s) %s" % [result['error'],
                                   result['error_description']]
      )
    end
  else
    result_load_to_memory result
  end
end
expand_files(token_file) click to toggle source
# File lib/ecobee/token.rb, line 323
def expand_files(token_file)
  if token_file.is_a? NilClass
    nil
  elsif token_file.is_a? Array
    token_file.map { |tf| File.expand_path tf }
  else
    expand_files [token_file]
  end
end
result_load_to_memory(result) click to toggle source
# File lib/ecobee/token.rb, line 223
def result_load_to_memory(result)
  @status = :ready
  @access_token = result['access_token']
  @access_token_expire = Time.now.to_i + result['expires_in']
  @pin = nil
  @refresh_token = result['refresh_token']
  @scope = result['scope']
  @token_type = result['token_type']
  config_save
  @access_token
end
token_register() click to toggle source
# File lib/ecobee/token.rb, line 333
def token_register
  @status = :authorization_pending
  result = Ecobee::Register.new(app_key: @app_key,
                                http: @http,
                                scope: @scope)
  @poll_interval = result.interval
  @pin = result.pin
  @access_token = result.code
  @access_token_expire = result.expire
  @refresh_token = nil
  @scope = result.scope
  check_for_authorization
  config_save
  @access_token
end