tesla_api 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +5 -5
  2. data/.gitbook.yml +4 -0
  3. data/.travis.yml +3 -4
  4. data/LICENSE +1 -1
  5. data/README.md +7 -3
  6. data/Rakefile +27 -1
  7. data/apiary.apib +21 -14
  8. data/docs/README.md +26 -0
  9. data/docs/SUMMARY.md +39 -0
  10. data/docs/api-basics/authentication.md +40 -0
  11. data/docs/api-basics/vehicles.md +77 -0
  12. data/docs/vehicle/autopark.md +4 -0
  13. data/docs/vehicle/commands/README.md +22 -0
  14. data/docs/vehicle/commands/alerts.md +5 -0
  15. data/docs/vehicle/commands/calendar.md +3 -0
  16. data/docs/vehicle/commands/charging.md +3 -0
  17. data/docs/vehicle/commands/climate.md +3 -0
  18. data/docs/vehicle/commands/doors.md +3 -0
  19. data/docs/vehicle/commands/media.md +3 -0
  20. data/docs/vehicle/commands/navigation.md +3 -0
  21. data/docs/vehicle/commands/remotestart.md +3 -0
  22. data/docs/vehicle/commands/softwareupdate.md +3 -0
  23. data/docs/vehicle/commands/speedlimit.md +3 -0
  24. data/docs/vehicle/commands/sunroof.md +3 -0
  25. data/docs/vehicle/commands/trunk.md +3 -0
  26. data/docs/vehicle/commands/valet.md +3 -0
  27. data/docs/vehicle/commands/wake.md +3 -0
  28. data/docs/vehicle/optioncodes.md +253 -0
  29. data/docs/vehicle/state/README.md +34 -0
  30. data/docs/vehicle/state/chargestate.md +56 -0
  31. data/docs/vehicle/state/climatestate.md +43 -0
  32. data/docs/vehicle/state/data.md +181 -0
  33. data/docs/vehicle/state/drivestate.md +27 -0
  34. data/docs/vehicle/state/guisettings.md +21 -0
  35. data/docs/vehicle/state/mobileenabled.md +14 -0
  36. data/docs/vehicle/state/vehiclestate.md +47 -0
  37. data/docs/vehicle/streaming.md +4 -0
  38. data/lib/tesla_api.rb +7 -6
  39. data/lib/tesla_api/autopark.rb +62 -0
  40. data/lib/tesla_api/client.rb +32 -11
  41. data/lib/tesla_api/stream.rb +11 -7
  42. data/lib/tesla_api/vehicle.rb +50 -30
  43. data/lib/tesla_api/version.rb +1 -1
  44. data/spec/cassettes/client-login.yml +1 -1
  45. data/spec/cassettes/vehicle-activate_speed_limit.yml +95 -0
  46. data/spec/cassettes/vehicle-clear_speed_limit_pin.yml +95 -0
  47. data/spec/cassettes/vehicle-deactivate_speed_limit.yml +95 -0
  48. data/spec/cassettes/vehicle-open_frunk.yml +40 -73
  49. data/spec/cassettes/vehicle-open_trunk.yml +40 -73
  50. data/spec/cassettes/vehicle-set_speed_limit.yml +95 -0
  51. data/spec/lib/tesla_api/client_spec.rb +42 -15
  52. data/spec/lib/tesla_api/vehicle_spec.rb +160 -136
  53. data/spec/spec_helper.rb +6 -18
  54. data/tesla_api.gemspec +20 -24
  55. metadata +67 -42
  56. data/circle.yml +0 -10
