tesla_api 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitbook.yml +4 -0
- data/.travis.yml +3 -4
- data/LICENSE +1 -1
- data/README.md +7 -3
- data/Rakefile +27 -1
- data/apiary.apib +21 -14
- data/docs/README.md +26 -0
- data/docs/SUMMARY.md +39 -0
- data/docs/api-basics/authentication.md +40 -0
- data/docs/api-basics/vehicles.md +77 -0
- data/docs/vehicle/autopark.md +4 -0
- data/docs/vehicle/commands/README.md +22 -0
- data/docs/vehicle/commands/alerts.md +5 -0
- data/docs/vehicle/commands/calendar.md +3 -0
- data/docs/vehicle/commands/charging.md +3 -0
- data/docs/vehicle/commands/climate.md +3 -0
- data/docs/vehicle/commands/doors.md +3 -0
- data/docs/vehicle/commands/media.md +3 -0
- data/docs/vehicle/commands/navigation.md +3 -0
- data/docs/vehicle/commands/remotestart.md +3 -0
- data/docs/vehicle/commands/softwareupdate.md +3 -0
- data/docs/vehicle/commands/speedlimit.md +3 -0
- data/docs/vehicle/commands/sunroof.md +3 -0
- data/docs/vehicle/commands/trunk.md +3 -0
- data/docs/vehicle/commands/valet.md +3 -0
- data/docs/vehicle/commands/wake.md +3 -0
- data/docs/vehicle/optioncodes.md +253 -0
- data/docs/vehicle/state/README.md +34 -0
- data/docs/vehicle/state/chargestate.md +56 -0
- data/docs/vehicle/state/climatestate.md +43 -0
- data/docs/vehicle/state/data.md +181 -0
- data/docs/vehicle/state/drivestate.md +27 -0
- data/docs/vehicle/state/guisettings.md +21 -0
- data/docs/vehicle/state/mobileenabled.md +14 -0
- data/docs/vehicle/state/vehiclestate.md +47 -0
- data/docs/vehicle/streaming.md +4 -0
- data/lib/tesla_api.rb +7 -6
- data/lib/tesla_api/autopark.rb +62 -0
- data/lib/tesla_api/client.rb +32 -11
- data/lib/tesla_api/stream.rb +11 -7
- data/lib/tesla_api/vehicle.rb +50 -30
- data/lib/tesla_api/version.rb +1 -1
- data/spec/cassettes/client-login.yml +1 -1
- data/spec/cassettes/vehicle-activate_speed_limit.yml +95 -0
- data/spec/cassettes/vehicle-clear_speed_limit_pin.yml +95 -0
- data/spec/cassettes/vehicle-deactivate_speed_limit.yml +95 -0
- data/spec/cassettes/vehicle-open_frunk.yml +40 -73
- data/spec/cassettes/vehicle-open_trunk.yml +40 -73
- data/spec/cassettes/vehicle-set_speed_limit.yml +95 -0
- data/spec/lib/tesla_api/client_spec.rb +42 -15
- data/spec/lib/tesla_api/vehicle_spec.rb +160 -136
- data/spec/spec_helper.rb +6 -18
- data/tesla_api.gemspec +20 -24
- metadata +67 -42
- data/circle.yml +0 -10
@@ -1,50 +1,5 @@
|
|
1
1
|
---
|
2
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: 200
|
19
|
-
message: OK
|
20
|
-
headers:
|
21
|
-
Server:
|
22
|
-
- nginx
|
23
|
-
Date:
|
24
|
-
- Wed, 17 Dec 2014 02:02:39 GMT
|
25
|
-
Content-Type:
|
26
|
-
- application/json; charset=utf-8
|
27
|
-
Transfer-Encoding:
|
28
|
-
- chunked
|
29
|
-
Connection:
|
30
|
-
- keep-alive
|
31
|
-
Status:
|
32
|
-
- 200 OK
|
33
|
-
Cache-Control:
|
34
|
-
- no-store
|
35
|
-
Pragma:
|
36
|
-
- no-cache
|
37
|
-
X-Ua-Compatible:
|
38
|
-
- IE=Edge,chrome=1
|
39
|
-
X-Request-Id:
|
40
|
-
- 463d9f839e6eb7ccc712ec5001dfa135
|
41
|
-
X-Runtime:
|
42
|
-
- '0.358328'
|
43
|
-
body:
|
44
|
-
encoding: UTF-8
|
45
|
-
string: '{"access_token":"614ac96b9d291d2af3d7bfc92e2880474afc1a7aa1c49b6df693fb1b1237c60d","token_type":"bearer","expires_in":7776000}'
|
46
|
-
http_version:
|
47
|
-
recorded_at: Wed, 17 Dec 2014 02:02:41 GMT
|
48
3
|
- request:
|
49
4
|
method: get
|
50
5
|
uri: https://owner-api.teslamotors.com/api/1/vehicles
|
@@ -53,7 +8,7 @@ http_interactions:
|
|
53
8
|
string: ''
|
54
9
|
headers:
|
55
10
|
Authorization:
|
56
|
-
- Bearer
|
11
|
+
- Bearer <TESLA_API_TOKEN>
|
57
12
|
response:
|
58
13
|
status:
|
59
14
|
code: 200
|
@@ -62,67 +17,79 @@ http_interactions:
|
|
62
17
|
Server:
|
63
18
|
- nginx
|
64
19
|
Date:
|
65
|
-
-
|
20
|
+
- Sun, 05 Aug 2018 17:05:00 GMT
|
66
21
|
Content-Type:
|
67
22
|
- application/json; charset=utf-8
|
68
23
|
Content-Length:
|
69
|
-
- '
|
24
|
+
- '623'
|
70
25
|
Connection:
|
71
26
|
- keep-alive
|
72
|
-
|
73
|
-
-
|
74
|
-
|
75
|
-
-
|
27
|
+
X-Frame-Options:
|
28
|
+
- SAMEORIGIN
|
29
|
+
- SAMEORIGIN
|
30
|
+
X-Xss-Protection:
|
31
|
+
- 1; mode=block
|
32
|
+
X-Content-Type-Options:
|
33
|
+
- nosniff
|
34
|
+
X-Txid:
|
35
|
+
- 47beb347d84e5640bf8650afc5a5d290
|
76
36
|
Etag:
|
77
|
-
-
|
37
|
+
- W/"6406a0c92e8caf93d1f9120da1610420"
|
78
38
|
Cache-Control:
|
79
39
|
- max-age=0, private, must-revalidate
|
80
40
|
X-Request-Id:
|
81
|
-
-
|
41
|
+
- 00374a64-8682-4b3f-9fd3-a801f43c60b0
|
82
42
|
X-Runtime:
|
83
|
-
- '0.
|
43
|
+
- '0.014727'
|
84
44
|
body:
|
85
45
|
encoding: UTF-8
|
86
46
|
string: '{"response":[{"color":null,"display_name":"Nikola","id":1514029006966957156,"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":["13e0ae629c708af6","156102684b1be0dd"],"state":"online","remote_start_enabled":true,"calendar_enabled":true,"notifications_enabled":true}],"count":1}'
|
87
47
|
http_version:
|
88
|
-
recorded_at:
|
48
|
+
recorded_at: Sun, 05 Aug 2018 17:05:00 GMT
|
89
49
|
- request:
|
90
50
|
method: post
|
91
|
-
uri: https://owner-api.teslamotors.com/api/1/vehicles/1514029006966957156/command/
|
51
|
+
uri: https://owner-api.teslamotors.com/api/1/vehicles/1514029006966957156/command/actuate_trunk
|
92
52
|
body:
|
93
53
|
encoding: UTF-8
|
94
|
-
string: which_trunk=
|
54
|
+
string: which_trunk=front
|
95
55
|
headers:
|
96
56
|
Authorization:
|
97
|
-
- Bearer
|
57
|
+
- Bearer <TESLA_API_TOKEN>
|
98
58
|
response:
|
99
59
|
status:
|
100
|
-
code:
|
101
|
-
message:
|
60
|
+
code: 200
|
61
|
+
message: OK
|
102
62
|
headers:
|
103
63
|
Server:
|
104
64
|
- nginx
|
105
65
|
Date:
|
106
|
-
-
|
66
|
+
- Sun, 05 Aug 2018 17:05:00 GMT
|
107
67
|
Content-Type:
|
108
68
|
- application/json; charset=utf-8
|
109
69
|
Content-Length:
|
110
|
-
- '
|
70
|
+
- '40'
|
111
71
|
Connection:
|
112
72
|
- keep-alive
|
113
|
-
|
114
|
-
-
|
115
|
-
|
116
|
-
-
|
73
|
+
X-Frame-Options:
|
74
|
+
- SAMEORIGIN
|
75
|
+
- SAMEORIGIN
|
76
|
+
X-Xss-Protection:
|
77
|
+
- 1; mode=block
|
78
|
+
X-Content-Type-Options:
|
79
|
+
- nosniff
|
80
|
+
X-Txid:
|
81
|
+
- 5e6adf1c1e9a73c310d45820d88451aa
|
82
|
+
Etag:
|
83
|
+
- W/"f67eec105dd6522783a1f1bacc52723a"
|
117
84
|
Cache-Control:
|
118
|
-
-
|
85
|
+
- max-age=0, private, must-revalidate
|
119
86
|
X-Request-Id:
|
120
|
-
-
|
87
|
+
- 845150c1-1b8d-4ee9-8690-65f60cfeb48f
|
121
88
|
X-Runtime:
|
122
|
-
- '0.
|
89
|
+
- '0.131305'
|
123
90
|
body:
|
124
91
|
encoding: UTF-8
|
125
|
-
string: '{"response":
|
92
|
+
string: '{"response":{"reason":"","result":true}}'
|
126
93
|
http_version:
|
127
|
-
recorded_at:
|
128
|
-
recorded_with: VCR
|
94
|
+
recorded_at: Sun, 05 Aug 2018 17:05:00 GMT
|
95
|
+
recorded_with: VCR 3.0.3
|
@@ -0,0 +1,95 @@
|
|
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
|
+
- Wed, 18 Jul 2018 23:51:18 GMT
|
21
|
+
Content-Type:
|
22
|
+
- application/json; charset=utf-8
|
23
|
+
Content-Length:
|
24
|
+
- '623'
|
25
|
+
Connection:
|
26
|
+
- keep-alive
|
27
|
+
X-Frame-Options:
|
28
|
+
- SAMEORIGIN
|
29
|
+
- SAMEORIGIN
|
30
|
+
X-Xss-Protection:
|
31
|
+
- 1; mode=block
|
32
|
+
X-Content-Type-Options:
|
33
|
+
- nosniff
|
34
|
+
X-Txid:
|
35
|
+
- 0dd1f1f026f902339fe4bb629429d653
|
36
|
+
Etag:
|
37
|
+
- W/"6958697e3e1e010f56068d8c7fa2f93d"
|
38
|
+
Cache-Control:
|
39
|
+
- max-age=0, private, must-revalidate
|
40
|
+
X-Request-Id:
|
41
|
+
- 810ad995-1c32-4527-9b55-3158ab119e1b
|
42
|
+
X-Runtime:
|
43
|
+
- '0.027684'
|
44
|
+
body:
|
45
|
+
encoding: UTF-8
|
46
|
+
string: '{"response":[{"color":null,"display_name":"Nikola","id":1514029006966957156,"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":["a1f3fab6393d3a6a","91a139ce322eed65"],"state":"online","remote_start_enabled":true,"calendar_enabled":true,"notifications_enabled":true}],"count":1}'
|
47
|
+
http_version:
|
48
|
+
recorded_at: Wed, 18 Jul 2018 23:51:18 GMT
|
49
|
+
- request:
|
50
|
+
method: post
|
51
|
+
uri: https://owner-api.teslamotors.com/api/1/vehicles/1514029006966957156/command/speed_limit_set_limit
|
52
|
+
body:
|
53
|
+
encoding: UTF-8
|
54
|
+
string: limit_mph=65
|
55
|
+
headers:
|
56
|
+
Authorization:
|
57
|
+
- Bearer <TESLA_API_TOKEN>
|
58
|
+
response:
|
59
|
+
status:
|
60
|
+
code: 200
|
61
|
+
message: OK
|
62
|
+
headers:
|
63
|
+
Server:
|
64
|
+
- nginx
|
65
|
+
Date:
|
66
|
+
- Wed, 18 Jul 2018 23:51:19 GMT
|
67
|
+
Content-Type:
|
68
|
+
- application/json; charset=utf-8
|
69
|
+
Content-Length:
|
70
|
+
- '40'
|
71
|
+
Connection:
|
72
|
+
- keep-alive
|
73
|
+
X-Frame-Options:
|
74
|
+
- SAMEORIGIN
|
75
|
+
- SAMEORIGIN
|
76
|
+
X-Xss-Protection:
|
77
|
+
- 1; mode=block
|
78
|
+
X-Content-Type-Options:
|
79
|
+
- nosniff
|
80
|
+
X-Txid:
|
81
|
+
- 47cdfaf37f13879846e19ed31441ade3
|
82
|
+
Etag:
|
83
|
+
- W/"f67eec105dd6522783a1f1bacc52723a"
|
84
|
+
Cache-Control:
|
85
|
+
- max-age=0, private, must-revalidate
|
86
|
+
X-Request-Id:
|
87
|
+
- 0523f468-6cde-4ae4-8dd3-870de69ad2f4
|
88
|
+
X-Runtime:
|
89
|
+
- '0.112159'
|
90
|
+
body:
|
91
|
+
encoding: UTF-8
|
92
|
+
string: '{"response":{"reason":"","result":true}}'
|
93
|
+
http_version:
|
94
|
+
recorded_at: Wed, 18 Jul 2018 23:51:19 GMT
|
95
|
+
recorded_with: VCR 3.0.3
|
@@ -1,36 +1,63 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe TeslaApi::Client do
|
4
|
-
subject(:tesla_api) { TeslaApi::Client.new(ENV[
|
4
|
+
subject(:tesla_api) { TeslaApi::Client.new(ENV['TESLA_EMAIL']) }
|
5
5
|
|
6
|
-
describe
|
7
|
-
it
|
6
|
+
describe '#new client' do
|
7
|
+
it 'has no expiry date' do
|
8
|
+
expect(tesla_api.expired_at).to eq(nil)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'has a expiry status set to true' do
|
12
|
+
expect(tesla_api.expired?).to eq(true)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#token=' do
|
17
|
+
it 'sets a Bearer token' do
|
8
18
|
tesla_api.token = Faker::Lorem.characters(32)
|
9
|
-
expect(tesla_api.class.headers).to include({
|
19
|
+
expect(tesla_api.class.headers).to include({'Authorization' => /Bearer [a-z0-9]{32}/})
|
10
20
|
end
|
11
21
|
end
|
12
22
|
|
13
|
-
describe
|
23
|
+
describe '#login!', vcr: { cassette_name: 'client-login' } do
|
14
24
|
it { is_expected.to be_a(TeslaApi::Client) }
|
15
25
|
|
16
|
-
it
|
17
|
-
tesla_api.login!(ENV[
|
26
|
+
it 'logs into the API' do
|
27
|
+
tesla_api.login!(ENV['TESLA_PASS'])
|
18
28
|
expect(a_request(:post, "https://#{URI.parse(tesla_api.class.base_uri).host}/oauth/token")).to have_been_made.once
|
19
29
|
end
|
20
30
|
|
21
|
-
it
|
22
|
-
tesla_api.login!(ENV[
|
23
|
-
expect(tesla_api.
|
31
|
+
it 'set a expiry date' do
|
32
|
+
tesla_api.login!(ENV['TESLA_PASS'])
|
33
|
+
expect(tesla_api.expired_at).to eq(Time.at(1475777133 + 7776000).to_datetime)
|
24
34
|
end
|
25
35
|
|
26
|
-
it
|
27
|
-
tesla_api.login!(ENV[
|
28
|
-
|
36
|
+
it 'expose expiry status' do
|
37
|
+
tesla_api.login!(ENV['TESLA_PASS'])
|
38
|
+
tesla_api.created_at = (Time.now - 1).to_i
|
39
|
+
expect(tesla_api.expired?).to eq(false)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'is expired when has a 90+ days old date' do
|
43
|
+
tesla_api.login!(ENV['TESLA_PASS'])
|
44
|
+
tesla_api.created_at = (Time.now - 7776000 - 1).to_i
|
45
|
+
expect(tesla_api.expired?).to eq(true)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'sets a Bearer token header' do
|
49
|
+
tesla_api.login!(ENV['TESLA_PASS'])
|
50
|
+
expect(tesla_api.class.headers).to include({'Authorization' => /Bearer [a-z0-9]{32}/})
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'obtains a Bearer token' do
|
54
|
+
tesla_api.login!(ENV['TESLA_PASS'])
|
55
|
+
expect(tesla_api.token).to match(/[a-z0-9]{32}/)
|
29
56
|
end
|
30
57
|
end
|
31
58
|
|
32
|
-
describe
|
33
|
-
it
|
59
|
+
describe '#vehicles', vcr: {cassette_name: 'client-vehicles'} do
|
60
|
+
it 'lists the vehicles on the account' do
|
34
61
|
expect(tesla_api.vehicles).to include(TeslaApi::Vehicle)
|
35
62
|
end
|
36
63
|
end
|
@@ -1,271 +1,295 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe TeslaApi::Vehicle do
|
4
|
-
let(:tesla_api) { TeslaApi::Client.new(ENV[
|
4
|
+
let(:tesla_api) { TeslaApi::Client.new(ENV['TESLA_EMAIL']) }
|
5
5
|
|
6
6
|
before do
|
7
|
-
tesla_api.token = ENV[
|
7
|
+
tesla_api.token = ENV['TESLA_API_TOKEN']
|
8
8
|
end
|
9
9
|
|
10
10
|
subject(:vehicle) { tesla_api.vehicles.first }
|
11
11
|
|
12
|
-
describe
|
13
|
-
it
|
14
|
-
expect(vehicle[
|
12
|
+
describe '#[]', vcr: { cassette_name: 'vehicle' } do
|
13
|
+
it 'contains the vehicle name' do
|
14
|
+
expect(vehicle['display_name']).to eq('Nikola')
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
describe
|
19
|
-
it
|
18
|
+
describe '#method_missing', vcr: {cassette_name: 'vehicle'} do
|
19
|
+
it 'doesn\'t provide dynamic methods for nonexistent properties' do
|
20
20
|
expect{vehicle.gas_level}.to raise_exception(NoMethodError)
|
21
21
|
end
|
22
22
|
|
23
|
-
it
|
24
|
-
expect(vehicle.display_name).to eq(
|
23
|
+
it 'provides a dynamic method for the display_name' do
|
24
|
+
expect(vehicle.display_name).to eq('Nikola')
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
describe
|
29
|
-
it
|
28
|
+
describe '#mobile_enabled', vcr: {cassette_name: 'vehicle-mobile_enabled'} do
|
29
|
+
it 'indicates if mobile app access is enabled' do
|
30
30
|
expect(vehicle.mobile_enabled).to eq(true)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
describe
|
35
|
-
context
|
34
|
+
describe '#gui_settings', vcr: {cassette_name: 'vehicle-gui_settings'} do
|
35
|
+
context 'details for a GUI built on the API' do
|
36
36
|
subject { vehicle.gui_settings }
|
37
37
|
|
38
|
-
it { should include(
|
39
|
-
it { should include(
|
40
|
-
it { should include(
|
41
|
-
it { should include(
|
42
|
-
it { should include(
|
38
|
+
it { should include('gui_charge_rate_units') }
|
39
|
+
it { should include('gui_distance_units') }
|
40
|
+
it { should include('gui_temperature_units') }
|
41
|
+
it { should include('gui_range_display') }
|
42
|
+
it { should include('gui_24_hour_time') }
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
describe
|
47
|
-
context
|
46
|
+
describe '#charge_state', vcr: {cassette_name: 'vehicle-charge_state'} do
|
47
|
+
context 'data about the vehicle\'s charging state' do
|
48
48
|
subject { vehicle.charge_state }
|
49
49
|
|
50
|
-
it { should include(
|
51
|
-
it { should include(
|
52
|
-
it { should include(
|
53
|
-
it { should include(
|
54
|
-
it { should include(
|
50
|
+
it { should include('battery_level') }
|
51
|
+
it { should include('charge_rate') }
|
52
|
+
it { should include('charging_state') }
|
53
|
+
it { should include('est_battery_range') }
|
54
|
+
it { should include('fast_charger_present') }
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
describe
|
59
|
-
context
|
58
|
+
describe '#climate_state', vcr: {cassette_name: 'vehicle-climate_state'} do
|
59
|
+
context 'data about the vehicle\'s climate controls and internal climate' do
|
60
60
|
subject { vehicle.climate_state }
|
61
61
|
|
62
|
-
it { should include(
|
63
|
-
it { should include(
|
64
|
-
it { should include(
|
65
|
-
it { should include(
|
66
|
-
it { should include(
|
62
|
+
it { should include('inside_temp') }
|
63
|
+
it { should include('outside_temp') }
|
64
|
+
it { should include('driver_temp_setting') }
|
65
|
+
it { should include('passenger_temp_setting') }
|
66
|
+
it { should include('is_auto_conditioning_on') }
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
describe
|
71
|
-
context
|
70
|
+
describe '#drive_state', vcr: {cassette_name: 'vehicle-drive_state'} do
|
71
|
+
context 'data about the vehicle\'s driving state' do
|
72
72
|
subject { vehicle.drive_state }
|
73
73
|
|
74
|
-
it { should include(
|
75
|
-
it { should include(
|
76
|
-
it { should include(
|
77
|
-
it { should include(
|
74
|
+
it { should include('heading') }
|
75
|
+
it { should include('latitude') }
|
76
|
+
it { should include('longitude') }
|
77
|
+
it { should include('speed') }
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
|
-
describe
|
82
|
-
context
|
81
|
+
describe '#vehicle_state', vcr: {cassette_name: 'vehicle-vehicle_state'} do
|
82
|
+
context 'data about the vehicle\'s overall state' do
|
83
83
|
subject { vehicle.vehicle_state }
|
84
84
|
|
85
|
-
it { should include(
|
86
|
-
it { should include(
|
87
|
-
it { should include(
|
88
|
-
it { should include(
|
89
|
-
it { should include(
|
90
|
-
it { should include(
|
85
|
+
it { should include('vehicle_name') }
|
86
|
+
it { should include('exterior_color') }
|
87
|
+
it { should include('wheel_type') }
|
88
|
+
it { should include('has_spoiler') }
|
89
|
+
it { should include('calendar_supported') }
|
90
|
+
it { should include('locked') }
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
94
|
-
describe
|
95
|
-
it
|
94
|
+
describe '#wake_up', vcr: {cassette_name: 'vehicle-wake_up'} do
|
95
|
+
it 'wakes up the car from sleep mode' do
|
96
96
|
vehicle.wake_up
|
97
|
-
expect(vehicle.state).to eq(
|
97
|
+
expect(vehicle.state).to eq('online')
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
|
-
describe
|
102
|
-
it
|
103
|
-
expect(vehicle.set_valet_mode(true, 1234)[
|
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
104
|
end
|
105
105
|
|
106
|
-
it
|
107
|
-
expect(vehicle.set_valet_mode(true)[
|
106
|
+
it 'enables valet mode with a previous PIN' do
|
107
|
+
expect(vehicle.set_valet_mode(true)['result']).to eq(true)
|
108
108
|
end
|
109
109
|
end
|
110
110
|
|
111
|
-
describe
|
112
|
-
it
|
113
|
-
expect(vehicle.reset_valet_pin[
|
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
114
|
end
|
115
115
|
end
|
116
116
|
|
117
|
-
describe
|
118
|
-
it
|
119
|
-
expect(vehicle.charge_port_door_open[
|
117
|
+
describe '#charge_port_door_open', vcr: {cassette_name: 'vehicle-charge_port_door_open'} do
|
118
|
+
it 'opens the charge port door' do
|
119
|
+
expect(vehicle.charge_port_door_open['result']).to eq(true)
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
123
|
-
describe
|
124
|
-
it
|
125
|
-
expect(vehicle.charge_standard[
|
123
|
+
describe '#charge_standard' do
|
124
|
+
it 'sets the charge limit to standard (90%)', vcr: {cassette_name: 'vehicle-charge_standard'} do
|
125
|
+
expect(vehicle.charge_standard['result']).to eq(true)
|
126
126
|
end
|
127
127
|
|
128
|
-
it
|
129
|
-
expect(vehicle.charge_standard[
|
130
|
-
expect(vehicle.charge_standard).to eq({
|
128
|
+
it 'doesn\'t set the charge rate to standard twice', vcr: {cassette_name: 'vehicle-charge_standard-twice'} do
|
129
|
+
expect(vehicle.charge_standard['result']).to eq(true)
|
130
|
+
expect(vehicle.charge_standard).to eq({'result' => false, 'reason' => 'already_standard'})
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|
134
|
-
describe
|
135
|
-
it
|
136
|
-
expect(vehicle.charge_max_range[
|
134
|
+
describe '#charge_max_range' do
|
135
|
+
it 'sets the charge limit to max range (100%)', vcr: {cassette_name: 'vehicle-charge_max_range'} do
|
136
|
+
expect(vehicle.charge_max_range['result']).to eq(true)
|
137
137
|
end
|
138
138
|
|
139
|
-
it
|
140
|
-
expect(vehicle.charge_max_range[
|
141
|
-
expect(vehicle.charge_max_range).to eq({
|
139
|
+
it 'doesn\'t set the charge rate to max range twice', vcr: {cassette_name: 'vehicle-charge_max_range-twice'} do
|
140
|
+
expect(vehicle.charge_max_range['result']).to eq(true)
|
141
|
+
expect(vehicle.charge_max_range).to eq({'result' => false, 'reason' => 'already_max_range'})
|
142
142
|
end
|
143
143
|
end
|
144
144
|
|
145
|
-
describe
|
146
|
-
it
|
147
|
-
expect(vehicle.set_charge_limit(100)[
|
145
|
+
describe '#set_charge_limit' do
|
146
|
+
it 'sets the charge limit to 100%', vcr: {cassette_name: 'vehicle-set_charge_limit-100'} do
|
147
|
+
expect(vehicle.set_charge_limit(100)['result']).to eq(true)
|
148
148
|
end
|
149
149
|
|
150
|
-
it
|
151
|
-
expect(vehicle.set_charge_limit(90)[
|
150
|
+
it 'sets the charge limit to 90%', vcr: {cassette_name: 'vehicle-set_charge_limit-90'} do
|
151
|
+
expect(vehicle.set_charge_limit(90)['result']).to eq(true)
|
152
152
|
end
|
153
153
|
|
154
|
-
it
|
155
|
-
expect(vehicle.set_charge_limit(50)[
|
154
|
+
it 'sets the charge limit to 50%', vcr: {cassette_name: 'vehicle-set_charge_limit-50'} do
|
155
|
+
expect(vehicle.set_charge_limit(50)['result']).to eq(true)
|
156
156
|
end
|
157
157
|
|
158
|
-
it
|
159
|
-
expect(vehicle.set_charge_limit(1)[
|
160
|
-
expect(vehicle.charge_state[
|
158
|
+
it 'doesn\'t actually set the charge limit to 1%', vcr: {cassette_name: 'vehicle-set_charge_limit-1'} do
|
159
|
+
expect(vehicle.set_charge_limit(1)['result']).to eq(true)
|
160
|
+
expect(vehicle.charge_state['charge_limit_soc']).to eq(50)
|
161
161
|
end
|
162
162
|
end
|
163
163
|
|
164
|
-
describe
|
165
|
-
it
|
166
|
-
expect(vehicle.charge_start[
|
164
|
+
describe '#charge_start', vcr: {cassette_name: 'vehicle-charge_start'} do
|
165
|
+
it 'starts charging' do
|
166
|
+
expect(vehicle.charge_start['result']).to eq(true)
|
167
167
|
end
|
168
168
|
end
|
169
169
|
|
170
|
-
describe
|
171
|
-
it
|
172
|
-
expect(vehicle.charge_stop[
|
170
|
+
describe '#charge_stop', vcr: {cassette_name: 'vehicle-charge_stop'} do
|
171
|
+
it 'stops charging' do
|
172
|
+
expect(vehicle.charge_stop['result']).to eq(true)
|
173
173
|
end
|
174
174
|
end
|
175
175
|
|
176
|
-
describe
|
177
|
-
it
|
178
|
-
expect(vehicle.flash_lights[
|
176
|
+
describe '#flash_lights', vcr: {cassette_name: 'vehicle-flash_lights'} do
|
177
|
+
it 'flashes the vehicle\'s lights' do
|
178
|
+
expect(vehicle.flash_lights['result']).to eq(true)
|
179
179
|
end
|
180
180
|
end
|
181
181
|
|
182
|
-
describe
|
183
|
-
it
|
184
|
-
expect(vehicle.honk_horn[
|
182
|
+
describe '#honk_horn', vcr: {cassette_name: 'vehicle-honk_horn'} do
|
183
|
+
it 'honks the vehicle\'s horn' do
|
184
|
+
expect(vehicle.honk_horn['result']).to eq(true)
|
185
185
|
end
|
186
186
|
end
|
187
187
|
|
188
|
-
describe
|
189
|
-
it
|
190
|
-
expect(vehicle.door_unlock[
|
188
|
+
describe '#door_unlock', vcr: {cassette_name: 'vehicle-door_unlock'} do
|
189
|
+
it 'unlocks the vehicle\'s doors' do
|
190
|
+
expect(vehicle.door_unlock['result']).to eq(true)
|
191
191
|
end
|
192
192
|
end
|
193
193
|
|
194
|
-
describe
|
195
|
-
it
|
196
|
-
expect(vehicle.door_lock[
|
194
|
+
describe '#door_lock', vcr: {cassette_name: 'vehicle-door_lock'} do
|
195
|
+
it 'locks the vehicle\'s doors' do
|
196
|
+
expect(vehicle.door_lock['result']).to eq(true)
|
197
197
|
end
|
198
198
|
end
|
199
199
|
|
200
|
-
describe
|
201
|
-
it
|
202
|
-
expect(vehicle.set_temps(20.9, 20.9)[
|
200
|
+
describe '#set_temps' do
|
201
|
+
it 'sets the desired temperature in the car to 70', vcr: {cassette_name: 'vehicle-set_temps-70-70'} do
|
202
|
+
expect(vehicle.set_temps(20.9, 20.9)['result']).to eq(true)
|
203
203
|
end
|
204
204
|
|
205
|
-
it
|
206
|
-
expect(vehicle.set_temps(23.7, 23.7)[
|
205
|
+
it 'sets the desired temperature in the car to 75 and 65', vcr: {cassette_name: 'vehicle-set_temps-75-65'} do
|
206
|
+
expect(vehicle.set_temps(23.7, 23.7)['result']).to eq(true)
|
207
207
|
end
|
208
208
|
end
|
209
209
|
|
210
|
-
describe
|
211
|
-
it
|
212
|
-
expect(vehicle.auto_conditioning_start[
|
210
|
+
describe '#auto_conditioning_start', vcr: {cassette_name: 'vehicle-auto_conditioning_start'} do
|
211
|
+
it 'starts the HVAC system' do
|
212
|
+
expect(vehicle.auto_conditioning_start['result']).to eq(true)
|
213
213
|
end
|
214
214
|
end
|
215
215
|
|
216
|
-
describe
|
217
|
-
it
|
218
|
-
expect(vehicle.auto_conditioning_stop[
|
216
|
+
describe '#auto_conditioning_stop', vcr: {cassette_name: 'vehicle-auto_conditioning_stop'} do
|
217
|
+
it 'stops the HVAC system' do
|
218
|
+
expect(vehicle.auto_conditioning_stop['result']).to eq(true)
|
219
219
|
end
|
220
220
|
end
|
221
221
|
|
222
|
-
describe
|
223
|
-
it
|
224
|
-
expect(vehicle.sun_roof_control(
|
222
|
+
describe '#sun_roof_control' do
|
223
|
+
it 'opens the sun roof', vcr: {cassette_name: 'vehicle-sun_roof_control-open'} do
|
224
|
+
expect(vehicle.sun_roof_control('open')['result']).to eq(true)
|
225
225
|
end
|
226
226
|
|
227
|
-
it
|
228
|
-
expect(vehicle.sun_roof_control(
|
227
|
+
it 'closes the sun roof', vcr: {cassette_name: 'vehicle-sun_roof_control-close'} do
|
228
|
+
expect(vehicle.sun_roof_control('close')['result']).to eq(true)
|
229
229
|
end
|
230
230
|
|
231
|
-
it
|
232
|
-
expect(vehicle.sun_roof_control(
|
231
|
+
it 'vents the sun roof', vcr: {cassette_name: 'vehicle-sun_roof_control-vent'} do
|
232
|
+
expect(vehicle.sun_roof_control('vent')['result']).to eq(true)
|
233
233
|
end
|
234
234
|
|
235
|
-
it
|
236
|
-
expect(vehicle.sun_roof_control(
|
235
|
+
it 'sets the sun roof to the comfort (80%) setting', vcr: {cassette_name: 'vehicle-sun_roof_control-comfort'} do
|
236
|
+
expect(vehicle.sun_roof_control('comfort')['result']).to eq(true)
|
237
237
|
end
|
238
238
|
end
|
239
239
|
|
240
|
-
describe
|
241
|
-
it
|
242
|
-
expect(vehicle.sun_roof_move(100)[
|
240
|
+
describe '#sun_roof_move' do
|
241
|
+
it 'moves the sun roof to 100% open', vcr: {cassette_name: 'vehicle-sun_roof_move-100'} do
|
242
|
+
expect(vehicle.sun_roof_move(100)['result']).to eq(true)
|
243
243
|
end
|
244
244
|
|
245
|
-
it
|
246
|
-
expect(vehicle.sun_roof_move(0)[
|
245
|
+
it 'moves the sun roof to closed', vcr: {cassette_name: 'vehicle-sun_roof_move-0'} do
|
246
|
+
expect(vehicle.sun_roof_move(0)['result']).to eq(true)
|
247
247
|
end
|
248
248
|
|
249
|
-
it
|
250
|
-
expect(vehicle.sun_roof_move(50)[
|
249
|
+
it 'moves the sun roof to 50% open', vcr: {cassette_name: 'vehicle-sun_roof_move-50'} do
|
250
|
+
expect(vehicle.sun_roof_move(50)['result']).to eq(true)
|
251
251
|
end
|
252
252
|
end
|
253
253
|
|
254
|
-
describe
|
255
|
-
it
|
256
|
-
expect(vehicle.remote_start_drive(
|
254
|
+
describe '#remote_start_drive', vcr: {cassette_name: 'vehicle-remote_start_drive'} do
|
255
|
+
it 'starts the vehicle\'s keyless driving mode' do
|
256
|
+
expect(vehicle.remote_start_drive('elon4eva')['result']).to eq(true)
|
257
257
|
end
|
258
258
|
end
|
259
259
|
|
260
|
-
describe
|
261
|
-
it
|
262
|
-
expect(vehicle.
|
260
|
+
describe '#open_trunk', vcr: {cassette_name: 'vehicle-open_trunk'} do
|
261
|
+
it 'opens the trunk' do
|
262
|
+
expect(vehicle.open_frunk['result']).to eq(true)
|
263
263
|
end
|
264
264
|
end
|
265
265
|
|
266
|
-
describe
|
267
|
-
it
|
268
|
-
expect(vehicle.open_frunk[
|
266
|
+
describe '#open_frunk', vcr: {cassette_name: 'vehicle-open_frunk'} do
|
267
|
+
it 'opens the frunk' do
|
268
|
+
expect(vehicle.open_frunk['result']).to eq(true)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
describe '#activate_speed_limit', vcr: {cassette_name: 'vehicle-activate_speed_limit'} do
|
273
|
+
it 'activates the speed limit' do
|
274
|
+
expect(vehicle.activate_speed_limit('1234')['result']).to eq(true)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
describe '#deactivate_speed_limit', vcr: {cassette_name: 'vehicle-deactivate_speed_limit'} do
|
279
|
+
it 'deactivates the speed limit' do
|
280
|
+
expect(vehicle.deactivate_speed_limit('1234')['result']).to eq(true)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
describe '#set_speed_limit', vcr: {cassette_name: 'vehicle-set_speed_limit'} do
|
285
|
+
it 'sets the speed limit' do
|
286
|
+
expect(vehicle.set_speed_limit(65)['result']).to eq(true)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
describe '#clear_speed_limit_pin', vcr: {cassette_name: 'vehicle-clear_speed_limit_pin'} do
|
291
|
+
it 'clears the speed limit pin' do
|
292
|
+
expect(vehicle.clear_speed_limit_pin('1234')['result']).to eq(true)
|
269
293
|
end
|
270
294
|
end
|
271
295
|
end
|