tesla_api 3.0.0 → 3.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/docs/README.md +1 -1
  3. data/docs/SUMMARY.md +35 -34
  4. data/docs/api-basics/vehicles.md +5 -6
  5. data/docs/miscellaneous/endpoints.md +29 -4
  6. data/docs/vehicle/commands/README.md +11 -3
  7. data/docs/vehicle/commands/climate.md +21 -0
  8. data/docs/vehicle/commands/homelink.md +21 -0
  9. data/docs/vehicle/commands/sharing.md +38 -0
  10. data/docs/vehicle/commands/valet.md +7 -5
  11. data/docs/vehicle/commands/wake.md +4 -1
  12. data/docs/vehicle/commands/windows.md +26 -0
  13. data/docs/vehicle/optioncodes.md +22 -6
  14. data/docs/vehicle/state/chargestate.md +1 -0
  15. data/docs/vehicle/state/climatestate.md +1 -1
  16. data/docs/vehicle/state/data.md +27 -7
  17. data/docs/vehicle/state/guisettings.md +1 -0
  18. data/docs/vehicle/state/vehiclestate.md +46 -15
  19. data/lib/tesla_api.rb +4 -2
  20. data/lib/tesla_api/autopark.rb +23 -41
  21. data/lib/tesla_api/client.rb +14 -7
  22. data/lib/tesla_api/stream.rb +40 -34
  23. data/lib/tesla_api/vehicle.rb +49 -1
  24. data/lib/tesla_api/version.rb +1 -1
  25. data/spec/cassettes/client-login_timeout.yml +83 -0
  26. data/spec/cassettes/vehicle-media_next_fav.yml +109 -0
  27. data/spec/cassettes/vehicle-media_next_track.yml +109 -0
  28. data/spec/cassettes/vehicle-media_prev_fav.yml +109 -0
  29. data/spec/cassettes/vehicle-media_prev_track.yml +109 -0
  30. data/spec/cassettes/vehicle-media_toggle_playback.yml +109 -0
  31. data/spec/cassettes/vehicle-media_volume_down.yml +109 -0
  32. data/spec/cassettes/vehicle-media_volume_up.yml +109 -0
  33. data/spec/cassettes/vehicle-vehicle_config.yml +108 -0
  34. data/spec/lib/tesla_api/client_spec.rb +30 -0
  35. data/spec/lib/tesla_api/vehicle_spec.rb +57 -0
  36. data/tesla_api.gemspec +1 -1
  37. metadata +30 -10
  38. data/docs/vehicle/commands/navigation.md +0 -36
@@ -43,6 +43,7 @@ Information on the state of charge in the battery and its various settings.
43
43
  "managed_charging_start_time": null,
44
44
  "managed_charging_user_canceled": false,
45
45
  "max_range_charge_counter": 0,
46
+ "minutes_to_full_charge": 0,
46
47
  "not_enough_power_to_heat": false,
47
48
  "scheduled_charging_pending": false,
48
49
  "scheduled_charging_start_time": null,
@@ -12,6 +12,7 @@ Information on the current internal temperature and climate control system.
12
12
  "battery_heater": false,
13
13
  "battery_heater_no_power": false,
14
14
  "climate_keeper_mode": "dog",
15
+ "defrost_mode": 0,
15
16
  "driver_temp_setting": 21.6,
16
17
  "fan_status": 0,
17
18
  "inside_temp": null,
@@ -35,7 +36,6 @@ Information on the current internal temperature and climate control system.
35
36
  "seat_heater_rear_right_back": 0,
36
37
  "seat_heater_right": 2,
37
38
  "side_mirror_heaters": false,
38
- "smart_preconditioning": false,
39
39
  "steering_wheel_heater": false,
40
40
  "timestamp": 1543187641727,
41
41
  "wiper_blade_heater": false
@@ -4,7 +4,7 @@
4
4
 
5
5
  A rollup of all the `data_request` endpoints plus vehicle configuration.
6
6
 
7
- *Note:* all `*_range` values are in miles, irrespective of GUI configuration.
7
+ _Note:_ all `*_range` values are in miles, irrespective of GUI configuration.
8
8
 
9
9
  ### Response
10
10
 
@@ -23,7 +23,7 @@ A rollup of all the `data_request` endpoints plus vehicle configuration.
23
23
  "in_service": false,
24
24
  "id_s": "12345678901234567",
25
25
  "calendar_enabled": true,
26
- "api_version": 6,
26
+ "api_version": 7,
27
27
  "backseat_token": null,
28
28
  "backseat_token_updated_at": null,
