smartcar 0.0.0 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 Odometer
3
+ #@attr [Number] distanceLast recorded odometer reading.
4
+ class Odometer < Base
5
+ # Path Proc for hitting odometer end point
6
+ PATH = Proc.new{|id| "/vehicles/#{id}/odometer"}
7
+ attr_reader :distance
8
+ end
9
+ 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