smartcar 0.0.0 → 1.0.3
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 +5 -5
- data/.gitignore +20 -0
- data/.rspec +3 -0
- data/.travis.yml +28 -0
- data/.yardopts +2 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +59 -0
- data/LICENSE.txt +21 -0
- data/README.md +147 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/smartcar.rb +53 -0
- data/lib/smartcar/base.rb +72 -0
- data/lib/smartcar/battery.rb +13 -0
- data/lib/smartcar/charge.rb +13 -0
- data/lib/smartcar/engine_oil.rb +12 -0
- data/lib/smartcar/fuel.rb +15 -0
- data/lib/smartcar/location.rb +10 -0
- data/lib/smartcar/oauth.rb +91 -0
- data/lib/smartcar/odometer.rb +9 -0
- data/lib/smartcar/permissions.rb +9 -0
- data/lib/smartcar/tire_pressure.rb +19 -0
- data/lib/smartcar/user.rb +26 -0
- data/lib/smartcar/utils.rb +35 -0
- data/lib/smartcar/vehicle.rb +294 -0
- data/lib/smartcar/vehicle_attributes.rb +12 -0
- data/lib/smartcar/version.rb +4 -0
- data/lib/smartcar/vin.rb +10 -0
- data/ruby-sdk.gemspec +33 -0
- metadata +151 -14
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'oauth2'
|
2
|
+
require 'base64'
|
3
|
+
module Smartcar
|
4
|
+
# The Base class for all of the other class.
|
5
|
+
# Let other classes inherit from here and put common methods here.
|
6
|
+
class Base
|
7
|
+
include Utils
|
8
|
+
|
9
|
+
# Error raised when an invalid parameter is passed.
|
10
|
+
class InvalidParameterValue < StandardError; end
|
11
|
+
# Constant for Bearer auth type
|
12
|
+
BEARER = 'BEARER'.freeze
|
13
|
+
# Constant for Basic auth type
|
14
|
+
BASIC = 'BASIC'.freeze
|
15
|
+
|
16
|
+
attr_accessor :token, :error, :meta
|
17
|
+
|
18
|
+
%i{get post patch put delete}.each do |verb|
|
19
|
+
# meta programming and define all Restful methods.
|
20
|
+
# @param path [String] the path to hit for the request.
|
21
|
+
# @param data [Hash] request body if needed.
|
22
|
+
#
|
23
|
+
# @return [Hash] The response Json parsed as a hash.
|
24
|
+
define_method verb do |path, data=nil|
|
25
|
+
response = service.send(verb) do |request|
|
26
|
+
request.headers['Authorization'] = "BEARER #{token}"
|
27
|
+
request.headers['Authorization'] = "BASIC #{get_basic_auth}" if data[:auth] == BASIC
|
28
|
+
request.headers['sc-unit-system'] = unit_system
|
29
|
+
request.headers['Content-Type'] = "application/json"
|
30
|
+
complete_path = "/#{API_VERSION}#{path}"
|
31
|
+
if verb==:get
|
32
|
+
request.url complete_path, data
|
33
|
+
else
|
34
|
+
request.url complete_path
|
35
|
+
request.body = data.to_json if data
|
36
|
+
end
|
37
|
+
end
|
38
|
+
error = get_error(response)
|
39
|
+
raise error if error
|
40
|
+
[JSON.parse(response.body), response.headers]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# This requires a proc 'PATH' to be defined in the class
|
45
|
+
# @param path [String] resource path
|
46
|
+
# @param options [Hash] query params
|
47
|
+
# @param auth [String] type of auth
|
48
|
+
#
|
49
|
+
# @return [Object]
|
50
|
+
def fetch(path: , options: {}, auth: 'BEARER')
|
51
|
+
_path = path
|
52
|
+
_path += "?#{URI.encode_www_form(options)}" unless options.empty?
|
53
|
+
get(_path, {auth: auth})
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# returns auth token for BASIC auth
|
59
|
+
#
|
60
|
+
# @return [String] Base64 encoding of CLIENT:SECRET
|
61
|
+
def get_basic_auth
|
62
|
+
Base64.strict_encode64("#{get_config('CLIENT_ID')}:#{get_config('CLIENT_SECRET')}")
|
63
|
+
end
|
64
|
+
|
65
|
+
# gets a smartcar API service/client
|
66
|
+
#
|
67
|
+
# @return [OAuth2::AccessToken] An initialized AccessToken instance that acts as service client
|
68
|
+
def service
|
69
|
+
@service ||= Faraday.new(url: SITE)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Smartcar
|
2
|
+
# class to represent Battery info
|
3
|
+
#@attr [Number] percentRemaining Decimal value representing the remaining charge percent.
|
4
|
+
#@attr [Number] range Remaining range of the vehicle.
|
5
|
+
class Battery < Base
|
6
|
+
# Path Proc for hitting battery end point
|
7
|
+
PATH = Proc.new{|id| "/vehicles/#{id}/battery"}
|
8
|
+
attr_reader :percentRemaining, :range
|
9
|
+
|
10
|
+
# just to have Ruby-esque method names
|
11
|
+
alias_method :percentage_remaining, :percentRemaining
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Smartcar
|
2
|
+
# class to represent Charge info
|
3
|
+
#@attr [Boolean] isPluggedIn Specifies if the vehicle is plugged in.
|
4
|
+
#@attr [String] state Charging state of the vehicle.
|
5
|
+
class Charge < Base
|
6
|
+
# Path Proc for hitting charge end point
|
7
|
+
PATH = Proc.new{|id| "/vehicles/#{id}/charge"}
|
8
|
+
attr_reader :isPluggedIn, :state
|
9
|
+
|
10
|
+
# just to have Ruby-esque method names
|
11
|
+
alias_method :is_plugged_in?, :isPluggedIn
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Smartcar
|
2
|
+
# class to represent Engine oil info
|
3
|
+
#@attr [Number] lifeRemaining Remaining life of the engine oil
|
4
|
+
class EngineOil < Base
|
5
|
+
# Path Proc for hitting engine oil end point
|
6
|
+
PATH = Proc.new{|id| "/vehicles/#{id}/engine/oil"}
|
7
|
+
attr_reader :lifeRemaining
|
8
|
+
|
9
|
+
# just to have Ruby-esque method names
|
10
|
+
alias_method :life_remaining, :lifeRemaining
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Smartcar
|
2
|
+
# class to represent Fuel info
|
3
|
+
#@attr [Number] amountRemaining Amount of fuel remaining.
|
4
|
+
#@attr [Number] percentageRemaining Decimal value representing the remaining fuel percent.
|
5
|
+
#@attr [Number] range Remaining range of the vehicle.
|
6
|
+
class Fuel < Base
|
7
|
+
# Path Proc for hitting fuel end point
|
8
|
+
PATH = Proc.new{|id| "/vehicles/#{id}/fuel"}
|
9
|
+
attr_reader :amountRemaining, :percentRemaining, :range
|
10
|
+
|
11
|
+
# just to have Ruby-esque method names
|
12
|
+
alias_method :amount_remaining, :amountRemaining
|
13
|
+
alias_method :percent_remaining, :percentRemaining
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Smartcar
|
2
|
+
# class to represent Location info
|
3
|
+
#@attr [Number] latitude Latitude of last recorded location.
|
4
|
+
#@attr [Number] longitude Longitude of last recorded location.
|
5
|
+
class Location < Base
|
6
|
+
# Path Proc for hitting location end point
|
7
|
+
PATH = Proc.new{|id| "/vehicles/#{id}/location"}
|
8
|
+
attr_reader :latitude, :longitude
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Smartcar
|
2
|
+
# Oauth class to take care of the Oauth 2.0 with Smartcar APIs
|
3
|
+
#
|
4
|
+
class Oauth < Base
|
5
|
+
extend Utils
|
6
|
+
# By default users are not shown the permission dialog if they have already
|
7
|
+
# approved the set of scopes for this application. The application can elect
|
8
|
+
# to always display the permissions dialog to the user by setting
|
9
|
+
# approval_prompt to `force`.
|
10
|
+
#
|
11
|
+
# @param options [Hash]
|
12
|
+
# @option options[:client_id] [String] - Client ID, if not passed fallsback to ENV['CLIENT_ID']
|
13
|
+
# @option options[:client_secret] [String] - Client Secret, if not passed fallsback to ENV['CLIENT_SECRET']
|
14
|
+
# @option options[:redirect_uri] [String] - Redirect URI, if not passed fallsback to ENV['REDIRECT_URI']
|
15
|
+
# @option options[:state] [String] - OAuth state parameter passed to the
|
16
|
+
# redirect uri. This parameter may be used for identifying the user who
|
17
|
+
# initiated the request.
|
18
|
+
# @option options[:test_mode] [Boolean] - Setting this to 'true' runs it in test mode.
|
19
|
+
# @option options[:force_prompt] [Boolean] - Setting `force_prompt` to
|
20
|
+
# `true` will show the permissions approval screen on every authentication
|
21
|
+
# attempt, even if the user has previously consented to the exact scope of
|
22
|
+
# permissions.
|
23
|
+
# @option options[:make] [String] - `make' is an optional parameter that allows
|
24
|
+
# users to bypass the car brand selection screen.
|
25
|
+
# For a complete list of supported makes, please see our
|
26
|
+
# [API Reference](https://smartcar.com/docs/api#authorization) documentation.
|
27
|
+
# @option options[:scope] [Array of Strings] - array of scopes that specify what the user can access
|
28
|
+
# EXAMPLE : ['read_odometer', 'read_vehicle_info', 'required:read_location']
|
29
|
+
# For further details refer to https://smartcar.com/docs/guides/scope/
|
30
|
+
#
|
31
|
+
# @return [Smartcar::Oauth] Returns a Smartcar::Oauth Object that has other methods
|
32
|
+
def initialize(options)
|
33
|
+
@redirect_uri = options[:redirect_uri] || get_config('REDIRECT_URI')
|
34
|
+
@client_id = options[:client_id] || get_config('CLIENT_ID')
|
35
|
+
@client_secret = options[:client_secret] || get_config('CLIENT_SECRET')
|
36
|
+
|
37
|
+
@auth_parameters = {
|
38
|
+
redirect_uri: @redirect_uri,
|
39
|
+
approval_prompt: options[:force_prompt] ? FORCE : AUTO,
|
40
|
+
mode: options[:test_mode] ? TEST : LIVE,
|
41
|
+
response_type: CODE
|
42
|
+
}
|
43
|
+
@auth_parameters[:scope] = options[:scope].join(' ') if options[:scope]
|
44
|
+
%I(state make).each do |parameter|
|
45
|
+
parameters[:parameter] = options[:parameter] unless options[:parameter].nil?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Generate the OAuth authorization URL.
|
50
|
+
#
|
51
|
+
# @return [String] Authorization URL string
|
52
|
+
def authorization_url
|
53
|
+
client.auth_code.authorize_url(@auth_parameters)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Generates the tokens hash using the code returned in oauth process.
|
57
|
+
# @param auth_code [String] This is the code that is returned after user
|
58
|
+
# visits and authorizes on the authorization URL.
|
59
|
+
#
|
60
|
+
# @return [Hash] Hash of token, refresh token, expiry info and token type
|
61
|
+
def get_token(auth_code)
|
62
|
+
client.auth_code
|
63
|
+
.get_token(
|
64
|
+
auth_code,
|
65
|
+
redirect_uri: @redirect_uri
|
66
|
+
).to_hash
|
67
|
+
end
|
68
|
+
|
69
|
+
# Refreshing the access token
|
70
|
+
# @param token_hash [Hash] This is the hash that is returned with the
|
71
|
+
# get_token method
|
72
|
+
#
|
73
|
+
# @return [Hash] Hash of token, refresh token, expiry info and token type
|
74
|
+
def refresh_token(token_hash)
|
75
|
+
token_object = OAuth2::AccessToken.from_hash(client, token_hash)
|
76
|
+
token_object = token_object.refresh!
|
77
|
+
token_object.to_hash
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
# gets the Oauth Client object
|
82
|
+
#
|
83
|
+
# @return [OAuth2::Client] A Oauth Client object.
|
84
|
+
def client
|
85
|
+
@client ||= OAuth2::Client.new( @client_id,
|
86
|
+
@client_secret,
|
87
|
+
:site => OAUTH_PATH
|
88
|
+
)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Smartcar
|
2
|
+
# class to represent permissions response
|
3
|
+
#@attr [Array] permissions Array of permissions granted on the vehicle.
|
4
|
+
class Permissions < Base
|
5
|
+
# Path Proc for hitting permissions end point
|
6
|
+
PATH = Proc.new{|id| "/vehicles/#{id}/permissions"}
|
7
|
+
attr_reader :permissions
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Smartcar
|
2
|
+
# class to represent Tire Pressure response
|
3
|
+
#@attr [Number] back_left Last recorded tire pressure of the back left tire.
|
4
|
+
#@attr [Number] back_right Last recorded tire pressure of the back right tire.
|
5
|
+
#@attr [Number] front_left Last recorded tire pressure of the front left tire.
|
6
|
+
#@attr [Number] front_right Last recorded tire pressure of the front right tire.
|
7
|
+
|
8
|
+
class TirePressure < Base
|
9
|
+
# Path Proc for hitting tire pressure end point
|
10
|
+
PATH = Proc.new{|id| "/vehicles/#{id}/tires/pressure"}
|
11
|
+
attr_reader :backLeft, :backRight, :frontLeft, :frontRight
|
12
|
+
|
13
|
+
# just to have Ruby-esque method names
|
14
|
+
alias_method :back_left, :backLeft
|
15
|
+
alias_method :back_right, :backRight
|
16
|
+
alias_method :front_left, :frontLeft
|
17
|
+
alias_method :front_right, :frontRight
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Smartcar
|
2
|
+
# Class to get to user API.
|
3
|
+
#@attr [String] id Smartcar user id.
|
4
|
+
#@attr [String] token Access token used to connect to Smartcar API.
|
5
|
+
class User < Base
|
6
|
+
# Path for hitting user end point
|
7
|
+
USER_PATH = '/user'.freeze
|
8
|
+
attr_reader :id, :token
|
9
|
+
|
10
|
+
def initialize(token:)
|
11
|
+
raise InvalidParameterValue.new, "Access Token(token) is a required field" if token.nil?
|
12
|
+
@token = token
|
13
|
+
end
|
14
|
+
|
15
|
+
# Class method Used to get user id
|
16
|
+
# EX : Smartcar::User.fetch
|
17
|
+
# API - https://smartcar.com/docs/api#get-user
|
18
|
+
# @param token [String] Access token
|
19
|
+
#
|
20
|
+
# @return [User] object
|
21
|
+
def self.user_id(token:)
|
22
|
+
new(token: token).get(USER_PATH)['id']
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Utils module , provides utility methods to underlying classes
|
2
|
+
module Utils
|
3
|
+
# A constructor to take a hash and assign it to the instance variables
|
4
|
+
# @param options = {} [Hash] Could by any class's hash, but the first level keys should be defined in the class
|
5
|
+
#
|
6
|
+
# @return [Subclass os Base] Returns object of any subclass like Report
|
7
|
+
def initialize(options = {})
|
8
|
+
options.each do |attribute, value|
|
9
|
+
instance_variable_set("@#{attribute}", value)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# gets a given env variable, checks for existence and throws exception if not present
|
14
|
+
# @param config_name [String] key of the env variable
|
15
|
+
#
|
16
|
+
# @return [String] value of the env variable
|
17
|
+
def get_config(config_name)
|
18
|
+
config_name = "INTEGRATION_#{config_name}" if ENV['MODE'] == 'test'
|
19
|
+
raise Smartcar::ConfigNotFound, "Environment variable #{config_name} not found !" unless ENV[config_name]
|
20
|
+
ENV[config_name]
|
21
|
+
end
|
22
|
+
|
23
|
+
# Given the response from smartcar API, returns an error object if needed
|
24
|
+
# @param response [Object] response Object with status and body
|
25
|
+
#
|
26
|
+
# @return [Object] nil OR Error object
|
27
|
+
def get_error(response)
|
28
|
+
status = response.status
|
29
|
+
return nil if [200,204].include?(status)
|
30
|
+
return Smartcar::ServiceUnavailableError.new("Service Unavailable - #{response.body}") if status == 404
|
31
|
+
return Smartcar::BadRequestError.new("Bad Request - #{response.body}") if status == 400
|
32
|
+
return Smartcar::AuthenticationError.new("Authentication error") if status == 401
|
33
|
+
return Smartcar::ExternalServiceError.new("API error - #{response.body}")
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,294 @@
|
|
1
|
+
module Smartcar
|
2
|
+
# Vehicle class to connect to vehicle basic info,disconnect, lock unlock and get all vehicles API
|
3
|
+
# For ease of use, this also has methods define to be able to call other resources on a vehicle object
|
4
|
+
# For Ex. Vehicle object will be treate as an entity and doing vehicle_object.
|
5
|
+
# Battery should return Battery object.
|
6
|
+
#
|
7
|
+
#@attr [String] token Access token used to connect to Smartcar API.
|
8
|
+
#@attr [String] id Smartcar vehicle ID.
|
9
|
+
#@attr [String] unit_system unit system to represent the data in.
|
10
|
+
class Vehicle < Base
|
11
|
+
include Utils
|
12
|
+
|
13
|
+
|
14
|
+
# Path for hitting compatibility end point
|
15
|
+
COMPATIBLITY_PATH = '/compatibility'.freeze
|
16
|
+
|
17
|
+
# Path for hitting vehicle ids end point
|
18
|
+
PATH = Proc.new{|id| "/vehicles/#{id}"}
|
19
|
+
|
20
|
+
attr_reader :id
|
21
|
+
attr_accessor :token, :unit_system
|
22
|
+
|
23
|
+
def initialize(token:, id:, unit_system: IMPERIAL)
|
24
|
+
raise InvalidParameterValue.new, "Invalid Units provided : #{unit_system}" unless UNITS.include?(unit_system)
|
25
|
+
raise InvalidParameterValue.new, "Vehicle ID (id) is a required field" if id.nil?
|
26
|
+
raise InvalidParameterValue.new, "Access Token(token) is a required field" if token.nil?
|
27
|
+
@token = token
|
28
|
+
@id = id
|
29
|
+
@unit_system = unit_system
|
30
|
+
end
|
31
|
+
|
32
|
+
# Class method Used to get all the vehicles in the app. This only returns
|
33
|
+
# API - https://smartcar.com/docs/api#get-all-vehicles
|
34
|
+
# @param token [String] - Access token
|
35
|
+
# @param options [Hash] - Optional filter parameters (check documentation)
|
36
|
+
#
|
37
|
+
# @return [Array] of vehicle IDs(Strings)
|
38
|
+
def self.all_vehicle_ids(token:, options: {})
|
39
|
+
response, meta = new(token: token, id: 'none').fetch(
|
40
|
+
path: PATH.call(''),
|
41
|
+
options: options
|
42
|
+
)
|
43
|
+
response['vehicles']
|
44
|
+
end
|
45
|
+
|
46
|
+
# Class method Used to check compatiblity for VIN and scope
|
47
|
+
# API - https://smartcar.com/docs/api#connect-compatibility
|
48
|
+
# @param vin [String] VIN of the vehicle to be checked
|
49
|
+
# @param scope [Array of Strings] - array of scopes
|
50
|
+
#
|
51
|
+
# @return [Boolean] true or false
|
52
|
+
def self.compatible?(vin:, scope:)
|
53
|
+
raise InvalidParameterValue.new, "vin is a required field" if vin.nil?
|
54
|
+
raise InvalidParameterValue.new, "scope is a required field" if scope.nil?
|
55
|
+
|
56
|
+
response, meta = new(token: 'none', id: 'none').fetch(path: COMPATIBLITY_PATH,
|
57
|
+
options: {
|
58
|
+
vin: vin,
|
59
|
+
scope: scope.join(' ')
|
60
|
+
},
|
61
|
+
auth: BASIC
|
62
|
+
)
|
63
|
+
response['compatible']
|
64
|
+
end
|
65
|
+
|
66
|
+
# Method to get batch requests
|
67
|
+
# API - https://smartcar.com/docs/api#post-batch-request
|
68
|
+
# @param attributes [Array] Array of strings or symbols of attributes to be fetched together
|
69
|
+
#
|
70
|
+
# @return [Hash] Hash wth key as requested attribute(symbol) and value as Error OR Object of the requested attribute
|
71
|
+
def batch(attributes = [])
|
72
|
+
raise InvalidParameterValue.new, "vin is a required field" if attributes.nil?
|
73
|
+
request_body = get_batch_request_body(attributes)
|
74
|
+
response, _meta = post(PATH.call(id) + "/batch", request_body)
|
75
|
+
process_batch_response(response)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Fetch the list of permissions that this application has been granted for
|
79
|
+
# this vehicle
|
80
|
+
# EX : Smartcar::Vehicle.new(token: token, id: id).permissions
|
81
|
+
# @param options [Hash] - Optional filter parameters (check documentation)
|
82
|
+
#
|
83
|
+
# @return [Array] of permissions (Strings)
|
84
|
+
def permissions(options: {})
|
85
|
+
get_attribute(Permissions)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Method Used toRevoke access for the current requesting application
|
89
|
+
# API - https://smartcar.com/docs/api#delete-disconnect
|
90
|
+
#
|
91
|
+
# @return [Boolean] true if success
|
92
|
+
def disconnect!
|
93
|
+
response = delete(PATH.call(id) + "/application")
|
94
|
+
response['status'] == SUCCESS
|
95
|
+
end
|
96
|
+
|
97
|
+
# Methods Used to lock car
|
98
|
+
# API - https://smartcar.com/docs/api#post-security
|
99
|
+
#
|
100
|
+
# @return [Boolean] true if success
|
101
|
+
def lock!
|
102
|
+
lock_or_unlock!(action: Smartcar::LOCK)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Methods Used to unlock car
|
106
|
+
# API - https://smartcar.com/docs/api#post-security
|
107
|
+
#
|
108
|
+
# @return [Boolean] true if success
|
109
|
+
def unlock!
|
110
|
+
lock_or_unlock!(action: Smartcar::UNLOCK)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Method used to start charging a car
|
114
|
+
#
|
115
|
+
#
|
116
|
+
# @return [Boolean] true if success
|
117
|
+
def start_charge!
|
118
|
+
start_or_stop_charge!(action: Smartcar::START_CHARGE)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Method used to stop charging a car
|
122
|
+
#
|
123
|
+
#
|
124
|
+
# @return [Boolean] true if success
|
125
|
+
def stop_charge!
|
126
|
+
start_or_stop_charge!(action: Smartcar::STOP_CHARGE)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns make model year and id of the vehicle
|
130
|
+
# API - https://smartcar.com/api#get-vehicle-attributes
|
131
|
+
#
|
132
|
+
# @return [VehicleAttributes] object
|
133
|
+
def vehicle_attributes
|
134
|
+
get_attribute(VehicleAttributes)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Returns the state of charge (SOC) and remaining range of an electric or
|
138
|
+
# plug-in hybrid vehicle's battery.
|
139
|
+
# API - https://smartcar.com/docs/api#get-ev-battery
|
140
|
+
#
|
141
|
+
# @return [Battery] object
|
142
|
+
def battery
|
143
|
+
get_attribute(Battery)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Returns the current charge status of the vehicle.
|
147
|
+
# API - https://smartcar.com/docs/api#get-ev-battery
|
148
|
+
#
|
149
|
+
# @return [Charge] object
|
150
|
+
def charge
|
151
|
+
get_attribute(Charge)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns the remaining life span of a vehicle's engine oil
|
155
|
+
# API - https://smartcar.com/docs/api#get-engine-oil-life
|
156
|
+
#
|
157
|
+
# @return [EngineOil] object
|
158
|
+
def engine_oil
|
159
|
+
get_attribute(EngineOil)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Returns the status of the fuel remaining in the vehicle's gas tank.
|
163
|
+
# API - https://smartcar.com/docs/api#get-fuel-tank
|
164
|
+
#
|
165
|
+
# @return [Fuel] object
|
166
|
+
def fuel
|
167
|
+
get_attribute(Fuel)
|
168
|
+
end
|
169
|
+
|
170
|
+
# Returns the last known location of the vehicle in geographic coordinates.
|
171
|
+
# API - https://smartcar.com/docs/api#get-location
|
172
|
+
#
|
173
|
+
# @return [Location] object
|
174
|
+
def location
|
175
|
+
get_attribute(Location)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Returns the vehicle's last known odometer reading.
|
179
|
+
# API - https://smartcar.com/docs/api#get-odometer
|
180
|
+
#
|
181
|
+
# @return [Odometer] object
|
182
|
+
def odometer
|
183
|
+
get_attribute(Odometer)
|
184
|
+
end
|
185
|
+
|
186
|
+
# Returns the air pressure of each of the vehicle's tires.
|
187
|
+
# API - https://smartcar.com/docs/api#get-tire-pressure
|
188
|
+
#
|
189
|
+
# @return [TirePressure] object
|
190
|
+
def tire_pressure
|
191
|
+
get_attribute(TirePressure)
|
192
|
+
end
|
193
|
+
|
194
|
+
# Returns the vehicle's manufacturer identifier (VIN).
|
195
|
+
# API - https://smartcar.com/docs/api#get-vin
|
196
|
+
#
|
197
|
+
# @return [String] Vin of the vehicle.
|
198
|
+
def vin
|
199
|
+
_object = get_attribute(Vin)
|
200
|
+
@vin ||= _object.vin
|
201
|
+
end
|
202
|
+
|
203
|
+
private
|
204
|
+
|
205
|
+
def allowed_attributes
|
206
|
+
@allowed_attributes ||= {
|
207
|
+
battery: get_path(Battery),
|
208
|
+
charge: get_path(Charge),
|
209
|
+
engine_oil: get_path(EngineOil),
|
210
|
+
fuel: get_path(Fuel),
|
211
|
+
location: get_path(Location),
|
212
|
+
odometer: get_path(Odometer),
|
213
|
+
permissions: get_path(Permissions),
|
214
|
+
tire_pressure: get_path(TirePressure),
|
215
|
+
vin: get_path(Vin),
|
216
|
+
}
|
217
|
+
end
|
218
|
+
|
219
|
+
def path_to_class
|
220
|
+
@path_to_class ||= {
|
221
|
+
get_path(Battery) => Battery,
|
222
|
+
get_path(Charge) => Charge,
|
223
|
+
get_path(EngineOil) => EngineOil,
|
224
|
+
get_path(Fuel) => Fuel,
|
225
|
+
get_path(Location) => Location,
|
226
|
+
get_path(Odometer) => Odometer,
|
227
|
+
get_path(Permissions) => Permissions,
|
228
|
+
get_path(TirePressure) => TirePressure,
|
229
|
+
get_path(Vin) => Vin,
|
230
|
+
}
|
231
|
+
end
|
232
|
+
|
233
|
+
# @private
|
234
|
+
BatchItemResponse = Struct.new(:body, :status, :headers) do
|
235
|
+
def body_with_meta
|
236
|
+
body.merge(meta: headers)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def get_batch_request_body(attributes)
|
241
|
+
attributes = validated_attributes(attributes)
|
242
|
+
requests = attributes.each_with_object([]) do |item, requests|
|
243
|
+
requests << { path: allowed_attributes[item] }
|
244
|
+
end
|
245
|
+
{ requests: requests }
|
246
|
+
end
|
247
|
+
|
248
|
+
def process_batch_response(responses)
|
249
|
+
inverted_map = allowed_attributes.invert
|
250
|
+
responses["responses"].each_with_object({}) do |response, result|
|
251
|
+
item_response = BatchItemResponse.new(response["body"], response["code"], response["headers"])
|
252
|
+
error = get_error(item_response)
|
253
|
+
path = response["path"]
|
254
|
+
result[inverted_map[path]] = error || get_object(path_to_class[path], item_response.body_with_meta)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def validated_attributes(attributes)
|
259
|
+
attributes.map!(&:to_sym)
|
260
|
+
unsupported_attributes = (attributes - allowed_attributes.keys) || []
|
261
|
+
unless unsupported_attributes.empty?
|
262
|
+
message = "Unsupported attribute(s) requested in batch - #{unsupported_attributes.join(',')}"
|
263
|
+
raise InvalidParameterValue.new, message
|
264
|
+
end
|
265
|
+
attributes
|
266
|
+
end
|
267
|
+
|
268
|
+
def get_attribute(klass)
|
269
|
+
body, meta = fetch(
|
270
|
+
path: klass::PATH.call(id)
|
271
|
+
)
|
272
|
+
get_object(klass, body.merge(meta: meta))
|
273
|
+
end
|
274
|
+
|
275
|
+
def get_object(klass, data)
|
276
|
+
klass.new(data)
|
277
|
+
end
|
278
|
+
|
279
|
+
def get_path(klass)
|
280
|
+
path = klass::PATH.call(id)
|
281
|
+
path.split("/vehicles/#{id}").last
|
282
|
+
end
|
283
|
+
|
284
|
+
def lock_or_unlock!(action:)
|
285
|
+
response, meta = post(PATH.call(id) + "/security", { action: action })
|
286
|
+
response['status'] == SUCCESS
|
287
|
+
end
|
288
|
+
|
289
|
+
def start_or_stop_charge!(action:)
|
290
|
+
response, meta = post(PATH.call(id) + "/charge", { action: action })
|
291
|
+
response['status'] == SUCCESS
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|