29
29
  "drive_state": {
@@ -44,6 +44,7 @@ A rollup of all the `data_request` endpoints plus vehicle configuration.
44
44
  "battery_heater": false,
45
45
  "battery_heater_no_power": false,
46
46
  "climate_keeper_mode": "dog",
47
+ "defrost_mode": 0,
47
48
  "driver_temp_setting": 21.6,
48
49
  "fan_status": 0,
49
50
  "inside_temp": null,
@@ -67,7 +68,6 @@ A rollup of all the `data_request` endpoints plus vehicle configuration.
67
68
  "seat_heater_rear_right_back": 0,
68
69
  "seat_heater_right": 2,
69
70
  "side_mirror_heaters": false,
70
- "smart_preconditioning": false,
71
71
  "steering_wheel_heater": false,
72
72
  "timestamp": 1543186971731,
73
73
  "wiper_blade_heater": false
@@ -107,6 +107,7 @@ A rollup of all the `data_request` endpoints plus vehicle configuration.
107
107
  "managed_charging_start_time": null,
108
108
  "managed_charging_user_canceled": false,
109
109
  "max_range_charge_counter": 0,
110
+ "minutes_to_full_charge": 0,
110
111
  "not_enough_power_to_heat": false,
111
112
  "scheduled_charging_pending": false,
112
113
  "scheduled_charging_start_time": null,
@@ -122,34 +123,51 @@ A rollup of all the `data_request` endpoints plus vehicle configuration.
122
123
  "gui_distance_units": "mi/hr",
123
124
  "gui_range_display": "Rated",
124
125
  "gui_temperature_units": "F",
126
+ "show_range_units": true,
125
127
  "timestamp": 1543186971728
126
128
  },
127
129
  "vehicle_state": {
128
- "api_version": 6,
130
+ "api_version": 7,
129
131
  "autopark_state_v2": "standby",
130
132
  "autopark_style": "standard",
131
133
  "calendar_supported": true,
132
- "car_version": "2018.42.2 19e7e44",
134
+ "car_version": "2019.40.2.1 38f55d9f9205",
133
135
  "center_display_state": 0,
134
136
  "df": 0,
135
137
  "dr": 0,
138
+ "fd_window": 0,
139
+ "fp_window": 0,
136
140
  "ft": 0,
141
+ "homelink_device_count": 0,
137
142
  "homelink_nearby": true,
138
143
  "is_user_present": false,
139
144
  "last_autopark_error": "no_error",
140
145
  "locked": true,
141
- "media_state": { "remote_control_enabled": true },
146
+ "media_state": {
147
+ "remote_control_enabled": true
148
+ },
142
149
  "notifications_supported": true,
143
150
  "odometer": 33561.422505,
144
151
  "parsed_calendar_supported": true,
145
152
  "pf": 0,
146
153
  "pr": 0,
154
+ "rd_window": 0,
147
155
  "remote_start": false,
148
156
  "remote_start_enabled": true,
149
157
  "remote_start_supported": true,
158
+ "rp_window": 0,
150
159
  "rt": 0,
151
160
  "sentry_mode": true,
152
- "software_update": { "expected_duration_sec": 2700, "status": "" },
161
+ "sentry_mode_available": true,
162
+ "smart_summon_available": true,
163
+ "software_update": {
164
+ "download_perc": 100,
165
+ "expected_duration_sec": 2700,
166
+ "install_perc": 10,
167
+ "scheduled_time_ms": 1575689678432,
168
+ "status": "scheduled",
169
+ "version": "2019.40.2.1"
170
+ },
153
171
  "speed_limit_mode": {
154
172
  "active": false,
155
173
  "current_limit_mph": 75.0,
@@ -157,6 +175,7 @@ A rollup of all the `data_request` endpoints plus vehicle configuration.
157
175
  "min_limit_mph": 50,
158
176
  "pin_code_set": false
159
177
  },
178
+ "summon_standby_mode_enabled": true,
160
179
  "sun_roof_percent_open": 0,
161
180
  "sun_roof_state": "unknown",
162
181
  "timestamp": 1538364666096,
@@ -188,6 +207,7 @@ A rollup of all the `data_request` endpoints plus vehicle configuration.
188
207
  "third_row_seats": "None",
189
208
  "timestamp": 1538364666096,
190
209
  "trim_badging": "p90d",
210
+ "use_range_badging": false,
191
211
  "wheel_type": "AeroTurbine19"
192
212
  }
193
213
  }
@@ -14,6 +14,7 @@ Returns various information about the GUI settings of the car, such as unit form
14
14
  "gui_distance_units": "mi/hr",
15
15
  "gui_range_display": "Rated",
16
16
  "gui_temperature_units": "F",
