tesla_api 3.0.0 → 3.0.5

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.
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}"),