tesla_api 3.0.2 → 3.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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": 1538364666096,
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": 6,
13
- "autopark_state_v2": "ready",
35
+ "api_version": 10,
36
+ "autopark_state_v2": "standby",
14
37
  "autopark_style": "standard",
15
38
  "calendar_supported": true,
16
- "car_version": "2018.42.2 19e7e44",
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": true,
48
+ "locked": false,
25
49
  "media_state": { "remote_control_enabled": true },
26
50
  "notifications_supported": true,
27
- "odometer": 36051.517239,
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": true,
36
- "software_update": { "expected_duration_sec": 2700, "status": "" },
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": false
74
+ "pin_code_set": true
43
75
  },
76
+ "summon_standby_mode_enabled": false,
44
77
  "sun_roof_percent_open": 0,
45
- "sun_roof_state": "unknown",
46
- "timestamp": 1543187581934,
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 |
@@ -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,
@@ -1,57 +1,63 @@
1
1
  module TeslaApi
2
2
  module Stream
3
- def stream(&receiver)
4
- EventMachine.run do
5
- socket = create_streaming_socket
3
+ def self.streaming_endpoint_url
4
+ 'wss://streaming.vn.teslamotors.com/streaming/'
5
+ end
6
6
 
7
- socket.on(:open) do |event|
8
- socket.send(JSON.generate(stream_connect_message))
9
- end
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
- 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
- })
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
- socket.on(:close) do |event|
36
- EventMachine.stop
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
- def create_streaming_socket
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 stream_connect_message
57
+ def streaming_connect_message
52
58
  {
53
- msg_type: 'data:subscribe',
54
- token: Base64.strict_encode64("#{email}:#{self['tokens'].first}"),
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
  }
@@ -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
@@ -1,3 +1,3 @@
1
1
  module TeslaApi
2
- VERSION = '3.0.2'
2
+ VERSION = '3.0.7'
3
3
  end
@@ -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