17
+ "show_range_units": true,
17
18
  "timestamp": 1543187561462
18
19
  }
19
20
  }
@@ -4,36 +4,75 @@
4
4
 
5
5
  Returns the vehicle's physical state, such as which doors are open.
6
6
 
7
+ For the trunk (rt) and frunk (ft) fields, you should interpret a zero (0) value as closed and a non-zero value as open (partially or fully).
8
+
9
+ Here are the currently known values for the `center_display_state` field:
10
+
11
+ | State | Description |
12
+ | ----- | --------------- |
13
+ | 0 | Off |
14
+ | 2 | Normal On |
15
+ | 3 | Charging Screen |
16
+ | 7 | Sentry Mode |
17
+ | 8 | Dog Mode |
18
+
19
+ Here are the descriptions for the shorthand fields:
20
+
21
+ | Field | Description |
22
+ | ----- | --------------- |
23
+ | df | driver front |
24
+ | dr | driver rear |
25
+ | pf | passenger front |
26
+ | pr | passenger rear |
27
+ | ft | front trunk |
28
+ | rt | rear trunk |
29
+
7
30
  ### Response
8
31
 
9
32
  ```json
10
33
  {
11
34
  "response": {
12
- "api_version": 6,
13
- "autopark_state_v2": "ready",
14
- "autopark_style": "standard",
35
+ "api_version": 7,
36
+ "autopark_state_v3": "standby",
37
+ "autopark_style": "dead_man",
15
38
  "calendar_supported": true,
16
- "car_version": "2018.42.2 19e7e44",
39
+ "car_version": "2019.40.2.1 38f55d9f9205",
17
40
  "center_display_state": 0,
18
41
  "df": 0,
19
42
  "dr": 0,
43
+ "fd_window": 0,
44
+ "fp_window": 0,
20
45
  "ft": 0,
46
+ "homelink_device_count": 0,
21
47
  "homelink_nearby": true,
22
48
  "is_user_present": false,
23
49
  "last_autopark_error": "no_error",
24
50
  "locked": true,
25
- "media_state": { "remote_control_enabled": true },
51
+ "media_state": {
52
+ "remote_control_enabled": true
53
+ },
26
54
  "notifications_supported": true,
27
55
  "odometer": 36051.517239,
28
56
  "parsed_calendar_supported": true,
29
57
  "pf": 0,
30
58
  "pr": 0,
59
+ "rd_window": 0,
31
60
  "remote_start": false,
32
61
  "remote_start_enabled": true,
33
62
  "remote_start_supported": true,
63
+ "rp_window": 0,
34
64
  "rt": 0,
35
65
  "sentry_mode": true,
36
- "software_update": { "expected_duration_sec": 2700, "status": "" },
66
+ "sentry_mode_available": true,
67
+ "smart_summon_available": true,
68
+ "software_update": {
69
+ "download_perc": 100,
70
+ "expected_duration_sec": 2700,
71
+ "install_perc": 10,
72
+ "scheduled_time_ms": 1575689678432,
73
+ "status": "scheduled",
74
+ "version": "2019.40.2.1"
75
+ },
37
76
  "speed_limit_mode": {
38
77
  "active": false,
39
78
  "current_limit_mph": 50.0,
@@ -41,6 +80,7 @@ Returns the vehicle's physical state, such as which doors are open.
41
80
  "min_limit_mph": 50,
42
81
  "pin_code_set": false
43
82
  },
83
+ "summon_standby_mode_enabled": true,
44
84
  "sun_roof_percent_open": 0,
45
85
  "sun_roof_state": "unknown",
46
86
  "timestamp": 1543187581934,
@@ -50,12 +90,3 @@ Returns the vehicle's physical state, such as which doors are open.
50
90
  }
51
91
  }
52
92
  ```
53
-
54
- ### Center Display States
55
- | State | Description |
56
- |-------|-----------------|
57
- | 0 | Off |
58
- | 2 | Normal On |
59
- | 3 | Charging Screen |
60
- | 7 | Sentry Mode |
61
- | 8 | Dog Mode |
@@ -3,8 +3,10 @@ require 'base64'
3
3
 
4
4
  require 'faraday'
5
5
  require 'faraday_middleware'
6
- require 'eventmachine'
7
- require 'faye/websocket'
6
+
7
+ require 'async'
8
+ require 'async/http/endpoint'
9
+ require 'async/websocket/client'
8
10
 
9
11
  require 'tesla_api/version'
10
12
  require 'tesla_api/client'
@@ -1,60 +1,42 @@
1
1
  module TeslaApi
2
2
  module Autopark
3
3
  def start_autopark(&handler)