@@ -0,0 +1,27 @@
1
+ # Drive State
2
+
3
+ ## GET `/api/1/vehicles/{id}/data_request/drive_state`
4
+
5
+ Returns the driving and position state of the vehicle.
6
+
7
+ ### Response
8
+
9
+ ```json
10
+ {
11
+ "response": {
12
+ "shift_state": null,
13
+ "speed": null,
14
+ "power": 0,
15
+ "latitude": 33.111111,
16
+ "longitude": -88.111111,
17
+ "heading": 5,
18
+ "gps_as_of": 1538365363,
19
+ "native_location_supported": 1,
20
+ "native_latitude": 33.111111,
21
+ "native_longitude": -88.111111,
22
+ "native_type": "wgs",
23
+ "timestamp": 1538365436098
24
+ }
25
+ }
26
+ ```
27
+
@@ -0,0 +1,21 @@
1
+ # GUI Settings
2
+
3
+ ## GET `/api/1/vehicles/{id}/data_request/gui_settings`
4
+
5
+ Returns various information about the GUI settings of the car, such as unit format and range display.
6
+
7
+ ### Response
8
+
9
+ ```json
10
+ {
11
+ "response": {
12
+ "gui_distance_units": "mi/hr",
13
+ "gui_temperature_units": "F",
14
+ "gui_charge_rate_units": "mi/hr",
15
+ "gui_24_hour_time": false,
16
+ "gui_range_display": "Rated",
17
+ "timestamp": 1538365490671
18
+ }
19
+ }
20
+ ```
21
+
@@ -0,0 +1,14 @@
1
+ # Mobile Enabled
2
+
3
+ ## GET `/api/1/vehicles/{id}/mobile_enabled`
4
+
5
+ Lets you know if the Mobile Access setting is enabled in the car.
6
+
7
+ ### Response
8
+
9
+ ```json
10
+ {
11
+ "response": true
12
+ }
13
+ ```
14
+
@@ -0,0 +1,47 @@
1
+ # Vehicle State
2
+
3
+ ## GET `/api/1/vehicles/{id}/data_request/vehicle_state`
4
+
5
+ Returns the vehicle's physical state, such as which doors are open.
6
+
7
+ ### Response
8
+
9
+ ```json
10
+ {
11
+ "response": {
12
+ "api_version": 3,
13
+ "autopark_state_v2": "standby",
14
+ "autopark_style": "standard",
15
+ "calendar_supported": true,
16
+ "car_version": "2018.34.1 3dd3072",
17
+ "center_display_state": 0,
18
+ "df": 0,
19
+ "dr": 0,
20
+ "ft": 0,
21
+ "homelink_nearby": true,
22
+ "last_autopark_error": "no_error",
23
+ "locked": true,
24
+ "notifications_supported": true,
25
+ "odometer": 33561.422505,
26
+ "parsed_calendar_supported": true,
27
+ "pf": 0,
28
+ "pr": 0,
29
+ "remote_start": false,
30
+ "remote_start_supported": true,
31
+ "rt": 0,
32
+ "speed_limit_mode": {
33
+ "active": false,
34
+ "current_limit_mph": 75.0,
35
+ "max_limit_mph": 90,
36
+ "min_limit_mph": 50,
37
+ "pin_code_set": false
38
+ },
39
+ "sun_roof_percent_open": 0,
40
+ "sun_roof_state": "unknown",
41
+ "timestamp": 1538365559247,
42
+ "valet_mode": false,
43
+ "vehicle_name": "Nikola 2.0"
44
+ }
45
+ }
46
+ ```
47
+
@@ -0,0 +1,4 @@
1
+ # Streaming
2
+
3
+
4
+
data/lib/tesla_api.rb CHANGED
@@ -1,7 +1,8 @@
1
- require "httparty"
2
- require "em-http-request"
1
+ require 'httparty'
2
+ require 'em-http-request'
3
3
 
