tesla 0.0.1 → 0.1.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.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +5 -0
  3. data/CHANGELOG.md +31 -0
  4. data/CONTRIBUTING.md +45 -0
  5. data/{LICENSE.txt → LICENSE.md} +1 -1
  6. data/README.md +366 -0
  7. data/SECURITY.md +38 -0
  8. data/lib/tesla/api/alert_endpoints.rb +35 -0
  9. data/lib/tesla/api/charging_endpoints.rb +115 -0
  10. data/lib/tesla/api/climate_endpoints.rb +180 -0
  11. data/lib/tesla/api/closure_endpoints.rb +142 -0
  12. data/lib/tesla/api/command_endpoints.rb +63 -0
  13. data/lib/tesla/api/media_endpoints.rb +114 -0
  14. data/lib/tesla/api/navigation_endpoints.rb +51 -0
  15. data/lib/tesla/api/oauth_endpoints.rb +118 -0
  16. data/lib/tesla/api/partner_endpoints.rb +62 -0
  17. data/lib/tesla/api/security_endpoints.rb +123 -0
  18. data/lib/tesla/api/software_endpoints.rb +38 -0
  19. data/lib/tesla/api/user_endpoints.rb +40 -0
  20. data/lib/tesla/api/vehicle_endpoints.rb +174 -0
  21. data/lib/tesla/api.rb +39 -0
  22. data/lib/tesla/authenticator.rb +33 -0
  23. data/lib/tesla/bearer_token_authenticator.rb +38 -0
  24. data/lib/tesla/certificate_options.rb +51 -0
  25. data/lib/tesla/charge_state.rb +111 -0
  26. data/lib/tesla/charging_site.rb +67 -0
  27. data/lib/tesla/client.rb +336 -0
  28. data/lib/tesla/client_credentials.rb +230 -0
  29. data/lib/tesla/climate_state.rb +116 -0
  30. data/lib/tesla/configuration.rb +483 -0
  31. data/lib/tesla/connection.rb +361 -0
  32. data/lib/tesla/connection_pool.rb +186 -0
  33. data/lib/tesla/deep_copy.rb +41 -0
  34. data/lib/tesla/drive_state.rb +80 -0
  35. data/lib/tesla/errors/bad_gateway.rb +9 -0
  36. data/lib/tesla/errors/bad_request.rb +9 -0
  37. data/lib/tesla/errors/client_error.rb +9 -0
  38. data/lib/tesla/errors/command_failed.rb +42 -0
  39. data/lib/tesla/errors/device_unexpected_response.rb +12 -0
  40. data/lib/tesla/errors/error.rb +7 -0
  41. data/lib/tesla/errors/forbidden.rb +14 -0
  42. data/lib/tesla/errors/gateway_timeout.rb +9 -0
  43. data/lib/tesla/errors/http_error.rb +89 -0
  44. data/lib/tesla/errors/internal_server_error.rb +9 -0
  45. data/lib/tesla/errors/invalid_response.rb +33 -0
  46. data/lib/tesla/errors/locked.rb +12 -0
  47. data/lib/tesla/errors/method_not_allowed.rb +9 -0
  48. data/lib/tesla/errors/misdirected_request.rb +13 -0
  49. data/lib/tesla/errors/network_error.rb +30 -0
  50. data/lib/tesla/errors/not_acceptable.rb +9 -0
  51. data/lib/tesla/errors/not_found.rb +9 -0
  52. data/lib/tesla/errors/oauth_error.rb +53 -0
  53. data/lib/tesla/errors/payment_required.rb +12 -0
  54. data/lib/tesla/errors/precondition_failed.rb +13 -0
  55. data/lib/tesla/errors/request_timeout.rb +13 -0
  56. data/lib/tesla/errors/server_error.rb +9 -0
  57. data/lib/tesla/errors/service_unavailable.rb +9 -0
  58. data/lib/tesla/errors/too_many_redirects.rb +9 -0
  59. data/lib/tesla/errors/too_many_requests.rb +9 -0
  60. data/lib/tesla/errors/unauthorized.rb +9 -0
  61. data/lib/tesla/errors/unavailable_for_legal_reasons.rb +12 -0
  62. data/lib/tesla/errors/unprocessable_content.rb +12 -0
  63. data/lib/tesla/errors/vehicle_asleep.rb +32 -0
  64. data/lib/tesla/gui_settings.rb +60 -0
  65. data/lib/tesla/idempotence.rb +30 -0
  66. data/lib/tesla/identifiers.rb +27 -0
  67. data/lib/tesla/json_parsing.rb +38 -0
  68. data/lib/tesla/nearby_charging_sites.rb +35 -0
  69. data/lib/tesla/path_escaping.rb +30 -0
  70. data/lib/tesla/redacted_output.rb +161 -0
  71. data/lib/tesla/redirect_handler.rb +196 -0
  72. data/lib/tesla/region.rb +27 -0
  73. data/lib/tesla/request_builder.rb +138 -0
  74. data/lib/tesla/resource.rb +403 -0
  75. data/lib/tesla/response_parser.rb +92 -0
  76. data/lib/tesla/retry_after.rb +34 -0
  77. data/lib/tesla/retry_handler.rb +252 -0
  78. data/lib/tesla/settings.rb +115 -0
  79. data/lib/tesla/url_validation.rb +131 -0
  80. data/lib/tesla/user.rb +36 -0
  81. data/lib/tesla/vehicle.rb +90 -0
  82. data/lib/tesla/vehicle_config.rb +95 -0
  83. data/lib/tesla/vehicle_data.rb +67 -0
  84. data/lib/tesla/vehicle_state.rb +125 -0
  85. data/lib/tesla/version.rb +6 -0
  86. data/lib/tesla.rb +139 -0
  87. data/sig/manifest.yaml +10 -0
  88. data/sig/tesla.rbs +989 -0
  89. metadata +134 -118
  90. data/.document +0 -5
  91. data/.rvmrc +0 -1
  92. data/Gemfile +0 -13
  93. data/Gemfile.lock +0 -20
  94. data/README.rdoc +0 -19
  95. data/Rakefile +0 -47
  96. data/test/helper.rb +0 -19
  97. data/test/test_tesla.rb +0 -7
