tesla_api 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +46 -0
- data/Rakefile +2 -0
- data/apiary.apib +433 -0
- data/lib/tesla_api.rb +172 -0
- data/lib/tesla_api/version.rb +3 -0
- data/logs/login.GET.log +44 -0
- data/logs/login.POST.log +29 -0
- data/logs/root.GET.log +50 -0
- data/tesla_api.gemspec +22 -0
- metadata +85 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 20e84c22651e2f1856d02fc5288277ec8ec512cc
|
4
|
+
data.tar.gz: b4a0471a5d0ca51c0974071b0ef526d7603a531c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6b0fc114f6cd257b6d52dbcc6aa6d3902a4241fd536cc39c75554ba96b5a88d56dc69e6076d15479c30e4133a1ca24573224daa054222dc04c7843a0a2f1ae17
|
7
|
+
data.tar.gz: f6f63dc83fd699d418c3f2702cf612f772c16a6a4980765a57d254eded2ded59a6cd065b3ed70762dc589464844720a2859d48b861bfa9b1c6b6ffd1da7e4902
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Tim Dorr
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# Tesla JSON API
|
2
|
+
|
3
|
+
[View Documentation](http://docs.timdorr.apiary.io/)
|
4
|
+
|
5
|
+
This is unofficial documentation of the Tesla Model S JSON API used by the iOS and Android apps.
|
6
|
+
The API provides functionality to monitor and control the Model S (and future Tesla vehicles) remotely.
|
7
|
+
The project provides both a documention of the API and a Ruby library to for accessing it.
|
8
|
+
|
9
|
+
## Ruby Library
|
10
|
+
|
11
|
+
This gem provides a basic wrapper around the API to easily query and command the car remotely.
|
12
|
+
It also provides access to the streaming API and a means to process data coming from it.
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
```ruby
|
18
|
+
gem 'tesla_api'
|
19
|
+
```
|
20
|
+
|
21
|
+
Or install it yourself:
|
22
|
+
```sh
|
23
|
+
gem install tesla_api
|
24
|
+
```
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
Here's a quick example:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
require 'tesla_api'
|
32
|
+
|
33
|
+
tesla_api = TeslaApi.new(email, password, client_id, client_secret)
|
34
|
+
model_s = tesla_api.vehicles.first
|
35
|
+
|
36
|
+
model_s.wake_up
|
37
|
+
model_s.auto_conditioning_start unless model_s.climate_state["is_auto_conditioning_on"]
|
38
|
+
|
39
|
+
model_s.set_charge_limit(90)
|
40
|
+
model_s.charge_start
|
41
|
+
|
42
|
+
charge_state = model_s.charge_state
|
43
|
+
puts "Your Model S is #{charge_state["charging_state"]} " +
|
44
|
+
"with a SOC of #{charge_state["battery_level"]}% " +
|
45
|
+
"and an estimate range of #{charge_state["est_battery_range"]} miles"
|
46
|
+
```
|
data/Rakefile
ADDED
data/apiary.apib
ADDED
@@ -0,0 +1,433 @@
|
|
1
|
+
FORMAT: 1A
|
2
|
+
HOST: https://portal.vn.teslamotors.com
|
3
|
+
|
4
|
+
# Tesla Model S JSON API
|
5
|
+
This is unofficial documentation of the Tesla Model S JSON API used by the iOS and Android apps. It features functionality to monitor and control the Model S remotely.
|
6
|
+
|
7
|
+
# Group Authentication Flow
|
8
|
+
These endpoints handle login and session management
|
9
|
+
|
10
|
+
## GET /login
|
11
|
+
Returns the login form. Sets a `_s_portal_session` cookie for the session
|
12
|
+
+ Response 200
|
13
|
+
|
14
|
+
+ Headers
|
15
|
+
|
16
|
+
Set-Cookie: _s_portal_session={cookie}; path=/; secure; HttpOnly
|
17
|
+
|
18
|
+
+ Body
|
19
|
+
|
20
|
+
Login Screen HTML
|
21
|
+
|
22
|
+
## POST /login
|
23
|
+
Performs the login. Takes in an plain text email and password, matching the owner's login from [http://teslamotors.com/mytesla](http://teslamotors.com/mytesla).
|
24
|
+
|
25
|
+
Sets a `user_credentials` cookie that expires in 3 months, which is passed along with all future requests to authenticate the user.
|
26
|
+
|
27
|
+
Redirects back to a dummy welcome page. This page is ignored by the smartphone app and can be ignored by your API client.
|
28
|
+
+ Request (application/x-www-form-urlencoded)
|
29
|
+
|
30
|
+
+ Body
|
31
|
+
|
32
|
+
user_session%5Bemail%5D=string&user_session%5Bpassword%5D=string
|
33
|
+
|
34
|
+
+ Response 302
|
35
|
+
|
36
|
+
+ Headers
|
37
|
+
|
38
|
+
Set-Cookie: _s_portal_session={cookie}; path=/; secure; HttpOnly
|
39
|
+
Set-Cookie: user_credentials=x; path=/; expires=Fri, 03-May-2013 03:01:54 GMT; secure; HttpOnly
|
40
|
+
Location: https://portal.vn.teslamotors.com/
|
41
|
+
|
42
|
+
|
43
|
+
+ Body
|
44
|
+
|
45
|
+
Dummy Welcome Page
|
46
|
+
|
47
|
+
|
48
|
+
# Group Vehicle List
|
49
|
+
A logged in user can have multiple vehicles under their account. This resource is primarily responsible for listing the vehicles and the basic details about them.
|
50
|
+
|
51
|
+
Must have a `_s_portal_session` and `user_credentials` cookie set for all requests.
|
52
|
+
|
53
|
+
## GET /vehicles
|
54
|
+
Retrieve a list of your owned vehicles (includes vehicles not yet shipped!)
|
55
|
+
+ Response 200 (application/json)
|
56
|
+
|
57
|
+
+ Body
|
58
|
+
|
59
|
+
[{
|
60
|
+
"color": null,
|
61
|
+
"display_name": null,
|
62
|
+
"id": 321,
|
63
|
+
"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",
|
64
|
+
"user_id": 123,
|
65
|
+
"vehicle_id": 1234567890,
|
66
|
+
"vin": "5YJSA1CN5CFP01657",
|
67
|
+
"tokens": ["x", "x"],
|
68
|
+
"state": "online"
|
69
|
+
}]
|
70
|
+
|
71
|
+
|
72
|
+
# Group Vehicle Status
|
73
|
+
These resources are read-only and determine the state of the vehicle's various sub-systems.
|
74
|
+
|
75
|
+
Must have a `_s_portal_session` and `user_credentials` cookie set for all requests.
|
76
|
+
|
77
|
+
## GET /vehicles/{id}/mobile_enabled
|
78
|
+
Determines if mobile access to the vehicle is enabled.
|
79
|
+
+ Parameters
|
80
|
+
|
81
|
+
+ id (number) ... The ID number of the car
|
82
|
+
|
83
|
+
+ Response 200 (application/json)
|
84
|
+
|
85
|
+
+ Body
|
86
|
+
|
87
|
+
{
|
88
|
+
"reason":"",
|
89
|
+
"result":true
|
90
|
+
}
|
91
|
+
|
92
|
+
## GET /vehicles/{id}/command/charge_state
|
93
|
+
Returns the state of charge in the battery.
|
94
|
+
+ Parameters
|
95
|
+
|
96
|
+
+ id (number) ... The ID number of the car
|
97
|
+
|
98
|
+
+ Response 200 (application/json)
|
99
|
+
|
100
|
+
+ Body
|
101
|
+
|
102
|
+
{
|
103
|
+
"charging_state": "Complete", // "Charging", ??
|
104
|
+
"charge_to_max_range": false, // current std/max-range setting
|
105
|
+
"max_range_charge_counter": 0,
|
106
|
+
"fast_charger_present": false, // connected to Supercharger?
|
107
|
+
"battery_range": 239.02, // rated miles
|
108
|
+
"est_battery_range": 155.79, // range estimated from recent driving
|
109
|
+
"ideal_battery_range": 275.09, // ideal miles
|
110
|
+
"battery_level": 91, // integer charge percentage
|
111
|
+
"battery_current": -0.6, // current flowing into battery
|
112
|
+
"charge_starting_range": null,
|
113
|
+
"charge_starting_soc": null,
|
114
|
+
"charger_voltage": 0, // only has value while charging
|
115
|
+
"charger_pilot_current": 40, // max current allowed by charger & adapter
|
116
|
+
"charger_actual_current": 0, // current actually being drawn
|
117
|
+
"charger_power": 0, // kW (rounded down) of charger
|
118
|
+
"time_to_full_charge": null, // valid only while charging
|
119
|
+
"charge_rate": -1.0, // float mi/hr charging or -1 if not charging
|
120
|
+
"charge_port_door_open": true
|
121
|
+
}
|
122
|
+
|
123
|
+
## GET /vehicles/{id}/command/climate_state
|
124
|
+
Returns the current temperature and climate control state.
|
125
|
+
+ Parameters
|
126
|
+
|
127
|
+
+ id (number) ... The ID number of the car
|
128
|
+
|
129
|
+
+ Response 200 (application/json)
|
130
|
+
|
131
|
+
+ Body
|
132
|
+
|
133
|
+
{
|
134
|
+
"inside_temp": 17.0, // degC inside car
|
135
|
+
"outside_temp": 9.5, // degC outside car or null
|
136
|
+
"driver_temp_setting": 22.6, // degC of driver temperature setpoint
|
137
|
+
"passenger_temp_setting": 22.6, // degC of passenger temperature setpoint
|
138
|
+
"is_auto_conditioning_on": false, // apparently even if on
|
139
|
+
"is_front_defroster_on": null, // null or boolean as integer?
|
140
|
+
"is_rear_defroster_on": false,
|
141
|
+
"fan_status": 0 // fan speed 0-6 or null
|
142
|
+
}
|
143
|
+
|
144
|
+
## GET /vehicles/{id}/command/drive_state
|
145
|
+
Returns the driving and position state of the vehicle.
|
146
|
+
+ Parameters
|
147
|
+
|
148
|
+
+ id (number) ... The ID number of the car
|
149
|
+
|
150
|
+
+ Response 200 (application/json)
|
151
|
+
|
152
|
+
+ Body
|
153
|
+
|
154
|
+
{
|
155
|
+
"shift_state": null, //
|
156
|
+
"speed": null, //
|
157
|
+
"latitude": 33.794839, // degrees N of equator
|
158
|
+
"longitude": -84.401593, // degrees W of the prime meridian
|
159
|
+
"heading": 4, // integer compass heading, 0-359
|
160
|
+
"gps_as_of": 1359863204 // Unix timestamp of GPS fix
|
161
|
+
}
|
162
|
+
|
163
|
+
## GET /vehicles/{id}/command/gui_settings
|
164
|
+
Returns various information about the GUI settings of the car, such as unit format and range display.
|
165
|
+
+ Parameters
|
166
|
+
|
167
|
+
+ id (number) ... The ID number of the car
|
168
|
+
|
169
|
+
+ Response 200 (application/json)
|
170
|
+
|
171
|
+
+ Body
|
172
|
+
|
173
|
+
{
|
174
|
+
"gui_distance_units": "mi/hr",
|
175
|
+
"gui_temperature_units": "F",
|
176
|
+
"gui_charge_rate_units": "mi/hr",
|
177
|
+
"gui_24_hour_time": false,
|
178
|
+
"gui_range_display": "Rated"
|
179
|
+
}
|
180
|
+
|
181
|
+
## GET /vehicles/{id}/command/vehicle_state
|
182
|
+
Returns the vehicle's physical state, such as which doors are open.
|
183
|
+
+ Parameters
|
184
|
+
|
185
|
+
+ id (number) ... The ID number of the car
|
186
|
+
|
187
|
+
+ Response 200 (application/json)
|
188
|
+
|
189
|
+
+ Body
|
190
|
+
|
191
|
+
{
|
192
|
+
"df": false, // driver's side front door open
|
193
|
+
"dr": false, // driver's side rear door open
|
194
|
+
"pf": false, // passenger's side front door open
|
195
|
+
"pr": false, // passenger's side rear door open
|
196
|
+
"ft": false, // front trunk is open
|
197
|
+
"rt": false, // rear trunk is open
|
198
|
+
"car_verson": "1.19.42", // car firmware version
|
199
|
+
"locked": true, // car is locked
|
200
|
+
"sun_roof_installed": false, // panoramic roof is installed
|
201
|
+
"sun_roof_state": "unknown",
|
202
|
+
"sun_roof_percent_open": 0, // null if not installed
|
203
|
+
"dark_rims": false, // gray rims installed
|
204
|
+
"wheel_type": "Base19", // wheel type installed
|
205
|
+
"has_spoiler": false, // spoiler is installed
|
206
|
+
"roof_color": "Colored", // "None" for panoramic roof
|
207
|
+
"perf_config": "Base"
|
208
|
+
}
|
209
|
+
|
210
|
+
# Group Vehicle Commands
|
211
|
+
These commands alter the vehicles state, and return result (true/false) to indicate success, and if failure reason contains the cause of failure.
|
212
|
+
|
213
|
+
Must have a `_s_portal_session` and `user_credentials` cookie set for all requests.
|
214
|
+
|
215
|
+
## GET /vehicles/{id}/command/charge_port_door_open
|
216
|
+
Open the charge port.
|
217
|
+
+ Parameters
|
218
|
+
|
219
|
+
+ id (number) ... The ID number of the car
|
220
|
+
|
221
|
+
+ Response 200 (application/json)
|
222
|
+
|
223
|
+
+ Body
|
224
|
+
|
225
|
+
{
|
226
|
+
"result": false,
|
227
|
+
"reason": "failure reason"
|
228
|
+
}
|
229
|
+
|
230
|
+
## GET /vehicles/{id}/command/charge_standard
|
231
|
+
Set the charge mode to standard (~90% under the new percentage system introduced in 4.5).
|
232
|
+
+ Parameters
|
233
|
+
|
234
|
+
+ id (number) ... The ID number of the car
|
235
|
+
|
236
|
+
+ Response 200 (application/json)
|
237
|
+
|
238
|
+
+ Body
|
239
|
+
|
240
|
+
{
|
241
|
+
"result": false,
|
242
|
+
"reason": "failure reason"
|
243
|
+
}
|
244
|
+
|
245
|
+
## GET /vehicles/{id}/command/charge_max_range
|
246
|
+
Set the charge mode to max range (100% under the new percentage system introduced in 4.5).
|
247
|
+
+ Parameters
|
248
|
+
|
249
|
+
+ id (number) ... The ID number of the car
|
250
|
+
|
251
|
+
+ Response 200 (application/json)
|
252
|
+
|
253
|
+
+ Body
|
254
|
+
|
255
|
+
{
|
256
|
+
"result": false,
|
257
|
+
"reason": "failure reason"
|
258
|
+
}
|
259
|
+
|
260
|
+
## GET /vehicles/{id}/command/set_charge_limit?percent={limit_value}
|
261
|
+
Set the charge limit to a custom percentage.
|
262
|
+
+ Parameters
|
263
|
+
|
264
|
+
+ id (number) ... The ID number of the car
|
265
|
+
+ limit_value (number) ... The percentage value
|
266
|
+
|
267
|
+
+ Response 200 (application/json)
|
268
|
+
|
269
|
+
+ Body
|
270
|
+
|
271
|
+
{
|
272
|
+
"result": false,
|
273
|
+
"reason": "failure reason"
|
274
|
+
}
|
275
|
+
|
276
|
+
## GET /vehicles/{id}/command/charge_start
|
277
|
+
Start charging.
|
278
|
+
+ Parameters
|
279
|
+
|
280
|
+
+ id (number) ... The ID number of the car
|
281
|
+
|
282
|
+
+ Response 200 (application/json)
|
283
|
+
|
284
|
+
+ Body
|
285
|
+
|
286
|
+
{
|
287
|
+
"result": false,
|
288
|
+
"reason": "failure reason" // "already started" if a charge is in progress
|
289
|
+
}
|
290
|
+
|
291
|
+
## GET /vehicles/{id}/command/charge_stop
|
292
|
+
Stop charging.
|
293
|
+
+ Parameters
|
294
|
+
|
295
|
+
+ id (number) ... The ID number of the car
|
296
|
+
|
297
|
+
+ Response 200 (application/json)
|
298
|
+
|
299
|
+
+ Body
|
300
|
+
|
301
|
+
{
|
302
|
+
"result": false,
|
303
|
+
"reason": "failure reason" // "not_charging" if a charge was not in progress
|
304
|
+
}
|
305
|
+
|
306
|
+
## GET /vehicles/{id}/command/flash_lights
|
307
|
+
Flash the lights once.
|
308
|
+
+ Parameters
|
309
|
+
|
310
|
+
+ id (number) ... The ID number of the car
|
311
|
+
|
312
|
+
+ Response 200 (application/json)
|
313
|
+
|
314
|
+
+ Body
|
315
|
+
|
316
|
+
{
|
317
|
+
"result": false,
|
318
|
+
"reason": "failure reason"
|
319
|
+
}
|
320
|
+
|
321
|
+
## GET /vehicles/{id}/command/honk_horn
|
322
|
+
Honk the horn once.
|
323
|
+
+ Parameters
|
324
|
+
|
325
|
+
+ id (number) ... The ID number of the car
|
326
|
+
|
327
|
+
+ Response 200 (application/json)
|
328
|
+
|
329
|
+
+ Body
|
330
|
+
|
331
|
+
{
|
332
|
+
"result": false,
|
333
|
+
"reason": "failure reason"
|
334
|
+
}
|
335
|
+
|
336
|
+
## GET /vehicles/{id}/command/door_unlock
|
337
|
+
Unlock the car's doors.
|
338
|
+
+ Parameters
|
339
|
+
|
340
|
+
+ id (number) ... The ID number of the car
|
341
|
+
|
342
|
+
+ Response 200 (application/json)
|
343
|
+
|
344
|
+
+ Body
|
345
|
+
|
346
|
+
{
|
347
|
+
"result": false,
|
348
|
+
"reason": "failure reason"
|
349
|
+
}
|
350
|
+
|
351
|
+
## GET /vehicles/{id}/command/door_lock
|
352
|
+
Lock the car's doors.
|
353
|
+
+ Parameters
|
354
|
+
|
355
|
+
+ id (number) ... The ID number of the car
|
356
|
+
|
357
|
+
+ Response 200 (application/json)
|
358
|
+
|
359
|
+
+ Body
|
360
|
+
|
361
|
+
{
|
362
|
+
"result": false,
|
363
|
+
"reason": "failure reason"
|
364
|
+
}
|
365
|
+
|
366
|
+
## GET /vehicles/{id}/command/set_temps?driver_temp={driver_degC}&passenger_temp={pass_degC}
|
367
|
+
Set the temperature target for the HVAC system.
|
368
|
+
+ Parameters
|
369
|
+
|
370
|
+
+ id (number) ... The ID number of the car
|
371
|
+
+ driver_degC (number) ... The desired temperature on the driver's side in celcius.
|
372
|
+
+ pass_degC (number) ... The desired temperature on the passenger's side in celcius.
|
373
|
+
|
374
|
+
+ Response 200 (application/json)
|
375
|
+
|
376
|
+
+ Body
|
377
|
+
|
378
|
+
{
|
379
|
+
"result": false,
|
380
|
+
"reason": "failure reason"
|
381
|
+
}
|
382
|
+
|
383
|
+
## GET /vehicles/{id}/command/auto_conditioning_start
|
384
|
+
Start the HVAC system. Will cool or heat automatically, depending on set temperature.
|
385
|
+
+ Parameters
|
386
|
+
|
387
|
+
+ id (number) ... The ID number of the car
|
388
|
+
|
389
|
+
+ Response 200 (application/json)
|
390
|
+
|
391
|
+
+ Body
|
392
|
+
|
393
|
+
{
|
394
|
+
"result": false,
|
395
|
+
"reason": "failure reason"
|
396
|
+
}
|
397
|
+
|
398
|
+
## GET /vehicles/{id}/command/auto_conditioning_stop
|
399
|
+
Stop the HVAC system.
|
400
|
+
+ Parameters
|
401
|
+
|
402
|
+
+ id (number) ... The ID number of the car
|
403
|
+
|
404
|
+
+ Response 200 (application/json)
|
405
|
+
|
406
|
+
+ Body
|
407
|
+
|
408
|
+
{
|
409
|
+
"result": false,
|
410
|
+
"reason": "failure reason"
|
411
|
+
}
|
412
|
+
|
413
|
+
## GET /vehicles/{id}/command/sun_roof_control?state={state}
|
414
|
+
Controls the car's panoramic roof, if installed.
|
415
|
+
+ Parameters
|
416
|
+
|
417
|
+
+ id (number) ... The ID number of the car
|
418
|
+
+ state (string)
|
419
|
+
The desired state of the panoramic roof. The approximate percent open values for each state are `open` = 100%, `close` = 0%, `comfort` = 80%, and `vent` = ~15%
|
420
|
+
+ Values
|
421
|
+
+ `open`
|
422
|
+
+ `close`
|
423
|
+
+ `comfort`
|
424
|
+
+ `vent`
|
425
|
+
|
426
|
+
+ Response 200 (application/json)
|
427
|
+
|
428
|
+
+ Body
|
429
|
+
|
430
|
+
{
|
431
|
+
"result": false,
|
432
|
+
"reason": "failure reason"
|
433
|
+
}
|
data/lib/tesla_api.rb
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
require "tesla_api/version"
|
2
|
+
|
3
|
+
class TeslaApi
|
4
|
+
include HTTParty
|
5
|
+
base_uri "https://owner-api.teslamotors.com/api/1"
|
6
|
+
format :json
|
7
|
+
|
8
|
+
attr_reader :email
|
9
|
+
|
10
|
+
def initialize(email, password, client_id, client_secret)
|
11
|
+
@email = email
|
12
|
+
response = self.class.post(
|
13
|
+
"https://owner-api.teslamotors.com/oauth/token",
|
14
|
+
body: {
|
15
|
+
"grant_type" => "password",
|
16
|
+
"client_id" => client_id,
|
17
|
+
"client_secret" => client_secret,
|
18
|
+
"email" => email,
|
19
|
+
"password" => password
|
20
|
+
}
|
21
|
+
)
|
22
|
+
self.class.headers "Authorization" => "Bearer #{response["access_token"]}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def vehicles
|
26
|
+
self.class.get("/vehicles")["response"].map { |v| Vehicle.new(self.class, email, v["id"], v) }
|
27
|
+
end
|
28
|
+
|
29
|
+
class Vehicle
|
30
|
+
attr_reader :api, :email, :id, :vehicle
|
31
|
+
|
32
|
+
def initialize(api, email, id, vehicle)
|
33
|
+
@api = api
|
34
|
+
@email = email
|
35
|
+
@id = id
|
36
|
+
@vehicle = vehicle
|
37
|
+
end
|
38
|
+
|
39
|
+
def [](key)
|
40
|
+
vehicle[key]
|
41
|
+
end
|
42
|
+
|
43
|
+
# State
|
44
|
+
|
45
|
+
def mobile_enabled
|
46
|
+
api.get("/vehicles/#{id}/mobile_enabled")["response"]
|
47
|
+
end
|
48
|
+
|
49
|
+
def charge_state
|
50
|
+
api.get("/vehicles/#{id}/data_request/charge_state")["response"]
|
51
|
+
end
|
52
|
+
|
53
|
+
def climate_state
|
54
|
+
api.get("/vehicles/#{id}/data_request/climate_state")["response"]
|
55
|
+
end
|
56
|
+
|
57
|
+
def drive_state
|
58
|
+
api.get("/vehicles/#{id}/data_request/drive_state")["response"]
|
59
|
+
end
|
60
|
+
|
61
|
+
def gui_settings
|
62
|
+
api.get("/vehicles/#{id}/data_request/gui_settings")["response"]
|
63
|
+
end
|
64
|
+
|
65
|
+
def vehicle_state
|
66
|
+
api.get("/vehicles/#{id}/data_request/vehicle_state")["response"]
|
67
|
+
end
|
68
|
+
|
69
|
+
# Commands
|
70
|
+
|
71
|
+
def wake_up
|
72
|
+
api.post("/vehicles/#{id}/wake_up")
|
73
|
+
end
|
74
|
+
|
75
|
+
def charge_port_door_open
|
76
|
+
api.post("/vehicles/#{id}/command/charge_port_door_open")
|
77
|
+
end
|
78
|
+
|
79
|
+
def charge_standard
|
80
|
+
api.post("/vehicles/#{id}/command/charge_standard")
|
81
|
+
end
|
82
|
+
|
83
|
+
def charge_max_range
|
84
|
+
api.post("/vehicles/#{id}/command/charge_max_range")
|
85
|
+
end
|
86
|
+
|
87
|
+
def set_charge_limit(percent)
|
88
|
+
api.post("/vehicles/#{id}/command/set_charge_limit", query: {state: "set", percent: percent})
|
89
|
+
end
|
90
|
+
|
91
|
+
def charge_start
|
92
|
+
api.post("/vehicles/#{id}/command/charge_start")
|
93
|
+
end
|
94
|
+
|
95
|
+
def charge_stop
|
96
|
+
api.post("/vehicles/#{id}/command/charge_stop")
|
97
|
+
end
|
98
|
+
|
99
|
+
def flash_lights
|
100
|
+
api.post("/vehicles/#{id}/command/flash_lights")
|
101
|
+
end
|
102
|
+
|
103
|
+
def honk_horn
|
104
|
+
api.post("/vehicles/#{id}/command/honk_horn")
|
105
|
+
end
|
106
|
+
|
107
|
+
def door_unlock
|
108
|
+
api.post("/vehicles/#{id}/command/door_unlock")
|
109
|
+
end
|
110
|
+
|
111
|
+
def door_lock
|
112
|
+
api.post("/vehicles/#{id}/command/door_lock")
|
113
|
+
end
|
114
|
+
|
115
|
+
def set_temps(driver_temp, passenger_temp)
|
116
|
+
api.post("/vehicles/#{id}/command/set_temps", query: {driver_temp: driver_temp, passenger_temp: passenger_temp})
|
117
|
+
end
|
118
|
+
|
119
|
+
def auto_conditioning_start
|
120
|
+
api.post("/vehicles/#{id}/command/auto_conditioning_start")
|
121
|
+
end
|
122
|
+
|
123
|
+
def auto_conditioning_stop
|
124
|
+
api.post("/vehicles/#{id}/command/auto_conditioning_stop")
|
125
|
+
end
|
126
|
+
|
127
|
+
def sun_roof_control(state)
|
128
|
+
api.post("/vehicles/#{id}/command/sun_roof_control", query: {state: state})
|
129
|
+
end
|
130
|
+
|
131
|
+
def sun_roof_move(percent)
|
132
|
+
api.post("/vehicles/#{id}/command/sun_roof_control", query: {state: "move", percent: percent})
|
133
|
+
end
|
134
|
+
|
135
|
+
# Streaming
|
136
|
+
|
137
|
+
def stream(&reciever)
|
138
|
+
EventMachine.run do
|
139
|
+
request = EventMachine::HttpRequest.new(
|
140
|
+
"https://streaming.vn.teslamotors.com/stream/#{self["vehicle_id"]}/" +
|
141
|
+
"?values=speed,odometer,soc,elevation,est_heading,est_lat,est_lng,power")
|
142
|
+
|
143
|
+
http = request.get(
|
144
|
+
head: {
|
145
|
+
"authorization" => [
|
146
|
+
email,
|
147
|
+
self["tokens"].first
|
148
|
+
]
|
149
|
+
},
|
150
|
+
inactivity_timeout: 15)
|
151
|
+
|
152
|
+
http.stream do |chunk|
|
153
|
+
attributes = chunk.split(",")
|
154
|
+
reciever.call({
|
155
|
+
time: DateTime.strptime((attributes[0].to_i/1000).to_s, "%s"),
|
156
|
+
speed: attributes[1].to_f,
|
157
|
+
odometer: attributes[2].to_f,
|
158
|
+
soc: attributes[3].to_f,
|
159
|
+
elevation: attributes[4].to_f,
|
160
|
+
est_heading: attributes[5].to_f,
|
161
|
+
est_lat: attributes[6].to_f,
|
162
|
+
est_lng: attributes[7].to_f,
|
163
|
+
power: attributes[8].to_f
|
164
|
+
})
|
165
|
+
end
|
166
|
+
|
167
|
+
http.callback { EventMachine.stop }
|
168
|
+
http.errback { EventMachine.stop }
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
data/logs/login.GET.log
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
GET /login HTTP/1.1
|
2
|
+
Host: portal.vn.teslamotors.com
|
3
|
+
Connection: Keep-Alive
|
4
|
+
Accept-Encoding: gzip deflate
|
5
|
+
|
6
|
+
|
7
|
+
HTTP/1.1 200 OK
|
8
|
+
Content-Type: text/html; charset=utf-8
|
9
|
+
X-UA-Compatible: IE=Edge,chrome=1
|
10
|
+
ETag: "68693d0c80ee503a112cafc18a273525"
|
11
|
+
Cache-Control: max-age=0, private, must-revalidate
|
12
|
+
Set-Cookie: _s_portal_session=x; path=/; secure; HttpOnly
|
13
|
+
X-Request-Id: 80985fcba166592ed09cce956fd395d6
|
14
|
+
X-Runtime: 0.006161
|
15
|
+
Date: Sun, 03 Feb 2013 03:01:52 GMT
|
16
|
+
X-Rack-Cache: miss
|
17
|
+
Server: thin 1.4.1 codename Chromeo
|
18
|
+
Transfer-Encoding: chunked
|
19
|
+
|
20
|
+
205
|
21
|
+
<!DOCTYPE html>
|
22
|
+
<html>
|
23
|
+
<head>
|
24
|
+
<title>Portal</title>
|
25
|
+
<script src="/assets/application-a5592d882a1bdc39d6b26c108a8ffbe0.js" type="text/javascript"></script>
|
26
|
+
<meta content="authenticity_token" name="csrf-param" />
|
27
|
+
<meta content="kOHBAsesbSMy0AJKsPC7f9aI/QgE9g4SxbUcfZAhvdg=" name="csrf-token" />
|
28
|
+
<meta name="csrf-token" content="kOHBAsesbSMy0AJKsPC7f9aI/QgE9g4SxbUcfZAhvdg=">
|
29
|
+
|
30
|
+
<link href='/images/favicon.ico' rel='shortcut icon'>
|
31
|
+
|
32
|
+
</head>
|
33
|
+
<body>
|
34
|
+
<header>
|
35
|
+
<li><a href="/login">Login</a></li>
|
36
|
+
</header>
|
37
|
+
|
38
|
+
|
39
|
+
</body>
|
40
|
+
</html>
|
41
|
+
|
42
|
+
0
|
43
|
+
|
44
|
+
|
data/logs/login.POST.log
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
POST /login HTTP/1.1
|
2
|
+
Content-Length: 81
|
3
|
+
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
|
4
|
+
Host: portal.vn.teslamotors.com
|
5
|
+
Connection: Keep-Alive
|
6
|
+
Cookie: _s_portal_session=BAh7B0kiD3Nlc3Npb25faWQGOgZFRkkiJWI2YzM0ZTJkMzZkMTk4ZWVkYjkzYzBiYWNmMzI3MTZkBjsAVEkiEF9jc3JmX3Rva2VuBjsARkkiMWtPSEJBc2VzYlNNeTBBSktzUEM3ZjlhSS9RZ0U5ZzRTeGJVY2ZaQWh2ZGc9BjsARg%3D%3D--da659682afba93065edfe2932903edc6eb16d0ac
|
7
|
+
Cookie2: $Version=1
|
8
|
+
Accept-Encoding: gzip deflate
|
9
|
+
|
10
|
+
user_session%5Bpassword%5D=xxx&user_session%5Bemail%5D=xxx%40xxx.com
|
11
|
+
HTTP/1.1 302 Found
|
12
|
+
Location: https://portal.vn.teslamotors.com/
|
13
|
+
Content-Type: text/html; charset=utf-8
|
14
|
+
X-UA-Compatible: IE=Edge,chrome=1
|
15
|
+
Cache-Control: no-cache
|
16
|
+
Set-Cookie: user_credentials=x; path=/; expires=Fri, 03-May-2013 03:01:54 GMT; secure; HttpOnly
|
17
|
+
Set-Cookie: _s_portal_session=x; path=/; secure; HttpOnly
|
18
|
+
X-Request-Id: a1c6e1dc7d2ba9c1c8cdec6ad536b2e4
|
19
|
+
X-Runtime: 1.129245
|
20
|
+
Date: Sun, 03 Feb 2013 03:01:54 GMT
|
21
|
+
X-Rack-Cache: invalidate, pass
|
22
|
+
Server: thin 1.4.1 codename Chromeo
|
23
|
+
Transfer-Encoding: chunked
|
24
|
+
|
25
|
+
64
|
26
|
+
<html><body>You are being <a href="https://portal.vn.teslamotors.com/">redirected</a>.</body></html>
|
27
|
+
0
|
28
|
+
|
29
|
+
|
data/logs/root.GET.log
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
GET / HTTP/1.1
|
2
|
+
Host: portal.vn.teslamotors.com
|
3
|
+
Connection: Keep-Alive
|
4
|
+
Cookie: _s_portal_session=x; user_credentials=x
|
5
|
+
Cookie2: $Version=1
|
6
|
+
Accept-Encoding: gzip deflate
|
7
|
+
|
8
|
+
|
9
|
+
HTTP/1.1 200 OK
|
10
|
+
Content-Type: text/html; charset=utf-8
|
11
|
+
X-UA-Compatible: IE=Edge,chrome=1
|
12
|
+
ETag: "389b2980d80c9fac569172b31c06dddb"
|
13
|
+
Cache-Control: must-revalidate, private, max-age=0
|
14
|
+
Set-Cookie: _s_portal_session=x; path=/; secure; HttpOnly
|
15
|
+
X-Request-Id: 2c54d0edded8b93de51a3cbf8a5dd3e9
|
16
|
+
X-Runtime: 0.007121
|
17
|
+
Date: Sun, 03 Feb 2013 03:01:54 GMT
|
18
|
+
X-Rack-Cache: miss
|
19
|
+
Server: thin 1.4.1 codename Chromeo
|
20
|
+
Transfer-Encoding: chunked
|
21
|
+
|
22
|
+
249
|
23
|
+
<!DOCTYPE html>
|
24
|
+
<html>
|
25
|
+
<head>
|
26
|
+
<title>Portal</title>
|
27
|
+
<script src="/assets/application-a5592d882a1bdc39d6b26c108a8ffbe0.js" type="text/javascript"></script>
|
28
|
+
<meta content="authenticity_token" name="csrf-param" />
|
29
|
+
<meta content="6/1QjgCk7xTyDJGAK8xIpCNAShD8a6NSco4rbEyz6bw=" name="csrf-token" />
|
30
|
+
<meta name="csrf-token" content="6/1QjgCk7xTyDJGAK8xIpCNAShD8a6NSco4rbEyz6bw=">
|
31
|
+
|
32
|
+
<link href='/images/favicon.ico' rel='shortcut icon'>
|
33
|
+
|
34
|
+
</head>
|
35
|
+
<body>
|
36
|
+
<header>
|
37
|
+
|
38
|
+
<li>
|
39
|
+
<a href="/logout" data-method="delete" rel="nofollow">Logout</a>
|
40
|
+
</li>
|
41
|
+
</header>
|
42
|
+
|
43
|
+
Welcome to the portal app.
|
44
|
+
|
45
|
+
</body>
|
46
|
+
</html>
|
47
|
+
|
48
|
+
0
|
49
|
+
|
50
|
+
|
data/tesla_api.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'tesla_api/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "tesla_api"
|
7
|
+
spec.version = TeslaApi::VERSION
|
8
|
+
spec.authors = ["Tim Dorr"]
|
9
|
+
spec.email = ["timdorr@timdorr.com"]
|
10
|
+
spec.summary = %q{A wrapper for the Tesla API}
|
11
|
+
spec.description = %q{Check the state of your Tesla Model S and issue basic commands. Stream data from the car's telematics system.}
|
12
|
+
spec.homepage = "https://github.com/timdorr/model-s-api"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
21
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tesla_api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tim Dorr
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-12-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description: Check the state of your Tesla Model S and issue basic commands. Stream
|
42
|
+
data from the car's telematics system.
|
43
|
+
email:
|
44
|
+
- timdorr@timdorr.com
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".gitignore"
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE.txt
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- apiary.apib
|
55
|
+
- lib/tesla_api.rb
|
56
|
+
- lib/tesla_api/version.rb
|
57
|
+
- logs/login.GET.log
|
58
|
+
- logs/login.POST.log
|
59
|
+
- logs/root.GET.log
|
60
|
+
- tesla_api.gemspec
|
61
|
+
homepage: https://github.com/timdorr/model-s-api
|
62
|
+
licenses:
|
63
|
+
- MIT
|
64
|
+
metadata: {}
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 2.2.2
|
82
|
+
signing_key:
|
83
|
+
specification_version: 4
|
84
|
+
summary: A wrapper for the Tesla API
|
85
|
+
test_files: []
|