4
- require "tesla_api/version"
5
- require "tesla_api/client"
6
- require "tesla_api/stream"
7
- require "tesla_api/vehicle"
4
+ require 'tesla_api/version'
5
+ require 'tesla_api/client'
6
+ require 'tesla_api/stream'
7
+ require 'tesla_api/autopark'
8
+ require 'tesla_api/vehicle'
@@ -0,0 +1,62 @@
1
+ require 'faye/websocket'
2
+
3
+ module TeslaApi
4
+ module Autopark
5
+ def start_autopark(&handler)
6
+ EventMachine.run do
7
+ socket.on(:message) do |event|
8
+ message = if event.data.is_a?(Array)
9
+ JSON.parse(event.data.map(&:chr).join)
10
+ else
11
+ JSON.parse(event.data)
12
+ end
13
+
14
+ default_handler(message)
15
+ handler.call(message.delete('msg_type'), message)
16
+ end
17
+
18
+ socket.on(:close) do |_|
19
+ @socket = nil
20
+ @heartbeat && @heartbeat.cancel
21
+ EventMachine.stop
22
+ end
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def default_handler(message)
29
+ case message['msg_type']
30
+ when 'control:hello'
31
+ interval = message['autopark']['heartbeat_frequency'] / 1000.0
32
+ @heartbeat = EventMachine::Timer.new(interval) do
33
+ beat = {
34
+ msg_type: 'autopark:heartbeat_app',
35
+ timestamp: Time.now.to_i
36
+ }
37
+ socket.send(beat.to_json)
38
+ end
39
+ end
40
+ end
41
+
42
+ def socket
43
+ @socket ||= Faye::WebSocket::Client.new(
44
+ socket_endpoint,
45
+ nil,
46
+ {
47
+ headers: {
48
+ 'Authorization' => "Basic #{socket_auth}"
49
+ }
50
+ }
51
+ )
52
+ end
53
+
54
+ def socket_auth
55
+ Base64.strict_encode64("#{email}:#{self['tokens'].first}")
56
+ end
57
+
58
+ def socket_endpoint
59
+ "wss://streaming.vn.teslamotors.com/connect/#{self['vehicle_id']}"
60
+ end
61
+ end
62
+ end
@@ -1,12 +1,13 @@
1
1
  module TeslaApi
2
2
  class Client
3
3
  include HTTParty
4
- base_uri "https://owner-api.teslamotors.com/api/1"
4
+ base_uri 'https://owner-api.teslamotors.com/api/1'
5
+ headers 'User-Agent' => "github.com/timdorr/tesla-api v:#{VERSION}"
5
6
  format :json
6
7
 
7
8
  attr_reader :email, :token, :client_id, :client_secret
8
9
 
9
- def initialize(email, client_id = ENV["TESLA_CLIENT_ID"], client_secret = ENV["TESLA_CLIENT_SECRET"])
10
+ def initialize(email, client_id = ENV['TESLA_CLIENT_ID'], client_secret = ENV['TESLA_CLIENT_SECRET'])
10
11
  @email = email
11
12
  @client_id = client_id
12
13
  @client_secret = client_secret
@@ -14,26 +15,46 @@ module TeslaApi
14
15
 
15
16
  def token=(token)
16
17
  @token = token
17
- self.class.headers "Authorization" => "Bearer #{token}"
18
+ self.class.headers 'Authorization' => "Bearer #{token}"
19
+ end
20
+
21
+ def expires_in=(seconds)
22
+ @expires_in = seconds.to_f
23
+ end
24
+
25
+ def created_at=(timestamp)
26
+ @created_at = Time.at(timestamp.to_f).to_datetime
27
+ end
28
+
29
+ def expired_at
30
+ return nil unless defined?(@created_at)
31
+ (@created_at.to_time + @expires_in.to_f).to_datetime
32
+ end
33
+
34
+ def expired?
35
+ return true unless defined?(@created_at)
36
+ expired_at <= DateTime.now
18
37
  end
19
38
 
20
39
  def login!(password)
21
40
  response = self.class.post(
22
- "https://owner-api.teslamotors.com/oauth/token",
41
+ 'https://owner-api.teslamotors.com/oauth/token',
23
42
  body: {
24
- "grant_type" => "password",
25
- "client_id" => client_id,
26
- "client_secret" => client_secret,
27
- "email" => email,
28
- "password" => password
43
+ grant_type: 'password',
44
+ client_id: client_id,
45
+ client_secret: client_secret,
46
+ email: email,
47
+ password: password
29
48
  }
30
49
  )
31
50
 
32
- self.token = response["access_token"]
51
+ self.expires_in = response['expires_in']
52
+ self.created_at = response['created_at']
53
+ self.token = response['access_token']
33
54
  end
34
55
 
35
56
  def vehicles
36
- self.class.get("/vehicles")["response"].map { |v| Vehicle.new(self.class, email, v["id"], v) }
57
+ self.class.get('/vehicles')['response'].map { |v| Vehicle.new(self.class, v['id'], v) }
37
58
  end
38
59
  end
39
60
  end
@@ -6,7 +6,7 @@ module TeslaApi
6
6
  attributes = chunk.split(",")
