smartcar 0.1.1 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 77763e723a789174f3bab09883c2c4770019d73f6984acc2a20ab5580b71683d
4
- data.tar.gz: 72ea1a37d4864c8f1e2251e15afcb8510ed07a910bdffe12abba2e6b17ff48fe
3
+ metadata.gz: 726e54a0eb8a7ebe98749b6765e8bf6302c4faccc42695d9d75fd3dd3cb5b544
4
+ data.tar.gz: d5ff12b61c36f1f151a3eb161ef8134fe55c6d0bf98cb71d0de8c4138dac4045
5
5
  SHA512:
6
- metadata.gz: c8ca779368dc98843d5e623b1faa52fda1503077b932727da4ac84fd6ce2d9b35670e15c82427ee4074ea821fc025a46158ac42d63e9441eb5af25d8a12d26ad
7
- data.tar.gz: 55c56eb58615e9ef19dfff70a8d25ec4cd61b3ac90152b63555ceb7218b15230a533302887d827b502d8b0b6f06dc3010412ae13d66e9d6757cd71aefcfaeaf7
6
+ metadata.gz: dd45d1487f7ee1247831d5af2eae9bba2dcf1de43ee4f748243b03bce7a09721de3ce3e4a3a79946bbcaa4bdcc19a92036978a052666a279b3f40447d1dffd4a
7
+ data.tar.gz: 4c44d4439be32d51be9b1bbc8bb0a27b1a8dfdfb00aff6ebcdfba945e05d4f0d62406dacc370f8560ca0d80474de7900bce001a9fafa822c76d139301c5dc59d
@@ -1,7 +1,28 @@
1
- ---
2
1
  sudo: false
3
2
  language: ruby
4
3
  cache: bundler
4
+ services:
5
+ - xvfb
6
+ addons:
7
+ firefox: latest
5
8
  rvm:
6
- - 2.5.5
7
- before_install: gem install bundler -v 2.0.2
9
+ - 2.5
10
+ - 2.6
11
+ - 2.7
12
+ before_install:
13
+ - gem install bundler -v 2.1.2
14
+ - wget https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-linux64.tar.gz
15
+ - mkdir geckodriver
16
+ - tar -xzf geckodriver-v0.26.0-linux64.tar.gz -C geckodriver
17
+ - export PATH=$PATH:$PWD/geckodriver
18
+ # install:
19
+ # - firefox -headless &
20
+ deploy:
21
+ provider: rubygems
22
+ api_key: $RUBYGEMSAPI_KEY
23
+ gem: smartcar
24
+ on:
25
+ tags: true
26
+ branch: master
27
+ rvm: 2.5
28
+ skip_cleanup: 'true'
@@ -0,0 +1,2 @@
1
+ --markup=markdown
2
+ --no-private
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
55
55
  ## Enforcement
56
56
 
57
57
  Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
- reported by contacting the project team at Ashwin.Subramanian@ooma.com. All
58
+ reported by contacting the project team at support@smartcar.com. All
59
59
  complaints will be reviewed and investigated and will result in a response that
60
60
  is deemed necessary and appropriate to the circumstances. The project team is
61
61
  obligated to maintain confidentiality with regard to the reporter of an incident.
@@ -1,41 +1,47 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- smartcar (0.1.1)
4
+ smartcar (1.0.5)
5
5
  oauth2 (~> 1.4)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- byebug (11.0.1)
10
+ byebug (11.1.3)
11
+ childprocess (3.0.0)
11
12
  diff-lcs (1.3)
12
- faraday (0.17.0)
13
+ faraday (1.0.1)
13
14
  multipart-post (>= 1.2, < 3)
14
- jwt (2.2.1)
15
- multi_json (1.14.1)
15
+ jwt (2.2.2)
16
+ multi_json (1.15.0)
16
17
  multi_xml (0.6.0)
17
18
  multipart-post (2.1.1)
18
- oauth2 (1.4.2)
19
+ oauth2 (1.4.4)
19
20
  faraday (>= 0.8, < 2.0)