@@ -0,0 +1,230 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "simple_oauth"
4
+ require "uri"
5
+ require_relative "authenticator"
6
+ require_relative "bearer_token_authenticator"
7
+ require_relative "errors/oauth_error"
8
+
9
+ module Tesla
10
+ # The credentials of a client, and the tokens it asks Tesla's authorization server for, mixed into {Client}
11
+ #
12
+ # The requests to the token endpoint are built by the simple_oauth gem and sent on the connection of the client,
13
+ # so that the timeouts, the proxy, and the certificates of the client apply to them.
14
+ #
15
+ # A refresh token of the Fleet API is used once: refreshing the access token answers with another refresh token,
16
+ # and the one it was asked for with stops working a day later. Two threads refreshing with the same refresh token
17
+ # would each be answered with tokens of their own, so a client refreshes one thread at a time, and a thread that
18
+ # waited for another to refresh uses the token that thread was answered with rather than refreshing again.
19
+ #
20
+ # @api private
21
+ module ClientCredentials
22
+ # The access token requests are authorized with
23
+ # @api public
24
+ # @return [String, nil] the access token
25
+ # @example Get or set the access token
26
+ # client.access_token = "eyJhbGciOiJSUzI1NiIs"
27
+ attr_accessor :access_token
28
+
29
+ # The refresh token a new access token is asked for with
30
+ #
31
+ # This is the refresh token the last refresh answered with, once the client has refreshed its access token.
32
+ #
33
+ # @api public
34
+ # @return [String, nil] the refresh token
35
+ # @example Get or set the refresh token
36
+ # client.refresh_token = "NA_9d0b4bfd0c4c3a6e"
37
+ attr_accessor :refresh_token
38
+
39
+ # The client ID of the application, as developer.tesla.com issued it
40
+ # @api public
41
+ # @return [String, nil] the client ID
42
+ # @example Get or set the client ID
43
+ # client.client_id = "81527cff06843c8634fdc09e8ac0abef"
44
+ attr_accessor :client_id
45
+
46
+ # The client secret of the application, as developer.tesla.com issued it
47
+ # @api public
48
+ # @return [String, nil] the client secret
49
+ # @example Get or set the client secret
50
+ # client.client_secret = "ta-secret.7vx6VkVvk2gBgSrN"
51
+ attr_accessor :client_secret
52
+
53
+ # The URL the authorization server sends a user back to
54
+ # @api public
55
+ # @return [String, nil] the redirect URI
56
+ # @example Get or set the redirect URI
57
+ # client.redirect_uri = "https://example.com/auth/callback"
58
+ attr_accessor :redirect_uri
59
+
60
+ # Set the audience tokens are issued for
61
+ # @api public
62
+ # @param value [String, nil] the host of the Fleet API the tokens are used with, or nil for the host of the client
63
+ # @return [void]
64
+ # @example Issue tokens for the Fleet API for Europe while requests are sent through a proxy
65
+ # client.audience = Tesla::Configuration::EUROPE_HOST
66
+ attr_writer :audience
67
+
68
+ # The URL a user is sent to in order to authorize the application
69
+ # @api public
70
+ # @return [String] the authorization endpoint
71
+ # @example Get or set the authorization endpoint
72
+ # client.authorization_endpoint = "https://auth.tesla.cn/oauth2/v3/authorize"
73
+ attr_accessor :authorization_endpoint
74
+
75
+ # The URL tokens are issued and refreshed at
76
+ # @api public
77
+ # @return [String] the token endpoint
78
+ # @example Get or set the token endpoint
79
+ # client.token_endpoint = "https://auth.tesla.cn/oauth2/v3/token"
80
+ attr_accessor :token_endpoint
81
+
82
+ # What is called with the token each time the access token is refreshed
83
+ # @api public
84
+ # @return [#call, nil] what is called with the `SimpleOAuth::OAuth2::Token`, or nil to call nothing
85
+ # @example Store the tokens each time they are refreshed
86
+ # client.on_token_refresh = ->(token) { File.write("refresh_token", token.refresh_token) }
87
+ attr_accessor :on_token_refresh
88
+
89
+ # The audience tokens are issued for
90
+ #
91
+ # @api public
92
+ # @return [String] the host of the Fleet API the tokens are used with, which is the host of the client unless
93
+ # another has been assigned
94
+ # @example Get the audience
95
+ # client.audience
96
+ def audience
97
+ @audience || host
98
+ end
99
+
100
+ private
101
+
102
+ # Assign the credentials the client is built with
103
+ #
104
+ # @api private
105
+ # @param credentials [Hash{Symbol => Object}] the credentials, each named as the setting it is assigned to
106
+ # @return [void]
107
+ def initialize_credentials(**credentials)
108
+ @token_mutex = Mutex.new
109
+ credentials.each { |setting, value| public_send(:"#{setting}=", value) }
110
+ end
111
+
112
+ # The authenticator for a request authorized with an access token
113
+ #
114
+ # @api private
115
+ # @param access_token [String, nil] the access token, or nil to send the request without one
116
+ # @return [Authenticator] the authenticator
117
+ def authenticator_for(access_token)
118
+ access_token ? BearerTokenAuthenticator.new(access_token:) : Authenticator.new
119
+ end
120
+
121
+ # Whether the client has what a refresh of its access token takes
122
+ #
123
+ # @api private
124
+ # @return [Boolean] whether the client has a refresh token and a client ID
125
+ def refreshable?
126
+ [refresh_token, client_id].none?(&:nil?)
127
+ end
128
+
129
+ # Run a block while no other thread refreshes the tokens of the client
130
+ #
131
+ # @api private
132
+ # @yield while the tokens are held
133
+ # @return [Object] what the block returned
134
+ def exclusively(&)
135
+ @token_mutex.synchronize(&)
136
+ end
137
+
138
+ # The access token to authorize a request with, refreshed when it is stale
139
+ #
140
+ # The token is refreshed only while it is still the stale one, so that a thread that waited for another to
141
+ # refresh it is given the token that thread was answered with rather than using a refresh token a second time.
142
+ # A client without an access token is given one this way before its first request, since nil is the token it
143
+ # has until then.
144
+ #
145
+ # @api private
146
+ # @param stale [String, nil] the access token that is no longer good, or nil for none
147
+ # @return [String, nil] the access token, or nil when the client has none and cannot refresh
148
+ def renewed_access_token(stale)
149
+ exclusively do
150
+ refresh if refreshable? && access_token.eql?(stale)
151
+ access_token
152
+ end
153
+ end
154
+
155
+ # Ask the token endpoint for a new access token with the refresh token
156
+ #
157
+ # @api private
158
+ # @return [SimpleOAuth::OAuth2::Token] the token
159
+ # @raise [ArgumentError] if the client has no refresh token or no client ID
160
+ # @raise [OAuthError] if the token endpoint turns the request away
161
+ def refresh
162
+ current = refresh_token
163
+ raise ArgumentError, "A refresh_token is required to refresh the access token" if current.nil?
164
+
165
+ token = store_token(request_token(oauth_client.refresh_token_request(refresh_token: current)))
166
+ on_token_refresh&.call(token)
167
+ token
168
+ end
169
+
170
+ # Keep the tokens the token endpoint answered with
171
+ #
172
+ # The refresh token is kept as it was when the response carries none.
173
+ #
174
+ # @api private
175
+ # @param token [SimpleOAuth::OAuth2::Token] the token
176
+ # @return [SimpleOAuth::OAuth2::Token] the token
177
+ def store_token(token)
178
+ @access_token = token.access_token
179
+ @refresh_token = token.refresh_token || refresh_token
180
+ token
181
+ end
182
+
183
+ # The OAuth 2.0 client that builds the requests for the authorization server
184
+ #
185
+ # The client secret is sent in the body of a request, as the token endpoint reads it.
186
+ #
187
+ # @api private
188
+ # @return [SimpleOAuth::OAuth2::Client] the OAuth 2.0 client
189
+ # @raise [ArgumentError] if the client has no client ID
190
+ def oauth_client
191
+ id = client_id
192
+ raise ArgumentError, "A client_id is required to ask the authorization server for a token" if id.nil?
193
+
194
+ SimpleOAuth::OAuth2::Client.new(client_id: id, client_secret:, authorization_endpoint:, token_endpoint:,
195
+ auth_method: :client_secret_post)
196
+ end
197
+
198
+ # Send a request to the token endpoint, and read the token it answers with
199
+ #
200
+ # @api private
201
+ # @param oauth_request [SimpleOAuth::OAuth2::Request] the request, as simple_oauth built it
202
+ # @return [SimpleOAuth::OAuth2::Token] the token
203
+ # @raise [OAuthError] if the token endpoint turns the request away, or answers without a token
204
+ # @raise [NetworkError] if the request is lost to the network
205
+ def request_token(oauth_request)
206
+ response = perform_token_request(oauth_request)
207
+ SimpleOAuth::OAuth2::Token.from_response(status: response.code, body: response.body)
208
+ rescue SimpleOAuth::OAuth2::Error => e
209
+ raise OAuthError.new(code: e.code, description: e.description, status: e.status)
210
+ end
211
+
212
+ # Send a request to the token endpoint
213
+ #
214
+ # A request a rate limiter turned away is sent again, as any request is, but one that went unanswered is not,
215
+ # since the token endpoint may have used the refresh token it carried.
216
+ #
217
+ # @api private
218
+ # @param oauth_request [SimpleOAuth::OAuth2::Request] the request, as simple_oauth built it
219
+ # @return [Net::HTTPResponse] the response
220
+ # @raise [NetworkError] if the request is lost to the network
221
+ def perform_token_request(oauth_request)
222
+ uri = URI(oauth_request.url)
223
+ body = oauth_request.body
224
+ headers = oauth_request.headers
225
+ retry_handler.handle(retry_unanswered: false) do
226
+ connection.perform(request: request_builder.build(http_method: :post, uri:, body:, headers:))
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "resource"
4
+
5
+ module Tesla
6
+ # The state of the climate control of a vehicle
7
+ # @api public
8
+ class ClimateState < Resource
9
+ inspect_with :inside_temp, :driver_temp_setting
10
+
11
+ # @!method inside_temp
12
+ # The temperature inside the vehicle in degrees Celsius
13
+ # @api public
14
+ # @return [Float, nil] the temperature inside the vehicle
15
+ # @example
16
+ # climate_state.inside_temp
17
+ attribute :inside_temp
18
+
19
+ # @!method outside_temp
20
+ # The temperature outside the vehicle in degrees Celsius
21
+ # @api public
22
+ # @return [Float, nil] the temperature outside the vehicle
23
+ # @example
24
+ # climate_state.outside_temp
25
+ attribute :outside_temp
26
+
27
+ # @!method driver_temp_setting
28
+ # The temperature the driver's side is set to in degrees Celsius
29
+ # @api public
30
+ # @return [Float, nil] the temperature the driver's side is set to
31
+ # @example
32
+ # climate_state.driver_temp_setting
33
+ attribute :driver_temp_setting
34
+
35
+ # @!method passenger_temp_setting
36
+ # The temperature the passenger's side is set to in degrees Celsius
37
+ # @api public
38
+ # @return [Float, nil] the temperature the passenger's side is set to
39
+ # @example
40
+ # climate_state.passenger_temp_setting
41
+ attribute :passenger_temp_setting
42
+
43
+ # @!method fan_status
44
+ # The speed of the fan
45
+ # @api public
46
+ # @return [Integer, nil] the speed of the fan
47
+ # @example
48
+ # climate_state.fan_status
49
+ attribute :fan_status
50
+
51
+ # @!method climate_keeper_mode
52
+ # The Climate Keeper mode, such as "off", "dog", or "camp"
53
+ # @api public
54
+ # @return [String, nil] the Climate Keeper mode
55
+ # @example
56
+ # climate_state.climate_keeper_mode
57
+ attribute :climate_keeper_mode
58
+
59
+ # @!method seat_heater_left
60
+ # The level of the heater of the front left seat, from 0 to 3
61
+ # @api public
62
+ # @return [Integer, nil] the level of the heater of the front left seat
63
+ # @example
64
+ # climate_state.seat_heater_left
65
+ attribute :seat_heater_left
66
+
67
+ # @!method seat_heater_right
68
+ # The level of the heater of the front right seat, from 0 to 3
69
+ # @api public
70
+ # @return [Integer, nil] the level of the heater of the front right seat
71
+ # @example
72
+ # climate_state.seat_heater_right
73
+ attribute :seat_heater_right
74
+
75
+ # @!method climate_on?
76
+ # Whether the climate control is on
77
+ # @api public
78
+ # @return [Boolean] whether the climate control is on
79
+ # @example
80
+ # climate_state.climate_on?
81
+ predicate :climate_on, :is_climate_on
82
+
83
+ # @!method preconditioning?
84
+ # Whether the vehicle is preconditioning
85
+ # @api public
86
+ # @return [Boolean] whether the vehicle is preconditioning
87
+ # @example
88
+ # climate_state.preconditioning?
89
+ predicate :preconditioning, :is_preconditioning
90
+
91
+ # @!method front_defroster_on?
92
+ # Whether the front defroster is on
93
+ # @api public
94
+ # @return [Boolean] whether the front defroster is on
95
+ # @example
96
+ # climate_state.front_defroster_on?
97
+ predicate :front_defroster_on, :is_front_defroster_on
98
+
99
+ # @!method rear_defroster_on?
100
+ # Whether the rear defroster is on
101
+ # @api public
102
+ # @return [Boolean] whether the rear defroster is on
103
+ # @example
104
+ # climate_state.rear_defroster_on?
105
+ predicate :rear_defroster_on, :is_rear_defroster_on
106
+
107
+ # @!method timestamp
108
+ # The time the vehicle reported the state
109
+ # @api public
110
+ # @return [Time, nil] the time the vehicle reported the state
111
+ # @raise [InvalidResponse] if the timestamp is not a number of milliseconds
112
+ # @example
113
+ # climate_state.timestamp
114
+ time_attribute :timestamp
115
+ end
116
+ end