7
7
 
8
8
  reciever.call({
9
- time: DateTime.strptime((attributes[0].to_i/1000).to_s, "%s"),
9
+ time: DateTime.strptime((attributes[0].to_i/1000).to_s, '%s'),
10
10
  speed: attributes[1].to_f,
11
11
  odometer: attributes[2].to_f,
12
12
  soc: attributes[3].to_f,
@@ -14,7 +14,11 @@ module TeslaApi
14
14
  est_heading: attributes[5].to_f,
15
15
  est_lat: attributes[6].to_f,
16
16
  est_lng: attributes[7].to_f,
17
- power: attributes[8].to_f
17
+ power: attributes[8].to_f,
18
+ shift_state: attributes[9].to_s,
19
+ range: attributes[10].to_f,
20
+ est_range: attributes[11].to_f,
21
+ heading: attributes[12].to_f
18
22
  })
19
23
  end
20
24
 
@@ -27,23 +31,23 @@ module TeslaApi
27
31
 
28
32
  def request
29
33
  @request ||= EventMachine::HttpRequest.new(
30
- "#{stream_endpoint}/stream/#{self["vehicle_id"]}/?values=#{stream_params}")
34
+ "#{stream_endpoint}/stream/#{self['vehicle_id']}/?values=#{stream_params}")
31
35
  end
32
36
 
33
37
  def http
