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.
@@ -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