4
- EventMachine.run do
5
- autopark_socket.on(:message) do |event|
6
- message = if event.data.is_a?(Array)
7
- JSON.parse(event.data.map(&:chr).join)
8
- else
9
- JSON.parse(event.data)
10
- end
11
-
12
- default_handler(message)
13
- handler.call(message.delete('msg_type'), message)
14
- end
4
+ Async do |task|
5
+ Async::WebSocket::Client.connect(autopark_endpoint, headers: headers) do |connection|
6
+ while message = connection.read
7
+ case message[:msg_type]
8
+ when 'control:hello'
9
+ interval = message[:autopark][:heartbeat_frequency] / 1000.0
10
+ task.async do |subtask|
11
+ subtask.sleep interval
12
+ connection.write({ msg_type: 'autopark:heartbeat_app', timestamp: Time.now.to_i }.to_json)
13
+ end
14
+ end
15
15
 
16
- autopark_socket.on(:close) do |_|
17
- @autopark_socket = nil
18
- @heartbeat && @heartbeat.cancel
19
- EventMachine.stop
16
+ handler.call(message)
17
+ end
20
18
  end
21
19
  end
22
20
  end
23
21
 
24
22
  private
25
23
 
26
- def default_handler(message)
27
- case message['msg_type']
28
- when 'control:hello'
29
- interval = message['autopark']['heartbeat_frequency'] / 1000.0
30
- @heartbeat = EventMachine::Timer.new(interval) do
31
- beat = {
32
- msg_type: 'autopark:heartbeat_app',
33
- timestamp: Time.now.to_i
34
- }
35
- autopark_socket.send(beat.to_json)
36
- end
37
- end
24
+ def autopark_endpoint
25
+ Async::HTTP::Endpoint.parse(autopark_endpoint_url)
38
26
  end
39
27
 
40
- def autopark_socket
41
- @autopark_socket ||= Faye::WebSocket::Client.new(
42
- autopark_socket_endpoint,
43
- nil,
44
- {
45
- headers: {
46
- 'Authorization' => "Basic #{socket_auth}"
47
- }
48
- }
49
- )
28
+ def autopark_endpoint_url
29
+ "wss://streaming.vn.teslamotors.com/connect/#{self['vehicle_id']}"
50
30
  end
51
31
 
52
- def socket_auth
53
- Base64.strict_encode64("#{email}:#{self['tokens'].first}")
32
+ def autopark_headers
33
+ {
34
+ 'Authorization' => "Basic #{socket_auth}"
35
+ }
54
36
  end
55
37
 
56
- def autopark_socket_endpoint
57
- "wss://streaming.vn.teslamotors.com/connect/#{self['vehicle_id']}"
38
+ def autopark_socket_auth
39
+ Base64.strict_encode64("#{email}:#{self['tokens'].first}")
58
40
  end
59
41
  end
60
42
  end
@@ -2,7 +2,7 @@ module TeslaApi
2
2
  class Client
3
3
  attr_reader :api, :email, :access_token, :access_token_expires_at, :refresh_token, :client_id, :client_secret
4
4
 
5
- BASE_URI = 'https://owner-api.teslamotors.com/api/1'
5
+ BASE_URI = 'https://owner-api.teslamotors.com'
6
6
 
7
7
  def initialize(
8
8
  email: nil,
@@ -10,9 +10,13 @@ module TeslaApi
10
10
  access_token_expires_at: nil,
11
11
  refresh_token: nil,
12
12
  client_id: ENV['TESLA_CLIENT_ID'],
13
- client_secret: ENV['TESLA_CLIENT_SECRET']
13
+ client_secret: ENV['TESLA_CLIENT_SECRET'],
14
+ retry_options: nil,
15
+ base_uri: nil,
16
+ client_options: {}
14
17
  )
15
18
  @email = email
19
+ @base_uri = base_uri || BASE_URI
16
20
 
17
21
  @client_id = client_id
18
22
  @client_secret = client_secret
@@ -22,19 +26,22 @@ module TeslaApi
22
26
  @refresh_token = refresh_token
23
27
 
24
28
  @api = Faraday.new(
25
- BASE_URI,
26
- headers: { 'User-Agent' => "github.com/timdorr/tesla-api v:#{VERSION}" }
29
+ @base_uri + '/api/1',
30
+ {
31
+ headers: { 'User-Agent' => "github.com/timdorr/tesla-api v:#{VERSION}" }
32
+ }.merge(client_options)
27
33
  ) do |conn|
28
34
  conn.request :json
29
35
  conn.response :json
30
36
  conn.response :raise_error
