mirror of
https://github.com/netfun2000/hipudding-teslamate.git
synced 2026-02-27 09:44:28 +08:00
Use Tesla client with custom auth middleware
This commit is contained in:
+6
-78
@@ -1,82 +1,10 @@
|
||||
defmodule TeslaApi do
|
||||
alias Finch.Response, as: Res
|
||||
alias TeslaMate.HTTP
|
||||
alias __MODULE__.Error
|
||||
use Tesla
|
||||
|
||||
@base_url URI.parse("https://owner-api.teslamotors.com/")
|
||||
@user_agent "github.com/adriankumpf/teslamate"
|
||||
@timeout 30_000
|
||||
adapter Tesla.Adapter.Finch, name: TeslaMate.HTTP, receive_timeout: 35_000
|
||||
|
||||
def get(path, token, opts \\ []) when is_binary(token) do
|
||||
headers = [{"user-agent", @user_agent}, {"Authorization", "Bearer " <> token}]
|
||||
|
||||
case HTTP.get(url(path), headers: headers, receive_timeout: @timeout) do
|
||||
{:ok, %Res{} = response} ->
|
||||
case decode_body(response) do
|
||||
%Res{status: status, body: %{"response" => res}} when status in 200..299 ->
|
||||
transform = Keyword.get(opts, :transform, & &1)
|
||||
{:ok, if(is_list(res), do: Enum.map(res, transform), else: transform.(res))}
|
||||
|
||||
%Res{status: 401} = env ->
|
||||
{:error, %Error{reason: :unauthorized, env: env}}
|
||||
|
||||
%Res{status: 404, body: %{"error" => "not_found"}} = env ->
|
||||
{:error, %Error{reason: :vehicle_not_found, env: env}}
|
||||
|
||||
%Res{status: 405, body: %{"error" => "vehicle is curently in service"}} = env ->
|
||||
{:error, %Error{reason: :vehicle_in_service, env: env}}
|
||||
|
||||
%Res{status: 408, body: %{"error" => "vehicle unavailable:" <> _}} = env ->
|
||||
{:error, %Error{reason: :vehicle_unavailable, env: env}}
|
||||
|
||||
%Res{status: 504} = env ->
|
||||
{:error, %Error{reason: :timeout, env: env}}
|
||||
|
||||
%Res{status: status, body: %{"error" => msg}} = env when status >= 500 ->
|
||||
{:error, %Error{reason: :unknown, message: msg, env: env}}
|
||||
|
||||
%Res{body: body} = env ->
|
||||
{:error, %Error{reason: :unknown, message: inspect(body), env: env}}
|
||||
end
|
||||
|
||||
{:error, %Mint.TransportError{reason: reason} = transport_error} ->
|
||||
{:error, %Error{reason: reason, message: Exception.message(transport_error)}}
|
||||
|
||||
{:error, %Mint.HTTPError{reason: reason} = transport_error} ->
|
||||
{:error, %Error{reason: reason, message: Exception.message(transport_error)}}
|
||||
end
|
||||
end
|
||||
|
||||
def post(path, token, params) when is_map(params) do
|
||||
body = Jason.encode!(params)
|
||||
|
||||
headers = [
|
||||
{"user-agent", @user_agent},
|
||||
{"content-type", "application/json"}
|
||||
| if(is_nil(token), do: [], else: [{"Authorization", "Bearer " <> token}])
|
||||
]
|
||||
|
||||
with {:ok, response} <-
|
||||
HTTP.post(url(path), body, headers: headers, receive_timeout: @timeout) do
|
||||
{:ok, decode_body(response)}
|
||||
end
|
||||
end
|
||||
|
||||
## Private
|
||||
|
||||
defp url(path) do
|
||||
@base_url
|
||||
|> Map.put(:path, path)
|
||||
|> URI.to_string()
|
||||
end
|
||||
|
||||
defp decode_body(%Res{body: body} = response) do
|
||||
body =
|
||||
case Jason.decode(body) do
|
||||
{:ok, decoded_body} -> decoded_body
|
||||
{:error, _reason} -> body
|
||||
end
|
||||
|
||||
Map.put(response, :body, body)
|
||||
end
|
||||
plug Tesla.Middleware.BaseUrl, "https://owner-api.teslamotors.com"
|
||||
plug Tesla.Middleware.Headers, [{"user-agent", "github.com/adriankumpf/teslamate"}]
|
||||
plug TeslaApi.Auth.Middleware
|
||||
plug Tesla.Middleware.JSON
|
||||
end
|
||||
|
||||
+23
-21
@@ -1,6 +1,4 @@
|
||||
defmodule TeslaApi.Auth do
|
||||
import TeslaApi
|
||||
|
||||
require Logger
|
||||
|
||||
alias TeslaApi.{Auth, Error}
|
||||
@@ -23,40 +21,44 @@ defmodule TeslaApi.Auth do
|
||||
end
|
||||
|
||||
def legacy_login(email, password) do
|
||||
post("/oauth/token", nil, %{
|
||||
"grant_type" => "password",
|
||||
"client_id" => @client_id,
|
||||
"client_secret" => @client_secret,
|
||||
"email" => email,
|
||||
"password" => password
|
||||
})
|
||||
data = %{
|
||||
grant_type: "password",
|
||||
client_id: @client_id,
|
||||
client_secret: @client_secret,
|
||||
email: email,
|
||||
password: password
|
||||
}
|
||||
|
||||
TeslaApi.post("/oauth/token", data)
|
||||
|> handle_response()
|
||||
end
|
||||
|
||||
def refresh(%Auth{token: token, refresh_token: refresh_token}) do
|
||||
post("/oauth/token", token, %{
|
||||
"grant_type" => "refresh_token",
|
||||
"client_id" => @client_id,
|
||||
"client_secret" => @client_secret,
|
||||
"refresh_token" => refresh_token
|
||||
})
|
||||
data = %{
|
||||
grant_type: "refresh_token",
|
||||
client_id: @client_id,
|
||||
client_secret: @client_secret,
|
||||
refresh_token: refresh_token
|
||||
}
|
||||
|
||||
TeslaApi.post("/oauth/token", data, opts: [access_token: token])
|
||||
|> handle_response()
|
||||
end
|
||||
|
||||
def revoke(%Auth{token: token}) do
|
||||
post("/oauth/revoke", token, %{"token" => token})
|
||||
TeslaApi.post("/oauth/revoke", %{token: token}, opts: [access_token: token])
|
||||
|> handle_response()
|
||||
end
|
||||
|
||||
defp handle_response(response) do
|
||||
case response do
|
||||
{:ok, %Finch.Response{status: 200, body: body}} when body == %{} ->
|
||||
{:ok, %Tesla.Env{status: 200, body: body}} when body == %{} ->
|
||||
:ok
|
||||
|
||||
{:ok, %Finch.Response{status: 200, body: %{"response" => true}}} ->
|
||||
{:ok, %Tesla.Env{status: 200, body: %{"response" => true}}} ->
|
||||
:ok
|
||||
|
||||
{:ok, %Finch.Response{status: 200, body: body}} when is_map(body) ->
|
||||
{:ok, %Tesla.Env{status: 200, body: body}} when is_map(body) ->
|
||||
auth = %__MODULE__{
|
||||
token: body["access_token"],
|
||||
type: body["token_type"],
|
||||
@@ -67,7 +69,7 @@ defmodule TeslaApi.Auth do
|
||||
|
||||
{:ok, auth}
|
||||
|
||||
{:ok, %Finch.Response{status: 401} = e} ->
|
||||
{:ok, %Tesla.Env{status: 401} = e} ->
|
||||
error = %Error{
|
||||
reason: :authentication_failure,
|
||||
message: "Failed to authenticate.",
|
||||
@@ -76,7 +78,7 @@ defmodule TeslaApi.Auth do
|
||||
|
||||
{:error, error}
|
||||
|
||||
{:ok, %Finch.Response{} = e} ->
|
||||
{:ok, %Tesla.Env{} = e} ->
|
||||
{:error, %Error{reason: :unknown, message: "An unknown error has occurred.", env: e}}
|
||||
|
||||
{:error, %{reason: reason} = e} ->
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
defmodule TeslaApi.Auth.Middleware do
|
||||
@behaviour Tesla.Middleware
|
||||
|
||||
@impl Tesla.Middleware
|
||||
def call(%Tesla.Env{} = env, next, _opts) do
|
||||
env =
|
||||
case env.opts[:access_token] do
|
||||
nil -> env
|
||||
token -> Tesla.put_header(env, "Authorization", "Bearer " <> token)
|
||||
end
|
||||
|
||||
Tesla.run(env, next)
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
defmodule TeslaApi.Vehicle do
|
||||
alias __MODULE__.State.{Charge, Climate, Drive, VehicleConfig, VehicleState}
|
||||
alias TeslaApi.Auth
|
||||
alias TeslaApi.{Auth, Error}
|
||||
|
||||
defstruct id: nil,
|
||||
vehicle_id: nil,
|
||||
@@ -23,15 +23,18 @@ defmodule TeslaApi.Vehicle do
|
||||
vehicle_state: nil
|
||||
|
||||
def list(%Auth{token: token}) do
|
||||
TeslaApi.get("/api/1/vehicles", token, transform: &result/1)
|
||||
TeslaApi.get("/api/1/vehicles", opts: [access_token: token])
|
||||
|> handle_response(transform: &result/1)
|
||||
end
|
||||
|
||||
def get(%Auth{token: token}, id) do
|
||||
TeslaApi.get("/api/1/vehicles/#{id}", token, transform: &result/1)
|
||||
TeslaApi.get("/api/1/vehicles/#{id}", opts: [access_token: token])
|
||||
|> handle_response(transform: &result/1)
|
||||
end
|
||||
|
||||
def get_with_state(%Auth{token: token}, id) do
|
||||
TeslaApi.get("/api/1/vehicles/#{id}/vehicle_data", token, transform: &result/1)
|
||||
TeslaApi.get("/api/1/vehicles/#{id}/vehicle_data", opts: [access_token: token])
|
||||
|> handle_response(transform: &result/1)
|
||||
end
|
||||
|
||||
def result(v) do
|
||||
@@ -56,4 +59,41 @@ defmodule TeslaApi.Vehicle do
|
||||
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, if(is_list(res), do: Enum.map(res, transform), else: 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 curently 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: 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
|
||||
|
||||
Reference in New Issue
Block a user