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.
@@ -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