tesla_api 1.1.0 → 1.2.0
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/.travis.yml +3 -1
- data/README.md +2 -1
- data/Rakefile +5 -0
- data/apiary.apib +401 -123
- data/circle.yml +10 -0
- data/lib/tesla_api.rb +1 -0
- data/lib/tesla_api/stream.rb +49 -0
- data/lib/tesla_api/vehicle.rb +43 -61
- data/lib/tesla_api/version.rb +1 -1
- data/spec/cassettes/{vehicle-remove_start_drive.yml → vehicle-remote_start_drive.yml} +0 -0
- data/spec/cassettes/vehicle-reset_valet_pin.yml +85 -0
- data/spec/cassettes/vehicle-set_valet_mode.yml +85 -0
- data/spec/lib/tesla_api/vehicle_spec.rb +22 -2
- data/spec/spec_helper.rb +1 -0
- metadata +11 -5
data/circle.yml
ADDED
data/lib/tesla_api.rb
CHANGED
@@ -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
|
data/lib/tesla_api/vehicle.rb
CHANGED
@@ -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
|
-
|
34
|
+
data_request("gui_settings")["response"]
|
34
35
|
end
|
35
36
|
|
36
37
|
def charge_state
|
37
|
-
|
38
|
+
data_request("charge_state")["response"]
|
38
39
|
end
|
39
40
|
|
40
41
|
def climate_state
|
41
|
-
|
42
|
+
data_request("climate_state")["response"]
|
42
43
|
end
|
43
44
|
|
44
45
|
def drive_state
|
45
|
-
|
46
|
+
data_request("drive_state")["response"]
|
46
47
|
end
|
47
48
|
|
48
49
|
def vehicle_state
|
49
|
-
|
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
|
-
|
68
|
+
command("charge_port_door_open")["response"]
|
60
69
|
end
|
61
70
|
|
62
71
|
def charge_standard
|
63
|
-
|
72
|
+
command("charge_standard")["response"]
|
64
73
|
end
|
65
74
|
|
66
75
|
def charge_max_range
|
67
|
-
|
76
|
+
command("charge_max_range")["response"]
|
68
77
|
end
|
69
78
|
|
70
79
|
def set_charge_limit(percent)
|
71
|
-
|
80
|
+
command("set_charge_limit", body: {percent: percent})["response"]
|
72
81
|
end
|
73
82
|
|
74
83
|
def charge_start
|
75
|
-
|
84
|
+
command("charge_start")["response"]
|
76
85
|
end
|
77
86
|
|
78
87
|
def charge_stop
|
79
|
-
|
88
|
+
command("charge_stop")["response"]
|
80
89
|
end
|
81
90
|
|
82
91
|
def flash_lights
|
83
|
-
|
92
|
+
command("flash_lights")["response"]
|
84
93
|
end
|
85
94
|
|
86
95
|
def honk_horn
|
87
|
-
|
96
|
+
command("honk_horn")["response"]
|
88
97
|
end
|
89
98
|
|
90
99
|
def door_unlock
|
91
|
-
|
100
|
+
command("door_unlock")["response"]
|
92
101
|
end
|
93
102
|
|
94
103
|
def door_lock
|
95
|
-
|
104
|
+
command("door_lock")["response"]
|
96
105
|
end
|
97
106
|
|
98
107
|
def set_temps(driver_temp, passenger_temp)
|
99
|
-
|
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
|
-
|
112
|
+
command("auto_conditioning_start")["response"]
|
104
113
|
end
|
105
114
|
|
106
115
|
def auto_conditioning_stop
|
107
|
-
|
116
|
+
command("auto_conditioning_stop")["response"]
|
108
117
|
end
|
109
118
|
|
110
119
|
def sun_roof_control(state)
|
111
|
-
|
120
|
+
command("sun_roof_control", body: {state: state})["response"]
|
112
121
|
end
|
113
122
|
|
114
123
|
def sun_roof_move(percent)
|
115
|
-
|
124
|
+
command("sun_roof_control", body: {state: "move", percent: percent})["response"]
|
116
125
|
end
|
117
126
|
|
118
|
-
def
|
119
|
-
|
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
|
-
|
132
|
+
command("trunk_open", body: {which_trunk: "rear"})
|
124
133
|
end
|
125
134
|
|
126
135
|
def open_frunk
|
127
|
-
|
128
|
-
end
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
def
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
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
|
data/lib/tesla_api/version.rb
CHANGED
File without changes
|
@@ -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 "#
|
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.
|
256
|
+
expect(vehicle.remote_start_drive("elon4eva")["result"]).to eq(true)
|
237
257
|
end
|
238
258
|
end
|
239
259
|
|