34
- @http ||= request.get(
38
+ request.get(
35
39
  head: {
36
- "authorization" => [email, self["tokens"].first]
40
+ 'authorization' => [email, self['tokens'].first]
37
41
  },
38
42
  inactivity_timeout: 15)
39
43
  end
40
44
 
41
45
  def stream_endpoint
42
- "https://streaming.vn.teslamotors.com"
46
+ 'https://streaming.vn.teslamotors.com'
43
47
  end
44
48
 
45
49
  def stream_params
46
- "speed,odometer,soc,elevation,est_heading,est_lat,est_lng,power"
50
+ 'speed,odometer,soc,elevation,est_heading,est_lat,est_lng,power,shift_state,range,est_range,heading'
47
51
  end
48
52
  end
49
53
  end
@@ -1,11 +1,11 @@
1
1
  module TeslaApi
2
2
  class Vehicle
3
3
  include Stream
4
- attr_reader :api, :email, :id, :vehicle
4
+ include Autopark
5
+ attr_reader :api, :id, :vehicle
5
6
 
6
- def initialize(api, email, id, vehicle)
7
+ def initialize(api, id, vehicle)
7
8
  @api = api
8
- @email = email
9
9
  @id = id
10
10
  @vehicle = vehicle
11
11
  end
@@ -26,114 +26,134 @@ module TeslaApi
26
26
 
27
27
  # State
28
28
 
29
+ def data
30
+ api.get("/vehicles/#{id}/data")['response']
31
+ end
32
+
29
33
  def mobile_enabled
30
- api.get("/vehicles/#{id}/mobile_enabled")["response"]
34
+ api.get("/vehicles/#{id}/mobile_enabled")['response']
31
35
  end
32
36
 
33
37
  def gui_settings
34
- data_request("gui_settings")["response"]
38
+ data_request('gui_settings')['response']
35
39
  end
36
40
 
37
41
  def charge_state
38
- data_request("charge_state")["response"]
42
+ data_request('charge_state')['response']
39
43
  end
40
44
 
41
45
  def climate_state
42
- data_request("climate_state")["response"]
46
+ data_request('climate_state')['response']
43
47
  end
44
48
 
45
49
  def drive_state
46
- data_request("drive_state")["response"]
50
+ data_request('drive_state')['response']
47
51
  end
48
52
 
49
53
  def vehicle_state
50
- data_request("vehicle_state")["response"]
54
+ data_request('vehicle_state')['response']
51
55
  end
52
56
 
53
57
  # Commands
54
58
 
55
59
  def wake_up
56
- @vehicle = api.post("/vehicles/#{id}/wake_up")["response"]
60
+ @vehicle = api.post("/vehicles/#{id}/wake_up")['response']
57
61
  end
58
62
 
59
63
  def set_valet_mode(on, password=nil)
60
- command("set_valet_mode", body: {on: on, password: password})["response"]
64
+ command('set_valet_mode', body: {on: on, password: password})['response']
61
65
  end
62
66
 
63
67
  def reset_valet_pin
64
- command("reset_valet_pin")["response"]
68
+ command('reset_valet_pin')['response']
65
69
  end
66
70
 
67
71
  def charge_port_door_open
68
- command("charge_port_door_open")["response"]
72
+ command('charge_port_door_open')['response']
69
73
  end
70
74
 
71
75
  def charge_standard
72
- command("charge_standard")["response"]
76
+ command('charge_standard')['response']
73
77
  end
74
78
 
75
79
  def charge_max_range
76
- command("charge_max_range")["response"]
80
+ command('charge_max_range')['response']
77
81
  end
78
82
 
79
83
  def set_charge_limit(percent)
80
- command("set_charge_limit", body: {percent: percent})["response"]
84
+ command('set_charge_limit', body: {percent: percent})['response']
81
85
  end
82
86
 
83
87
  def charge_start
84
- command("charge_start")["response"]
88
+ command('charge_start')['response']
85
89
  end
86
90
 
87
91
  def charge_stop
88
- command("charge_stop")["response"]
92
+ command('charge_stop')['response']
89
93
  end
90
94
 
91
95
  def flash_lights
92
- command("flash_lights")["response"]
96
+ command('flash_lights')['response']
93
97
  end
94
98
 
95
99
  def honk_horn
96
- command("honk_horn")["response"]
100
+ command('honk_horn')['response']
97
101
  end
98
102
 
99
103
  def door_unlock
100
- command("door_unlock")["response"]
104
+ command('door_unlock')['response']
101
105
  end
102
106
 
103
107
  def door_lock
104
- command("door_lock")["response"]
108
+ command('door_lock')['response']
105
109
  end
106
110
 
107
111
  def set_temps(driver_temp, passenger_temp)
108
- command("set_temps", body: {driver_temp: driver_temp, passenger_temp: passenger_temp})["response"]
112
+ command('set_temps', body: {driver_temp: driver_temp, passenger_temp: passenger_temp})['response']
109
113
  end
110
114
 
111
115
  def auto_conditioning_start
112
- command("auto_conditioning_start")["response"]
116
+ command('auto_conditioning_start')['response']
113
117
  end
114
118
 
115
119
  def auto_conditioning_stop
116
- command("auto_conditioning_stop")["response"]
120
+ command('auto_conditioning_stop')['response']
117
121
  end
118
122
 
119
123
  def sun_roof_control(state)
120
- command("sun_roof_control", body: {state: state})["response"]
124
+ command('sun_roof_control', body: {state: state})['response']
121
125
  end
122
126
 
123
127
  def sun_roof_move(percent)
124
- command("sun_roof_control", body: {state: "move", percent: percent})["response"]
128
+ command('sun_roof_control', body: {state: 'move', percent: percent})['response']
125
129
  end
126
130
 
127
131
  def remote_start_drive(password)
128
- command("remote_start_drive", body: {password: password})["response"]
132
+ command('remote_start_drive', body: {password: password})['response']
129
133
  end
130
134
 
131
135
  def open_trunk
132
- command("trunk_open", body: {which_trunk: "rear"})
136
+ command('actuate_trunk', body: {which_trunk: 'rear'})['response']
133
137
  end
134
138
 
135
139
  def open_frunk
136
- command("trunk_open", body: {which_trunk: "rear"})
140
+ command('actuate_trunk', body: {which_trunk: 'front'})['response']
141
+ end
142
+
143
+ def activate_speed_limit(pin)
144
+ command('speed_limit_activate', body: {pin: pin})['response']
145
+ end
146
+
147
+ def deactivate_speed_limit(pin)
148
+ command('speed_limit_deactivate', body: {pin: pin})['response']
149
+ end
150
+
151
+ def set_speed_limit(limit_mph)
152
+ command('speed_limit_set_limit', body: {limit_mph: limit_mph})['response']
153
+ end
154
+
155
+ def clear_speed_limit_pin(pin)
156
+ command('speed_limit_clear_pin', body: {pin: pin})['response']
137
157
  end
138
158
 
139
159
  private