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