tesla_api 3.0.2 → 3.0.7
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.
- checksums.yaml +4 -4
- data/.tool-versions +1 -1
- data/.travis.yml +2 -2
- data/docs/README.md +1 -1
- data/docs/api-basics/vehicles.md +5 -6
- data/docs/miscellaneous/endpoints.md +26 -4
- data/docs/vehicle/commands/climate.md +2 -0
- data/docs/vehicle/commands/homelink.md +1 -1
- data/docs/vehicle/commands/sharing.md +2 -2
- data/docs/vehicle/commands/valet.md +7 -5
- data/docs/vehicle/commands/wake.md +4 -1
- data/docs/vehicle/commands/windows.md +6 -2
- data/docs/vehicle/optioncodes.md +60 -27
- data/docs/vehicle/state/chargestate.md +17 -16
- data/docs/vehicle/state/climatestate.md +13 -19
- data/docs/vehicle/state/data.md +61 -54
- data/docs/vehicle/state/guisettings.md +1 -0
- data/docs/vehicle/state/nearbychargingsites.md +49 -74
- data/docs/vehicle/state/vehicleconfig.md +3 -3
- data/docs/vehicle/state/vehiclestate.md +43 -19
- data/lib/tesla_api.rb +4 -2
- data/lib/tesla_api/autopark.rb +23 -41
- data/lib/tesla_api/client.rb +13 -6
- data/lib/tesla_api/stream.rb +46 -40
- data/lib/tesla_api/vehicle.rb +5 -1
- data/lib/tesla_api/version.rb +1 -1
- data/spec/cassettes/client-login_timeout.yml +83 -0
- data/spec/cassettes/vehicle-vehicle_data.yml +115 -0
- data/spec/lib/tesla_api/client_spec.rb +30 -0
- data/spec/lib/tesla_api/vehicle_spec.rb +12 -0
- data/tesla_api.gemspec +3 -3
- metadata +14 -10
@@ -14,13 +14,12 @@ Returns the vehicle's configuration information including model, color, badging
|
|
14
14
|
"car_special_type": "base",
|
15
15
|
"car_type": "models2",
|
16
16
|
"charge_port_type": "US",
|
17
|
+
"ece_restrictions": false,
|
17
18
|
"eu_vehicle": false,
|
18
19
|
"exterior_color": "White",
|
19
20
|
"has_air_suspension": true,
|
20
21
|
"has_ludicrous_mode": false,
|
21
|
-
"key_version": 1,
|
22
22
|
"motorized_charge_port": true,
|
23
|
-
"perf_config": "P2",
|
24
23
|
"plg": true,
|
25
24
|
"rear_seat_heaters": 0,
|
26
25
|
"rear_seat_type": 0,
|
@@ -30,8 +29,9 @@ Returns the vehicle's configuration information including model, color, badging
|
|
30
29
|
"spoiler_type": "None",
|
31
30
|
"sun_roof_installed": 2,
|
32
31
|
"third_row_seats": "None",
|
33
|
-
"timestamp":
|
32
|
+
"timestamp": 1604977445448,
|
34
33
|
"trim_badging": "p90d",
|
34
|
+
"use_range_badging": false,
|
35
35
|
"wheel_type": "AeroTurbine19"
|
36
36
|
}
|
37
37
|
}
|
@@ -4,27 +4,51 @@
|
|
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":
|
13
|
-
"autopark_state_v2": "
|
35
|
+
"api_version": 10,
|
36
|
+
"autopark_state_v2": "standby",
|
14
37
|
"autopark_style": "standard",
|
15
38
|
"calendar_supported": true,
|
16
|
-
"car_version": "
|
39
|
+
"car_version": "2020.36.16 3e9e4e8dd287",
|
17
40
|
"center_display_state": 0,
|
18
41
|
"df": 0,
|
19
42
|
"dr": 0,
|
20
43
|
"ft": 0,
|
44
|
+
"homelink_device_count": 2,
|
21
45
|
"homelink_nearby": true,
|
22
46
|
"is_user_present": false,
|
23
47
|
"last_autopark_error": "no_error",
|
24
|
-
"locked":
|
48
|
+
"locked": false,
|
25
49
|
"media_state": { "remote_control_enabled": true },
|
26
50
|
"notifications_supported": true,
|
27
|
-
"odometer":
|
51
|
+
"odometer": 57509.856033,
|
28
52
|
"parsed_calendar_supported": true,
|
29
53
|
"pf": 0,
|
30
54
|
"pr": 0,
|
@@ -32,30 +56,30 @@ Returns the vehicle's physical state, such as which doors are open.
|
|
32
56
|
"remote_start_enabled": true,
|
33
57
|
"remote_start_supported": true,
|
34
58
|
"rt": 0,
|
35
|
-
"sentry_mode":
|
36
|
-
"
|
59
|
+
"sentry_mode": false,
|
60
|
+
"sentry_mode_available": true,
|
61
|
+
"smart_summon_available": true,
|
62
|
+
"software_update": {
|
63
|
+
"download_perc": 0,
|
64
|
+
"expected_duration_sec": 2700,
|
65
|
+
"install_perc": 1,
|
66
|
+
"status": "",
|
67
|
+
"version": ""
|
68
|
+
},
|
37
69
|
"speed_limit_mode": {
|
38
70
|
"active": false,
|
39
71
|
"current_limit_mph": 50.0,
|
40
72
|
"max_limit_mph": 90,
|
41
73
|
"min_limit_mph": 50,
|
42
|
-
"pin_code_set":
|
74
|
+
"pin_code_set": true
|
43
75
|
},
|
76
|
+
"summon_standby_mode_enabled": false,
|
44
77
|
"sun_roof_percent_open": 0,
|
45
|
-
"sun_roof_state": "
|
46
|
-
"timestamp":
|
78
|
+
"sun_roof_state": "closed",
|
79
|
+
"timestamp": 1604977470379,
|
47
80
|
"valet_mode": false,
|
48
81
|
"valet_pin_needed": true,
|
49
82
|
"vehicle_name": "Nikola 2.0"
|
50
83
|
}
|
51
84
|
}
|
52
85
|
```
|
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 |
|
data/lib/tesla_api.rb
CHANGED
@@ -3,8 +3,10 @@ require 'base64'
|
|
3
3
|
|
4
4
|
require 'faraday'
|
5
5
|
require 'faraday_middleware'
|
6
|
-
|
7
|
-
require '
|
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'
|
data/lib/tesla_api/autopark.rb
CHANGED
@@ -1,60 +1,42 @@
|
|
1
1
|
module TeslaApi
|
2
2
|
module Autopark
|
3
3
|
def start_autopark(&handler)
|
4
|
-
|
5
|
-
|
6
|
-
message =
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
17
|
-
|
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
|
27
|
-
|
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
|
41
|
-
|
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
|
53
|
-
|
32
|
+
def autopark_headers
|
33
|
+
{
|
34
|
+
'Authorization' => "Basic #{socket_auth}"
|
35
|
+
}
|
54
36
|
end
|
55
37
|
|
56
|
-
def
|
57
|
-
"
|
38
|
+
def autopark_socket_auth
|
39
|
+
Base64.strict_encode64("#{email}:#{self['tokens'].first}")
|
58
40
|
end
|
59
41
|
end
|
60
42
|
end
|
data/lib/tesla_api/client.rb
CHANGED
@@ -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
|
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
|
-
|
26
|
-
|
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
|
-
'
|
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
|
-
'
|
62
|
+
@base_uri + '/oauth/token',
|
56
63
|
{
|
57
64
|
grant_type: 'password',
|
58
65
|
client_id: client_id,
|
data/lib/tesla_api/stream.rb
CHANGED
@@ -1,57 +1,63 @@
|
|
1
1
|
module TeslaApi
|
2
2
|
module Stream
|
3
|
-
def
|
4
|
-
|
5
|
-
|
3
|
+
def self.streaming_endpoint_url
|
4
|
+
'wss://streaming.vn.teslamotors.com/streaming/'
|
5
|
+
end
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
def self.streaming_endpoint
|
8
|
+
Async::HTTP::Endpoint.parse(streaming_endpoint_url, alpn_protocols: Async::HTTP::Protocol::HTTP11.names)
|
9
|
+
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
})
|
11
|
+
def stream(endpoint: Stream.streaming_endpoint, &receiver)
|
12
|
+
Async do |task|
|
13
|
+
Async::WebSocket::Client.connect(endpoint) do |connection|
|
14
|
+
on_timeout = ->(subtask) do
|
15
|
+
subtask.sleep TIMEOUT
|
16
|
+
task.stop
|
32
17
|
end
|
33
|
-
end
|
34
18
|
|
35
|
-
|
36
|
-
|
19
|
+
connection.write(streaming_connect_message)
|
20
|
+
timeout = task.async(&on_timeout)
|
21
|
+
|
22
|
+
while message = connection.read
|
23
|
+
timeout.stop
|
24
|
+
timeout = task.async(&on_timeout)
|
25
|
+
|
26
|
+
case message[:msg_type]
|
27
|
+
when 'data:update'
|
28
|
+
attributes = message[:value].split(',')
|
29
|
+
|
30
|
+
receiver.call({
|
31
|
+
time: DateTime.strptime((attributes[0].to_i/1000).to_s, '%s'),
|
32
|
+
speed: attributes[1].to_f,
|
33
|
+
odometer: attributes[2].to_f,
|
34
|
+
soc: attributes[3].to_f,
|
35
|
+
elevation: attributes[4].to_f,
|
36
|
+
est_heading: attributes[5].to_f,
|
37
|
+
est_lat: attributes[6].to_f,
|
38
|
+
est_lng: attributes[7].to_f,
|
39
|
+
power: attributes[8].to_f,
|
40
|
+
shift_state: attributes[9].to_s,
|
41
|
+
range: attributes[10].to_f,
|
42
|
+
est_range: attributes[11].to_f,
|
43
|
+
heading: attributes[12].to_f
|
44
|
+
})
|
45
|
+
when 'data:error'
|
46
|
+
task.stop
|
47
|
+
end
|
48
|
+
end
|
37
49
|
end
|
38
50
|
end
|
39
51
|
end
|
40
52
|
|
41
53
|
private
|
42
54
|
|
43
|
-
|
44
|
-
Faye::WebSocket::Client.new(streaming_endpoint)
|
45
|
-
end
|
46
|
-
|
47
|
-
def streaming_endpoint
|
48
|
-
'wss://streaming.vn.teslamotors.com/streaming/'
|
49
|
-
end
|
55
|
+
TIMEOUT = 30
|
50
56
|
|
51
|
-
def
|
57
|
+
def streaming_connect_message
|
52
58
|
{
|
53
|
-
msg_type: 'data:
|
54
|
-
token:
|
59
|
+
msg_type: 'data:subscribe_oauth',
|
60
|
+
token: client.access_token,
|
55
61
|
value: 'speed,odometer,soc,elevation,est_heading,est_lat,est_lng,power,shift_state,range,est_range,heading',
|
56
62
|
tag: self['vehicle_id'].to_s,
|
57
63
|
}
|
data/lib/tesla_api/vehicle.rb
CHANGED
@@ -27,6 +27,10 @@ module TeslaApi
|
|
27
27
|
|
28
28
|
# State
|
29
29
|
|
30
|
+
def vehicle_data
|
31
|
+
client.get("/vehicles/#{id}/vehicle_data")['response']
|
32
|
+
end
|
33
|
+
|
30
34
|
def data
|
31
35
|
client.get("/vehicles/#{id}/data")['response']
|
32
36
|
end
|
@@ -94,7 +98,7 @@ module TeslaApi
|
|
94
98
|
end
|
95
99
|
|
96
100
|
def set_charge_limit(percent)
|
97
|
-
command('set_charge_limit', body: {percent: percent})['response']
|
101
|
+
command('set_charge_limit', body: {percent: percent.to_i})['response']
|
98
102
|
end
|
99
103
|
|
100
104
|
def charge_start
|
data/lib/tesla_api/version.rb
CHANGED
@@ -0,0 +1,83 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://owner-api.teslamotors.com/oauth/token
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: grant_type=password&client_id=<TESLA_CLIENT_ID>&client_secret=<TESLA_CLIENT_SECRET>&email=<TESLA_EMAIL>&password=<TESLA_PASS>
|
9
|
+
headers:
|
10
|
+
Accept-Encoding:
|
11
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
12
|
+
Accept:
|
13
|
+
- "*/*"
|
14
|
+
User-Agent:
|
15
|
+
- Ruby
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 408
|
19
|
+
message: Request Timeout
|
20
|
+
headers:
|
21
|
+
Server:
|
22
|
+
- nginx
|
23
|
+
Date:
|
24
|
+
- Mon, 15 Dec 2014 03:09:22 GMT
|
25
|
+
Content-Type:
|
26
|
+
- application/json; charset=utf-8
|
27
|
+
Transfer-Encoding:
|
28
|
+
- chunked
|
29
|
+
Connection:
|
30
|
+
- keep-alive
|
31
|
+
Status:
|
32
|
+
- 408 Request Timeout
|
33
|
+
body:
|
34
|
+
encoding: UTF-8
|
35
|
+
string: '{}'
|
36
|
+
http_version:
|
37
|
+
recorded_at: Mon, 15 Dec 2014 03:09:22 GMT
|
38
|
+
- request:
|
39
|
+
method: post
|
40
|
+
uri: https://owner-api.teslamotors.com/oauth/token
|
41
|
+
body:
|
42
|
+
encoding: UTF-8
|
43
|
+
string: grant_type=password&client_id=<TESLA_CLIENT_ID>&client_secret=<TESLA_CLIENT_SECRET>&email=<TESLA_EMAIL>&password=<TESLA_PASS>
|
44
|
+
headers:
|
45
|
+
Accept-Encoding:
|
46
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
47
|
+
Accept:
|
48
|
+
- "*/*"
|
49
|
+
User-Agent:
|
50
|
+
- Ruby
|
51
|
+
response:
|
52
|
+
status:
|
53
|
+
code: 200
|
54
|
+
message: OK
|
55
|
+
headers:
|
56
|
+
Server:
|
57
|
+
- nginx
|
58
|
+
Date:
|
59
|
+
- Mon, 15 Dec 2014 03:09:22 GMT
|
60
|
+
Content-Type:
|
61
|
+
- application/json; charset=utf-8
|
62
|
+
Transfer-Encoding:
|
63
|
+
- chunked
|
64
|
+
Connection:
|
65
|
+
- keep-alive
|
66
|
+
Status:
|
67
|
+
- 200 OK
|
68
|
+
Cache-Control:
|
69
|
+
- no-store
|
70
|
+
Pragma:
|
71
|
+
- no-cache
|
72
|
+
X-Ua-Compatible:
|
73
|
+
- IE=Edge,chrome=1
|
74
|
+
X-Request-Id:
|
75
|
+
- 349d563d345a9694c610770b743d3006
|
76
|
+
X-Runtime:
|
77
|
+
- '0.416152'
|
78
|
+
body:
|
79
|
+
encoding: UTF-8
|
80
|
+
string: '{"access_token":"1cba4845a8653d4b731440e9911d84304a179bd16a9ecbc9b649f2d8e0f6947e","token_type":"bearer","expires_in":7776000,"refresh_token":"fea03b395fa4e72ebc399d9cda6163dcf438c248f744ebdd5bfcda571f5f317f","created_at":1475777133}'
|
81
|
+
http_version:
|
82
|
+
recorded_at: Mon, 15 Dec 2014 03:09:22 GMT
|
83
|
+
recorded_with: VCR 2.9.3
|