20
21
  jwt (>= 1.0, < 3.0)
21
22
  multi_json (~> 1.3)
22
23
  multi_xml (~> 0.5)
23
24
  rack (>= 1.2, < 3)
24
- rack (2.0.8)
25
- rake (13.0.1)
26
- rspec (3.8.0)
27
- rspec-core (~> 3.8.0)
28
- rspec-expectations (~> 3.8.0)
29
- rspec-mocks (~> 3.8.0)
30
- rspec-core (3.8.2)
31
- rspec-support (~> 3.8.0)
32
- rspec-expectations (3.8.4)
25
+ rack (2.2.3)
26
+ rake (12.3.3)
27
+ redcarpet (3.5.0)
28
+ rspec (3.9.0)
29
+ rspec-core (~> 3.9.0)
30
+ rspec-expectations (~> 3.9.0)
31
+ rspec-mocks (~> 3.9.0)
32
+ rspec-core (3.9.2)
33
+ rspec-support (~> 3.9.3)
34
+ rspec-expectations (3.9.2)
33
35
  diff-lcs (>= 1.2.0, < 2.0)
34
- rspec-support (~> 3.8.0)
35
- rspec-mocks (3.8.1)
36
+ rspec-support (~> 3.9.0)
37
+ rspec-mocks (3.9.1)
36
38
  diff-lcs (>= 1.2.0, < 2.0)
37
- rspec-support (~> 3.8.0)
38
- rspec-support (3.8.2)
39
+ rspec-support (~> 3.9.0)
40
+ rspec-support (3.9.3)
41
+ rubyzip (2.3.0)
42
+ selenium-webdriver (3.142.7)
43
+ childprocess (>= 0.5, < 4.0)
44
+ rubyzip (>= 1.2.2)
39
45
 
40
46
  PLATFORMS
41
47
  ruby
@@ -43,8 +49,10 @@ PLATFORMS
43
49
  DEPENDENCIES
44
50
  bundler (~> 2.0)
45
51
  byebug (~> 11.0)
46
- rake (>= 12.3.3)
52
+ rake (~> 12.3, >= 12.3.3)
53
+ redcarpet (~> 3.5.0)
47
54
  rspec (~> 3.0)
55
+ selenium-webdriver (~> 3.142)
48
56
  smartcar!
49
57
 
50
58
  BUNDLED WITH
data/README.md CHANGED
@@ -1,7 +1,57 @@
1
1
 
2
+ # Smartcar Ruby SDK [![Gem Version][gem-url]][gem-image]
2
3
 
3
4
  Ruby gem library to quickly get started with the Smartcar API.
4
5
 
