tesla_api 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ machine:
2
+ ruby:
3
+ version:
4
+ 2.3.0
5
+ environment:
6
+ TESLA_EMAIL: elon.musk@teslamotors.com
7
+ TESLA_PASS: oilLOL
8
+ TESLA_CLIENT_ID: 1
9
+ TESLA_CLIENT_SECRET: 2
10
+ TESLA_API_TOKEN: 3
@@ -3,4 +3,5 @@ require "em-http-request"
3
3
 
4
4
  require "tesla_api/version"
5
5
  require "tesla_api/client"
6
+ require "tesla_api/stream"
6
7
  require "tesla_api/vehicle"
@@ -0,0 +1,49 @@
1
+ module TeslaApi
2
+ module Stream
3
+ def stream(&reciever)
4
+ EventMachine.run do
5
+ http.stream do |chunk|
6
+ attributes = chunk.split(",")
7
+
8
+ reciever.call({
9
+ time: DateTime.strptime((attributes[0].to_i/1000).to_s, "%s"),
10
+ speed: attributes[1].to_f,
11
+ odometer: attributes[2].to_f,
12
+ soc: attributes[3].to_f,
13
+ elevation: attributes[4].to_f,
14
+ est_heading: attributes[5].to_f,
15
+ est_lat: attributes[6].to_f,
16
+ est_lng: attributes[7].to_f,
17
+ power: attributes[8].to_f
18
+ })
19
+ end
20
+
21
+ http.callback { EventMachine.stop }
22
+ http.errback { EventMachine.stop }
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def request
29
+ @request ||= EventMachine::HttpRequest.new(
30
+ "#{stream_endpoint}/stream/#{self["vehicle_id"]}/?values=#{stream_params}")
31
+ end
32
+
33
+ def http
34
+ @http ||= request.get(
35
+ head: {
36
+ "authorization" => [email, self["tokens"].first]
37
+ },
38
+ inactivity_timeout: 15)
39
+ end
40
+
41
+ def stream_endpoint
42
+ "https://streaming.vn.teslamotors.com"
43
+ end
44
+
45
+ def stream_params
46
+ "speed,odometer,soc,elevation,est_heading,est_lat,est_lng,power"
47
+ end
48
+ end
49
+ end
@@ -1,5 +1,6 @@
1
1
  module TeslaApi
2
2
  class Vehicle
3
+ include Stream
3
4
  attr_reader :api, :email, :id, :vehicle
4
5
 
5
6
  def initialize(api, email, id, vehicle)
@@ -30,23 +31,23 @@ module TeslaApi
30
31
  end
31
32
 
32
33
  def gui_settings
33
- api.get("/vehicles/#{id}/data_request/gui_settings")["response"]
34
+ data_request("gui_settings")["response"]
34
35
  end
35
36
 
36
37
  def charge_state
37
- api.get("/vehicles/#{id}/data_request/charge_state")["response"]
38
+ data_request("charge_state")["response"]
38
39
  end
39
40
 
40
41
  def climate_state
41
- api.get("/vehicles/#{id}/data_request/climate_state")["response"]
42
+ data_request("climate_state")["response"]
42
43
  end
43
44
 
44
45
  def drive_state
45
- api.get("/vehicles/#{id}/data_request/drive_state")["response"]
46
+ data_request("drive_state")["response"]
46
47
  end
47
48
 
48
49
  def vehicle_state
49
- api.get("/vehicles/#{id}/data_request/vehicle_state")["response"]
50
+ data_request("vehicle_state")["response"]
50
51
  end
51
52
 
52
53
  # Commands
@@ -55,113 +56,94 @@ module TeslaApi
55
56
  @vehicle = api.post("/vehicles/#{id}/wake_up")["response"]
56
57
  end
57
58
 
59
+ def set_valet_mode(on, password=nil)
60
+ command("set_valet_mode", body: {on: on, password: password})["response"]
61
+ end
62
+
63
+ def reset_valet_pin
64
+ command("reset_valet_pin")["response"]
65
+ end
66
+
58
67
  def charge_port_door_open
59
- api.post("/vehicles/#{id}/command/charge_port_door_open")["response"]
68
+ command("charge_port_door_open")["response"]
60
69
  end
61
70
 
62
71
  def charge_standard
63
- api.post("/vehicles/#{id}/command/charge_standard")["response"]
72
+ command("charge_standard")["response"]
64
73
  end
65
74
 
66
75
  def charge_max_range
67
- api.post("/vehicles/#{id}/command/charge_max_range")["response"]
76
+ command("charge_max_range")["response"]
68
77
  end
69
78
 
70
79
  def set_charge_limit(percent)
