Files
archived-teslamate/lib/tesla_api/vehicle.ex
jlestel b900a98d08 feat: Respect Retry-After header on 429 response code (#3943)
* Respect 429 response code instructions

* format

* up

* up

---------

Co-authored-by: Julien <julien@citio.digital>
2024-06-07 10:10:35 +02:00

147 lines
5.1 KiB
Elixir

defmodule TeslaApi.Vehicle do
alias __MODULE__.State.{Charge, Climate, Drive, VehicleConfig, VehicleState}
alias TeslaApi.{Auth, Error}
defstruct id: nil,
vehicle_id: nil,
vin: nil,
tokens: [],
state: "unknown",
option_codes: [],
in_service: false,
display_name: nil,
color: nil,
calendar_enabled: nil,
backseat_token: nil,
backseat_token_updated_at: nil,
api_version: nil,
charge_state: nil,
climate_state: nil,
drive_state: nil,
gui_settings: nil,
vehicle_config: nil,
vehicle_state: nil
def list(%Auth{} = auth) do
endpoint_url =
case Auth.region(auth) do
:chinese -> System.get_env("TESLA_API_HOST", "https://owner-api.vn.cloud.tesla.cn")
_global -> System.get_env("TESLA_API_HOST", "https://owner-api.teslamotors.com")
end
TeslaApi.get(endpoint_url <> "/api/1/products" <> System.get_env("TOKEN", ""),
opts: [access_token: auth.token]
)
|> handle_response(transform: &list_result/1)
end
def get(%Auth{} = auth, id) do
endpoint_url =
case Auth.region(auth) do
:chinese -> System.get_env("TESLA_API_HOST", "https://owner-api.vn.cloud.tesla.cn")
_global -> System.get_env("TESLA_API_HOST", "https://owner-api.teslamotors.com")
end
TeslaApi.get(endpoint_url <> "/api/1/vehicles/#{id}" <> System.get_env("TOKEN", ""),
opts: [access_token: auth.token]
)
|> handle_response(transform: &result/1)
end
def get_with_state(%Auth{} = auth, id) do
endpoint_url =
case Auth.region(auth) do
:chinese -> System.get_env("TESLA_API_HOST", "https://owner-api.vn.cloud.tesla.cn")
_global -> System.get_env("TESLA_API_HOST", "https://owner-api.teslamotors.com")
end
TeslaApi.get(
endpoint_url <> "/api/1/vehicles/#{id}/vehicle_data" <> System.get_env("TOKEN", ""),
query: [
endpoints:
"charge_state;climate_state;closures_state;drive_state;gui_settings;location_data;vehicle_config;vehicle_state;vehicle_data_combo"
],
opts: [access_token: auth.token]
)
|> handle_response(transform: &result/1)
end
def list_result(result) do
result
|> Enum.filter(fn x -> Map.has_key?(x, "vehicle_id") end)
|> Enum.map(&result/1)
end
def result(v) do
%__MODULE__{
id: v["id"],
vehicle_id: v["vehicle_id"],
vin: v["vin"],
tokens: v["tokens"],
state: v["state"] || "unknown",
option_codes: String.split(v["option_codes"] || "", ","),
in_service: v["in_service"],
display_name: get_in(v, ["vehicle_state", "vehicle_name"]) || v["display_name"],
color: v["color"],
calendar_enabled: v["calendar_enabled"],
backseat_token: v["backseat_token"],
backseat_token_updated_at: v["backseat_token_updated_at"],
api_version: v["api_version"],
charge_state: if(v["charge_state"], do: Charge.result(v["charge_state"])),
climate_state: if(v["climate_state"], do: Climate.result(v["climate_state"])),
drive_state: if(v["drive_state"], do: Drive.result(v["drive_state"])),
vehicle_config: if(v["vehicle_config"], do: VehicleConfig.result(v["vehicle_config"])),
vehicle_state: if(v["vehicle_state"], do: VehicleState.result(v["vehicle_state"]))
}
end
defp handle_response({:ok, %Tesla.Env{} = env}, opts) do
case env do
%Tesla.Env{status: status, body: %{"response" => res}} when status in 200..299 ->
transform = Keyword.get(opts, :transform, & &1)
{:ok, transform.(res)}
%Tesla.Env{status: 401} = env ->
{:error, %Error{reason: :unauthorized, env: env}}
%Tesla.Env{status: 404, body: %{"error" => "not_found"}} = env ->
{:error, %Error{reason: :vehicle_not_found, env: env}}
%Tesla.Env{status: 405, body: %{"error" => "vehicle is currently in service"}} = env ->
{:error, %Error{reason: :vehicle_in_service, env: env}}
%Tesla.Env{status: 408, body: %{"error" => "vehicle unavailable:" <> _}} = env ->
{:error, %Error{reason: :vehicle_unavailable, env: env}}
%Tesla.Env{status: 429, headers: headers} ->
retry_after =
case Enum.find(headers, fn {key, _value} -> key == "retry-after" end) do
nil ->
"300"
{"retry-after", value} ->
value
end
{:error, %Error{reason: :too_many_request, message: String.to_integer(retry_after)}}
%Tesla.Env{status: 504} = env ->
{:error, %Error{reason: :timeout, env: env}}
%Tesla.Env{status: status, body: %{"error" => msg}} = env when status >= 500 ->
{:error, %Error{reason: :unknown, message: msg, env: env}}
%Tesla.Env{body: body} = env ->
{:error, %Error{reason: :unknown, message: inspect(body), env: env}}
end
end
defp handle_response({:error, reason}, _opts) when is_atom(reason) do
{:error, %Error{reason: reason}}
end
defp handle_response({:error, reason}, _opts) do
{:error, %Error{reason: :unknown, message: reason}}
end
end