37
+ conn.request :retry, retry_options if retry_options # Must be registered after :raise_error
31
38
  conn.adapter Faraday.default_adapter
32
39
  end
33
40
  end
34
41
 
35
42
  def refresh_access_token
36
43
  response = api.post(
37
- 'https://owner-api.teslamotors.com/oauth/token',
44
+ @base_uri + '/oauth/token',
38
45
  {
39
46
  grant_type: 'refresh_token',
40
47
  client_id: client_id,
@@ -52,7 +59,7 @@ module TeslaApi
52
59
 
53
60
  def login!(password)
54
61
  response = api.post(
55
- 'https://owner-api.teslamotors.com/oauth/token',
62
+ @base_uri + '/oauth/token',
56
63
  {
57
64
  grant_type: 'password',
58
65
  client_id: client_id,
@@ -87,7 +94,7 @@ module TeslaApi
87
94
  end
88
95
 
89
96
  def vehicle(id)
90
- Vehicle.new(self, email, id, self.class.get("/vehicles/#{id}")['response'])
97
+ Vehicle.new(self, email, id, self.get("/vehicles/#{id}")['response'])
91
98
  end
92
99
  end
93
100
  end
@@ -1,54 +1,60 @@
1
1
  module TeslaApi
2
2
  module Stream
3
3
  def stream(&receiver)
4
- EventMachine.run do
5
- socket = create_streaming_socket
4
+ Async do |task|
5
+ Async::WebSocket::Client.connect(streaming_endpoint) do |connection|
6
+ on_timeout = ->(subtask) do
7
+ subtask.sleep TIMEOUT
8
+ task.stop
9
+ end
6
10
 
7
- socket.on(:open) do |event|
8
- socket.send(JSON.generate(stream_connect_message))
9
- end
11
+ connection.write(streaming_connect_message)
12
+ timeout = task.async(&on_timeout)
10
13
 
11
- socket.on(:message) do |event|
12
- data = JSON.parse(event.data.pack('c*'))
13
-
14
- if data['msg_type'] == 'data:update'
15
- attributes = data['value'].split(',')
16
-
17
- receiver.call({
18
- time: DateTime.strptime((attributes[0].to_i/1000).to_s, '%s'),
19
- speed: attributes[1].to_f,
20
- odometer: attributes[2].to_f,
21
- soc: attributes[3].to_f,
22
- elevation: attributes[4].to_f,
23
- est_heading: attributes[5].to_f,
24
- est_lat: attributes[6].to_f,
25
- est_lng: attributes[7].to_f,
26
- power: attributes[8].to_f,
27
- shift_state: attributes[9].to_s,
28
- range: attributes[10].to_f,
29
- est_range: attributes[11].to_f,
30
- heading: attributes[12].to_f
31
- })
32
- end
33
- end
14
+ while message = connection.read
15
+ timeout.stop
16
+ timeout = task.async(&on_timeout)
17
+
18
+ case message[:msg_type]
19
+ when 'data:update'
20
+ attributes = message[:value].split(',')
34
21
 
35
- socket.on(:close) do |event|
36
- EventMachine.stop
22
+ receiver.call({
23
+ time: DateTime.strptime((attributes[0].to_i/1000).to_s, '%s'),
24
+ speed: attributes[1].to_f,
25
+ odometer: attributes[2].to_f,
26
+ soc: attributes[3].to_f,
27
+ elevation: attributes[4].to_f,
28
+ est_heading: attributes[5].to_f,
29
+ est_lat: attributes[6].to_f,
30
+ est_lng: attributes[7].to_f,
31
+ power: attributes[8].to_f,
32
+ shift_state: attributes[9].to_s,
33
+ range: attributes[10].to_f,
34
+ est_range: attributes[11].to_f,
35
+ heading: attributes[12].to_f
36
+ })
37
+ when 'data:error'
38
+ task.stop
39
+ end
40
+ end
37
41
  end
38
42
  end
39
43
  end
40
44
 
41
45
  private
42
46
 
43
- def create_streaming_socket
44
- Faye::WebSocket::Client.new(streaming_endpoint)
45
- end
47
+ TIMEOUT = 30
46
48
 
47
49
  def streaming_endpoint
50
+ Async::HTTP::Endpoint.parse(streaming_endpoint_url)
51
+ end
52
+
53
+ def streaming_endpoint_url
48
54
  'wss://streaming.vn.teslamotors.com/streaming/'
49
55
  end
50
56
 
51
- def stream_connect_message
57
+ def streaming_connect_message
52
58
  {
53
59
  msg_type: 'data:subscribe',
54
60
  token: Base64.strict_encode64("#{email}:#{self['tokens'].first}"),