71
- api.post("/vehicles/#{id}/command/set_charge_limit", body: {percent: percent})["response"]
80
+ command("set_charge_limit", body: {percent: percent})["response"]
72
81
  end
73
82
 
74
83
  def charge_start
75
- api.post("/vehicles/#{id}/command/charge_start")["response"]
84
+ command("charge_start")["response"]
76
85
  end
77
86
 
78
87
  def charge_stop
79
- api.post("/vehicles/#{id}/command/charge_stop")["response"]
88
+ command("charge_stop")["response"]
80
89
  end
81
90
 
82
91
  def flash_lights
83
- api.post("/vehicles/#{id}/command/flash_lights")["response"]
92
+ command("flash_lights")["response"]
84
93
  end
85
94
 
86
95
  def honk_horn
87
- api.post("/vehicles/#{id}/command/honk_horn")["response"]
96
+ command("honk_horn")["response"]
88
97
  end
89
98
 
90
99
  def door_unlock
91
- api.post("/vehicles/#{id}/command/door_unlock")["response"]
100
+ command("door_unlock")["response"]
92
101
  end
93
102
 
94
103
  def door_lock
95
- api.post("/vehicles/#{id}/command/door_lock")["response"]
104
+ command("door_lock")["response"]
96
105
  end
97
106
 
98
107
  def set_temps(driver_temp, passenger_temp)
99
- api.post("/vehicles/#{id}/command/set_temps", body: {driver_temp: driver_temp, passenger_temp: passenger_temp})["response"]
108
+ command("set_temps", body: {driver_temp: driver_temp, passenger_temp: passenger_temp})["response"]
100
109
  end
101
110
 
102
111
  def auto_conditioning_start
103
- api.post("/vehicles/#{id}/command/auto_conditioning_start")["response"]
112
+ command("auto_conditioning_start")["response"]
104
113
  end
105
114
 
106
115
  def auto_conditioning_stop
107
- api.post("/vehicles/#{id}/command/auto_conditioning_stop")["response"]
116
+ command("auto_conditioning_stop")["response"]
108
117
  end
109
118
 
110
119
  def sun_roof_control(state)
111
- api.post("/vehicles/#{id}/command/sun_roof_control", body: {state: state})["response"]
120
+ command("sun_roof_control", body: {state: state})["response"]
112
121
  end
113
122
 
114
123
  def sun_roof_move(percent)
115
- api.post("/vehicles/#{id}/command/sun_roof_control", body: {state: "move", percent: percent})["response"]
124
+ command("sun_roof_control", body: {state: "move", percent: percent})["response"]
116
125
  end
117
126
 
118
- def remove_start_drive(password)
119
- api.post("/vehicles/#{id}/command/remote_start_drive", body: {password: password})["response"]
127
+ def remote_start_drive(password)
128
+ command("remote_start_drive", body: {password: password})["response"]
120
129
  end
121
130
 
122
131
  def open_trunk
123
- api.post("/vehicles/#{id}/command/trunk_open", body: {which_trunk: "rear"})
132
+ command("trunk_open", body: {which_trunk: "rear"})
124
133
  end
125
134
 
126
135
  def open_frunk
127
- api.post("/vehicles/#{id}/command/trunk_open", body: {which_trunk: "rear"})
128
- end
129
-
130
- # Streaming
131
-
132
- def stream(&reciever)
133
- EventMachine.run do
134
- request = EventMachine::HttpRequest.new(
135
- "https://streaming.vn.teslamotors.com/stream/#{self["vehicle_id"]}/" +
136
- "?values=speed,odometer,soc,elevation,est_heading,est_lat,est_lng,power")
137
-
138
- http = request.get(
139
- head: {
140
- "authorization" => [
141
- email,
142
- self["tokens"].first
143
- ]
144
- },
145
- inactivity_timeout: 15)
146
-
147
- http.stream do |chunk|
148
- attributes = chunk.split(",")
149
- reciever.call({
150
- time: DateTime.strptime((attributes[0].to_i/1000).to_s, "%s"),
151
- speed: attributes[1].to_f,
152
- odometer: attributes[2].to_f,
153
- soc: attributes[3].to_f,
154
- elevation: attributes[4].to_f,
155
- est_heading: attributes[5].to_f,
156
- est_lat: attributes[6].to_f,
157
- est_lng: attributes[7].to_f,
158
- power: attributes[8].to_f
159
- })
160
- end
161
-
162
- http.callback { EventMachine.stop }
163
- http.errback { EventMachine.stop }
164
- end
136
+ command("trunk_open", body: {which_trunk: "rear"})
137
+ end
138
+
139
+ private
140
+
141
+ def data_request(name)
142
+ api.get("/vehicles/#{id}/data_request/#{name}")
143
+ end
144
+
145
+ def command(name, body: nil)
146
+ api.post("/vehicles/#{id}/command/#{name}", body: body)
165
147
  end