6
+ ## Overview
7
+
8
+ The [Smartcar API](https://smartcar.com/docs) lets you read vehicle data
9
+ (location, odometer) and send commands to vehicles (lock, unlock) using HTTP requests.
10
+
11
+ To make requests to a vehicle from a web or mobile application, the end user
12
+ must connect their vehicle using
13
+ [Smartcar Connect](https://smartcar.com/docs/api#smartcar-connect).
14
+ This flow follows the OAuth spec and will return a `code` which can be used to
15
+ obtain an access token from Smartcar.
16
+
17
+ The Smartcar Ruby Gem provides methods to:
18
+
19
+ 1. Generate the link to redirect to Connect.
20
+ 2. Make a request to Smartcar with the `code` obtained from Connect to obtain an
21
+ access and refresh token
22
+ 3. Make requests to the Smartcar API to read vehicle data and send commands to
23
+ vehicles using the access token obtained in step 2.
24
+
25
+ Before integrating with Smartcar's SDK, you'll need to register an application
26
+ in the [Smartcar Developer portal](https://developer.smartcar.com). If you do
27
+ not have access to the dashboard, please
28
+ [request access](https://smartcar.com/subscribe).
29
+
30
+ ### Flow
31
+
32
+ - Create a new `AuthClient` object with your `clientId`, `clientSecret`,
33
+ `redirectUri`, and required `scope`.
34
+ - Redirect the user to Smartcar Connect using `getAuthUrl` or one
35
+ of our frontend SDKs.
36
+ - The user will login, and then accept or deny your `scope`'s permissions.
37
+ - Handle the get request to `redirectUri`.
38
+ - If the user accepted your permissions, `req.query.code` will contain an
39
+ authorization code.
40
+ - Use `exchangeCode` with this code to obtain an access object
41
+ containing an access token (lasting 2 hours) and a refresh token
42
+ (lasting 60 days).
43
+ - Save this access object.
44
+ - If the user denied your permissions, `req.query.error` will be set
45
+ to `"access_denied"`.
46
+ - If you passed a state parameter to `getAuthUrl`, `req.query.state` will
47
+ contain the state value.
48
+ - Get the user's vehicles with `getVehicleIds`.
49
+ - Create a new `Vehicle` object using a `vehicleId` from the previous response,
50
+ and the `access_token`.
51
+ - Make requests to the Smartcar API.
52
+ - Use `exchangeRefreshToken` on your saved `refreshToken` to retrieve a new token
53
+ when your `accessToken` expires.
54
+
5
55
  ## Installation
6
56
 
7
57
  Add this line to your application's Gemfile:
@@ -20,29 +70,38 @@ Or install it yourself as:
20
70
 
21
71
  ## Usage
22
72
 
23
- Setup the environment variables for CLIENT_ID and CLIENT_SECRET.
73
+ Setup the environment variables for CLIENT_ID and CLIENT_SECRET.
24
74
  ```bash
25
75
  # Get your API keys from https://dashboard.smartcar.com/signup
26
76
  export CLIENT_ID=<client id>
27
77
  export CLIENT_SECRET=<client secret>
28
- ```
78
+ ```
29
79
 
30
80
  Example Usage for calling the reports API with oAuth token
31
81
  ```ruby
32
- 2.5.5 :003 > require 'smartcar'
82
+ 2.5.7 :001 > require 'smartcar'
33
83
  => true
34
- 2.5.5 :009 > ids = Smartcar::Vehicle.all_vehicle_ids(token: token)
35
- => ["35e8a7c4-9e5c-4eb6-b552-7509e371669a", "c3332c35-fdeb-4780-a84b-706b7364979a", "d10ad5cf-5469-467e-972e-90427981873f", "fab5a744-6488-40d8-a6dd-41f0a804d44f"]
36
- 2.5.5 :010 > vehicle = Smartcar::Vehicle.new(token: token, id: ids.first)
37
- => #<Smartcar::Vehicle:0x00007fbad71aa2b8 @token="56801a5e-6a0b-4d05-a43e-52a4d5e6648f", @id="35e8a7c4-9e5c-4eb6-b552-7509e371669a", @unit_system="imperial">
38
- 2.5.5 :011 > vehicle.permissions
39
- => ["control_security", "read_battery", "read_charge", "read_location", "read_odometer", "read_vehicle_info", "read_vin"]
40
- 2.5.5 :012 > vehicle.odometer
41
- => #<Smartcar::Odometer:0x00007fbad718a3f0 @distance=74988.44443760936>
42
- 2.5.5 :013 > vehicle.battery
43
- => #<Smartcar::Battery:0x00007fbad50f4c80 @range=134.35, @percentRemaining=0.02>
44
- 2.5.5 :014 > vehicle.charge
45
- => #<Smartcar::Charge:0x00007fbad787e620 @state="FULLY_CHARGED", @isPluggedIn=true>
84
+ 2.5.7 :003 > ids = Smartcar::Vehicle.all_vehicle_ids(token: token)
85
+ => ["4bb777b2-bde7-4305-8952-25956f8c0868"]
86
+ 2.5.7 :004 > vehicle = Smartcar::Vehicle.new(token: token, id: ids.first)
87
+ => #<Smartcar::Vehicle:0x00005564211a7c48 @token="5ae77cb0-7c1a-486a-ac20-00c76d2fd1aa", @id="4bb777b2-bde7-4305-8952-25956f8c0868", @unit_system="imperial">
88
+ 2.5.7 :006 > vehicle.odometer
89
+ => #<Smartcar::Odometer:0x00005564211330f0 @distance=17966.94802354251, @meta={"date"=>"Fri, 12 Jun 2020 06:04:32 GMT", "content-type"=>"application/json; charset=utf-8", "content-length"=>"30", "connection"=>"keep-alive", "access-control-allow-origin"=>"*", "sc-data-age"=>"2020-06-12T06:04:28.843Z", "sc-unit-system"=>"imperial", "sc-request-id"=>"3c447e9e-4cf7-43cb-b688-fba8db3d3582"}>
90
+ 2.5.7 :007 > vehicle.battery
91
+ => #<Smartcar::Battery:0x00005564210fcb18 @range=105.63, @percentRemaining=0.98, @meta={"date"=>"Fri, 12 Jun 2020 06:04:44 GMT", "content-type"=>"application/json; charset=utf-8", "content-length"=>"40", "connection"=>"keep-alive", "access-control-allow-origin"=>"*", "sc-data-age"=>"2020-06-12T06:04:28.843Z", "sc-unit-system"=>"imperial", "sc-request-id"=>"455ed4b0-b768-4961-86d7-436ad71cf0fa"}>
92
+ 2.5.7 :009 > vehicle.lock!
93
+ => true
94
+ 2.5.7 :010 > vehicle.batch(["charge","battery"])
95
+ => {:charge=>#<Smartcar::Charge:0x000055853d1fd7c8 @state="NOT_CHARGING", @isPluggedIn=false, @meta={"sc-data-age"=>"2020-06-12T06:18:50.581Z"}>, :battery=>#<Smartcar::Battery:0x000055853d1fd638 @range=105.63, @percentRemaining=0.98, @meta={"sc-data-age"=>"2020-06-12T06:18:50.581Z", "sc-unit-system"=>"imperial"}>}
96
+ 2.5.7 :011 > vehicle.start_charge!
97
+ Traceback (most recent call last):
98
+ 5: from /usr/share/rvm/rubies/ruby-2.5.7/bin/irb:11:in `<main>'
99
+ 4: from (irb):5
100
+ 3: from /home/st-2vgpnn2/.rvm/gems/ruby-2.5.7/gems/smartcar-1.0.0/lib/smartcar/vehicle.rb:118:in `start_charge!'
101
+ 2: from /home/st-2vgpnn2/.rvm/gems/ruby-2.5.7/gems/smartcar-1.0.0/lib/smartcar/vehicle.rb:290:in `start_or_stop_charge!'
102
+ 1: from /home/st-2vgpnn2/.rvm/gems/ruby-2.5.7/gems/smartcar-1.0.0/lib/smartcar/base.rb:39:in `block (2 levels) in <class:Base>'
103
+ Smartcar::ExternalServiceError (API error - {"error":"vehicle_state_error","message":"Charging plug is not connected to the vehicle.","code":"VS_004"})
104
+
46
105
  ```
47
106
 
48
107
  Example Usage for oAuth -
@@ -64,8 +123,18 @@ Example Usage for oAuth -
64
123
 
65
124
  ## Development
66
125
 
67
- 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).
126
+ 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 create a git tag for the version, push git commits and tags. When merging to master if it finds the tag it will deploy to rubygems automatically
68
127
 
128
+ To run tests, make sure you have the env variables setup for client id and secret.
129
+ ```shell
130
+ export INTEGRATION_CLIENT_ID=<client id>
131
+ export INTEGRATION_CLIENT_SECRET=<client secret>
132
+ ```
133
+
134
+ Tests can be run using either default rake command OR specific rspec command.
135
+ ```ruby
136
+ bundle exec rake spec
137
+ ```
69
138
  ## Contributing
70
139
 
71
140
  To contribute, please:
@@ -73,3 +142,6 @@ To contribute, please:
73
142
  1. Open an issue for the feature (or bug) you would like to resolve.
74
143
  2. Resolve the issue and add tests in your feature branch.
75
144
  3. Open a PR from your feature branch into `develop` that tags the issue.
145
+
146
+ [gem-image]: https://badge.fury.io/rb/smartcar
147
+ [gem-url]: https://badge.fury.io/rb/smartcar.svg
@@ -11,26 +11,43 @@ require "smartcar/location"
11
11
  require "smartcar/odometer"
12
12
  require "smartcar/tire_pressure"
13
13
  require "smartcar/vin"
14
+ require "smartcar/vehicle_attributes"
14
15
  require "smartcar/vehicle"
15
16
  require "smartcar/user"
16
17
 
17
18
 
18
- module Smartcar
19
+ # Main Smartcar umbrella module
20
+ module Smartcar
21
+ # Error raised when a config is not found
19
22
  class ConfigNotFound < StandardError; end
23
+ # Error raised when Smartcar returns non 400, 404, 401, 200 or 204 response
20
24
  class ExternalServiceError < StandardError; end
25
+ # Error raised when Smartcar returns 404
21
26
  class ServiceUnavailableError < ExternalServiceError; end
27
+ # Error raised when Smartcar returns Authentication Error with status 401
22
28
  class AuthenticationError < ExternalServiceError; end
23
- class ParserError < ExternalServiceError; end
29
+ # Error raised when Smartcar returns 400 response
24
30
  class BadRequestError < ExternalServiceError; end
31
+ # Smartcar API version being used
25
32
  API_VERSION = "v1.0".freeze
33
+ # Host to connect to smartcar
26
34
  SITE = "https://api.smartcar.com/".freeze
27
35
 
28
36
  # Path for smartcar oauth
29
37
  OAUTH_PATH = "https://connect.smartcar.com/oauth/authorize".freeze
30
38
  %w(success code test live force auto metric imperial).each do |constant|
39
+ # Constant to represent the value
31
40
  const_set(constant.upcase, constant.freeze)
32
41
  end
42
+
43
+ # Lock value sent in request body
33
44
  LOCK = "LOCK".freeze
45
+ # Unlock value sent in request body
34
46
  UNLOCK = "UNLOCK".freeze
47
+ # Start charge value sent in request body
48
+ START_CHARGE = "START".freeze
49
+ # Stop charge value sent in request body
50
+ STOP_CHARGE = "STOP".freeze
51
+ # Constant for units
35
52
  UNITS = [IMPERIAL,METRIC]
36
53
  end
@@ -3,21 +3,26 @@ require 'base64'
3
3
  module Smartcar
4
4
  # The Base class for all of the other class.
5
5
  # Let other classes inherit from here and put common methods here.
6
- #
7
- # @author [ashwin]
8
- #
9
6
  class Base
7
+ include Utils
8
+
9
+ # Error raised when an invalid parameter is passed.
10
10
  class InvalidParameterValue < StandardError; end
11
+ # Constant for Bearer auth type
11
12
  BEARER = 'BEARER'.freeze
13
+ # Constant for Basic auth type
12
14
  BASIC = 'BASIC'.freeze
15
+ # Number of seconds to wait for response
16
+ REQUEST_TIMEOUT = 310
17
+
18
+ attr_accessor :token, :error, :meta
13
19
 
14
- attr_accessor :token
15
- # meta programming and define all Restful methods.
16
- # @param path [String] the path to hit for the request.
17
- # @param token [String] the access token to be used.
18
- #
19
- # @return [Hash] The response Json parsed as a hash.
20
20
  %i{get post patch put delete}.each do |verb|
21
+ # meta programming and define all Restful methods.
22
+ # @param path [String] the path to hit for the request.
23
+ # @param data [Hash] request body if needed.
24
+ #
25
+ # @return [Hash] The response Json parsed as a hash.
21
26
  define_method verb do |path, data=nil|
22
27
  response = service.send(verb) do |request|
23
28
  request.headers['Authorization'] = "BEARER #{token}"
@@ -29,21 +34,19 @@ module Smartcar
29
34
  request.url complete_path, data
30
35
  else
31
36
  request.url complete_path
32
- request.body = data if data
37
+ request.body = data.to_json if data
33
38
  end
34
39
  end
35
- status = response.status
36
- raise ServiceUnavailableError.new, "Service Unavailable - #{response.body}" if status == 404
37
- raise BadRequestError.new, "Bad Request - #{response.body}" if status == 400
38
- raise AuthenticationError.new, "Authentication error" if status == 401
39
- raise ExternalServiceError.new, "API error - #{response.body}" unless [200,204].include?(status)
40
- JSON.parse(response.body)
40
+ error = get_error(response)
41
+ raise error if error
42
+ [JSON.parse(response.body), response.headers]
41
43
  end
42
44
  end
43
45
 
44
46
  # This requires a proc 'PATH' to be defined in the class
45
- # @param token [String] Access token
46
- # @param token [String] Vechicle ID
47
+ # @param path [String] resource path
48
+ # @param options [Hash] query params
49
+ # @param auth [String] type of auth
47
50
  #
48
51
  # @return [Object]
49
52
  def fetch(path: , options: {}, auth: 'BEARER')
@@ -58,15 +61,14 @@ module Smartcar
58
61
  #
59
62
  # @return [String] Base64 encoding of CLIENT:SECRET
60
63
  def get_basic_auth
61
- Base64.strict_encode64("#{ENV['CLIENT_ID']}:#{ENV['CLIENT_SECRET']}")
64
+ Base64.strict_encode64("#{get_config('CLIENT_ID')}:#{get_config('CLIENT_SECRET')}")
62
65
  end
63
66
 
64
67
  # gets a smartcar API service/client
65
- # @param token [String] Access token.
66
68
  #
67
69
  # @return [OAuth2::AccessToken] An initialized AccessToken instance that acts as service client
68
70
  def service
69
- @service ||= Faraday.new(url: SITE)
71
+ @service ||= Faraday.new(url: SITE, request: { timeout: REQUEST_TIMEOUT })
70
72
  end
71
73
  end
72
- end
74
+ end
@@ -1,12 +1,11 @@
1
1
  module Smartcar
2
- # class to represent battery info
3
- #
4
- # @author [ashwin]
5
- #
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.
6
5
  class Battery < Base
7
- include Utils
6
+ # Path Proc for hitting battery end point
8
7
  PATH = Proc.new{|id| "/vehicles/#{id}/battery"}
9
- attr_accessor :percentRemaining, :range
8
+ attr_reader :percentRemaining, :range
10
9
 
11
10
  # just to have Ruby-esque method names
12
11
  alias_method :percentage_remaining, :percentRemaining
@@ -1,12 +1,11 @@
1
1
  module Smartcar
2
2
  # class to represent Charge info
3
- #
4
- # @author [ashwin]
5
- #
3
+ #@attr [Boolean] isPluggedIn Specifies if the vehicle is plugged in.
4
+ #@attr [String] state Charging state of the vehicle.
6
5
  class Charge < Base
7
- include Utils
6
+ # Path Proc for hitting charge end point
8
7
  PATH = Proc.new{|id| "/vehicles/#{id}/charge"}
9
- attr_accessor :isPluggedIn, :state
8
+ attr_reader :isPluggedIn, :state
10
9
 
11
10
  # just to have Ruby-esque method names
12
11
  alias_method :is_plugged_in?, :isPluggedIn
@@ -1,12 +1,10 @@
1
1
  module Smartcar
2
- # class to represent Engine oil life
3
- #
4
- # @author [ashwin]
5
- #
2
+ # class to represent Engine oil info
3
+ #@attr [Number] lifeRemaining Remaining life of the engine oil
6
4
  class EngineOil < Base
7
- include Utils
5
+ # Path Proc for hitting engine oil end point
8
6
  PATH = Proc.new{|id| "/vehicles/#{id}/engine/oil"}
9
- attr_accessor :lifeRemaining
7
+ attr_reader :lifeRemaining
10
8
 
11
9
  # just to have Ruby-esque method names
12
10
  alias_method :life_remaining, :lifeRemaining