166
148
  end
167
149
  end
@@ -1,3 +1,3 @@
1
1
  module TeslaApi
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -0,0 +1,85 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://owner-api.teslamotors.com/api/1/vehicles
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Authorization:
11
+ - Bearer <TESLA_API_TOKEN>
12
+ response:
13
+ status:
14
+ code: 200
15
+ message: OK
16
+ headers:
17
+ Server:
18
+ - nginx
19
+ Date:
20
+ - Sat, 23 Jan 2016 18:38:15 GMT
21
+ Content-Type:
22
+ - application/json; charset=utf-8
23
+ Content-Length:
24
+ - '526'
25
+ Connection:
26
+ - keep-alive
27
+ Status:
28
+ - 200 OK
29
+ X-Ua-Compatible:
30
+ - IE=Edge,chrome=1
31
+ Etag:
32
+ - '"1a341f7e69533f4f8201999a4732d298"'
33
+ Cache-Control:
34
+ - max-age=0, private, must-revalidate
35
+ X-Request-Id:
36
+ - 0cb5d6356855f3ffdc77ff604b5be599
37
+ X-Runtime:
38
+ - '0.242210'
39
+ body:
40
+ encoding: UTF-8
41
+ string: '{"response":[{"color":null,"display_name":"Nikola","id":49800504717279029,"option_codes":"MS01,RENA,TM00,DRLH,PF00,BT85,PBCW,RFPO,WT19,IBMB,IDPB,TR00,SU01,SC01,TP01,AU01,CH00,HP00,PA00,PS00,AD02,X020,X025,X001,X003,X007,X011,X013,COUS","vehicle_id":490215852,"vin":"5YJSA1CN5CFP01657","tokens":["b3c212faedd98d90","29435ecf655ed476"],"state":"online","id_s":"49800504717279029","remote_start_enabled":true,"calendar_enabled":true,"notifications_enabled":true,"backseat_token":null,"backseat_token_updated_at":null}],"count":1}'
42
+ http_version:
43
+ recorded_at: Sat, 23 Jan 2016 18:38:15 GMT
44
+ - request:
45
+ method: post
46
+ uri: https://owner-api.teslamotors.com/api/1/vehicles/49800504717279029/command/reset_valet_pin
47
+ body:
48
+ encoding: UTF-8
49
+ string: ''
50
+ headers:
51
+ Authorization:
52
+ - Bearer <TESLA_API_TOKEN>
53
+ response:
54
+ status:
55
+ code: 200
56
+ message: OK
57
+ headers:
58
+ Server:
59
+ - nginx
60
+ Date:
61
+ - Sat, 23 Jan 2016 18:38:16 GMT
62
+ Content-Type:
63
+ - application/json; charset=utf-8
64
+ Content-Length:
65
+ - '40'
66
+ Connection:
67
+ - keep-alive
68
+ Status:
69
+ - 200 OK
70
+ X-Ua-Compatible:
71
+ - IE=Edge,chrome=1
72
+ Etag:
73
+ - '"f67eec105dd6522783a1f1bacc52723a"'
74
+ Cache-Control:
75
+ - max-age=0, private, must-revalidate
76
+ X-Request-Id:
77
+ - 5c81d2c0bd2d440c60fa4aa6d532d10c
78
+ X-Runtime:
79
+ - '0.402684'
80
+ body:
81
+ encoding: UTF-8
82
+ string: '{"response":{"reason":"","result":true}}'
83
+ http_version:
84
+ recorded_at: Sat, 23 Jan 2016 18:38:16 GMT
85
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,85 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://owner-api.teslamotors.com/api/1/vehicles
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Authorization:
11
+ - Bearer <TESLA_API_TOKEN>
12
+ response:
13
+ status:
14
+ code: 200
15
+ message: OK
16
+ headers:
17
+ Server:
18
+ - nginx
19
+ Date:
20
+ - Sat, 23 Jan 2016 18:39:42 GMT
21
+ Content-Type:
22
+ - application/json; charset=utf-8
23
+ Content-Length:
24
+ - '526'
25
+ Connection:
26
+ - keep-alive
27
+ Status:
28
+ - 200 OK
29
+ X-Ua-Compatible:
30
+ - IE=Edge,chrome=1
31
+ Etag:
32
+ - '"1a341f7e69533f4f8201999a4732d298"'
33
+ Cache-Control:
34
+ - max-age=0, private, must-revalidate
35
+ X-Request-Id:
36
+ - 0c957b2d043ce894fcc3fd98adb511e4
37
+ X-Runtime:
38
+ - '0.099915'
39
+ body:
40
+ encoding: UTF-8
41
+ string: '{"response":[{"color":null,"display_name":"Nikola","id":49800504717279029,"option_codes":"MS01,RENA,TM00,DRLH,PF00,BT85,PBCW,RFPO,WT19,IBMB,IDPB,TR00,SU01,SC01,TP01,AU01,CH00,HP00,PA00,PS00,AD02,X020,X025,X001,X003,X007,X011,X013,COUS","vehicle_id":490215852,"vin":"5YJSA1CN5CFP01657","tokens":["b3c212faedd98d90","29435ecf655ed476"],"state":"online","id_s":"49800504717279029","remote_start_enabled":true,"calendar_enabled":true,"notifications_enabled":true,"backseat_token":null,"backseat_token_updated_at":null}],"count":1}'
42
+ http_version:
43
+ recorded_at: Sat, 23 Jan 2016 18:39:43 GMT
44
+ - request:
45
+ method: post
46
+ uri: https://owner-api.teslamotors.com/api/1/vehicles/49800504717279029/command/set_valet_mode
47
+ body:
48
+ encoding: UTF-8
49
+ string: on=true&password=1234
50
+ headers:
51
+ Authorization:
52
+ - Bearer <TESLA_API_TOKEN>
53
+ response:
54
+ status:
55
+ code: 200
56
+ message: OK
57
+ headers:
58
+ Server:
59
+ - nginx
60
+ Date:
61
+ - Sat, 23 Jan 2016 18:39:45 GMT
62
+ Content-Type:
63
+ - application/json; charset=utf-8
64
+ Content-Length:
65
+ - '40'
66
+ Connection:
67
+ - keep-alive
68
+ Status:
69
+ - 200 OK
70
+ X-Ua-Compatible:
71
+ - IE=Edge,chrome=1
72
+ Etag:
73
+ - '"f67eec105dd6522783a1f1bacc52723a"'
74
+ Cache-Control:
75
+ - max-age=0, private, must-revalidate
76
+ X-Request-Id:
77
+ - ad2ab96494fe4ae4f04b80d806a18956
78
+ X-Runtime:
79
+ - '0.689346'
80
+ body:
81
+ encoding: UTF-8
82
+ string: '{"response":{"reason":"","result":true}}'
83
+ http_version:
84
+ recorded_at: Sat, 23 Jan 2016 18:39:44 GMT
85
+ recorded_with: VCR 2.9.3
@@ -3,6 +3,10 @@ require 'spec_helper'
3
3
  RSpec.describe TeslaApi::Vehicle do
4
4
  let(:tesla_api) { TeslaApi::Client.new(ENV["TESLA_EMAIL"]) }
5
5
 
6
+ before do
7
+ tesla_api.token = ENV["TESLA_API_TOKEN"]
8
+ end
9
+
6
10
  subject(:vehicle) { tesla_api.vehicles.first }
7
11
 
8
12
  describe "#[]", vcr: { cassette_name: "vehicle" } do
@@ -94,6 +98,22 @@ RSpec.describe TeslaApi::Vehicle do
94
98
  end
95
99
  end
96
100
 
101
+ describe "#set_valet_mode", vcr: {cassette_name: "vehicle-set_valet_mode"} do
102
+ it "enables valet mode on the car" do
103
+ expect(vehicle.set_valet_mode(true, 1234)["result"]).to eq(true)
104
+ end
105
+
106
+ it "enables valet mode with a previous PIN" do
107
+ expect(vehicle.set_valet_mode(true)["result"]).to eq(true)
108
+ end
109
+ end
110
+
111
+ describe "#reset_valet_pin", vcr: {cassette_name: "vehicle-reset_valet_pin"} do
112
+ it "resets the valet mode PIN" do
113
+ expect(vehicle.reset_valet_pin["result"]).to eq(true)
114
+ end
115
+ end
116
+
97
117
  describe "#charge_port_door_open", vcr: {cassette_name: "vehicle-charge_port_door_open"} do
98
118
  it "opens the charge port door" do
99
119
  expect(vehicle.charge_port_door_open["result"]).to eq(true)
@@ -231,9 +251,9 @@ RSpec.describe TeslaApi::Vehicle do
231
251
  end
232
252
  end
233
253
 
234
- describe "#remove_start_drive", vcr: {cassette_name: "vehicle-remove_start_drive"} do
254
+ describe "#remote_start_drive", vcr: {cassette_name: "vehicle-remote_start_drive"} do
235
255
  it "starts the vehicle's keyless driving mode" do
236
- expect(vehicle.remove_start_drive("elon4eva")["result"]).to eq(true)
256
+ expect(vehicle.remote_start_drive("elon4eva")["result"]).to eq(true)
237
257
  end
238
258
  end
239
259