Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: use product api endpoint to fetch vehicles #3630

Conversation

JakobLichterfeld
Copy link
Collaborator

@JakobLichterfeld JakobLichterfeld added WIP Work in progress area:tesla api Related to the Tesla API labels Jan 23, 2024
Copy link

netlify bot commented Jan 23, 2024

Deploy Preview for teslamate ready!

Name Link
🔨 Latest commit 7c8763a
🔍 Latest deploy log https://app.netlify.com/sites/teslamate/deploys/65b3c00eebd86600084ef347
😎 Deploy Preview https://deploy-preview-3630--teslamate.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@brianmay
Copy link
Collaborator

The concern I have with this is if you have other Tesla products, e.g. solar, they may get returned by the new API, and this could cause endless confusion if we try to process that as a car. If so, we might need some way of filtering the list returned so it only contains cars.

Unfortunately, not something I can test.

@JakobLichterfeld
Copy link
Collaborator Author

The concern I have with this is if you have other Tesla products, e.g. solar, they may get returned by the new API, and this could cause endless confusion if we try to process that as a car. If so, we might need some way of filtering the list returned so it only contains cars.

100% that's why it's WIP, only wanted to test if we think in the right direction in general.

@JakobLichterfeld JakobLichterfeld marked this pull request as draft January 24, 2024 08:00
@weljajoh
Copy link

The concern I have with this is if you have other Tesla products, e.g. solar, they may get returned by the new API, and this could cause endless confusion if we try to process that as a car. If so, we might need some way of filtering the list returned so it only contains cars.

100% that's why it's WIP, only wanted to test if we think in the right direction in general.

Testing for the presence of the vin and vehicle_id fields in the product_list response might be a good way to filter vehicles from the product list in the presence of powerwalls or solar. See the bottom part of tdorssers/TeslaPy#156 (comment)

@lotharbach
Copy link

Testing for the presence of the vin and vehicle_id fields in the product_list response might be a good way to filter vehicles from the product list in the presence of powerwalls or solar.

Agreed! I am somewhat sure the Tesla App checks primarily for "vehicle_id" to determine if a product from api/1/products should be tried to be loaded as a vehicle.

From decompiled apk, shortened for readability:

                        if (jSONObject != null && jSONObject.has("vehicle_id")) {
                            jSONObject.put("type", "VEHICLE");
                            Product fromJson = jx.a.f50357a.b().fromJson(jSONObject.toString());
                            arrayList.add((Vehicle) fromJson);

@brianmay
Copy link
Collaborator

brianmay commented Jan 25, 2024

I think the solution would be something like:

diff --git a/lib/tesla_api/vehicle.ex b/lib/tesla_api/vehicle.ex
index 9c25743d..c39c475b 100644
--- a/lib/tesla_api/vehicle.ex
+++ b/lib/tesla_api/vehicle.ex
@@ -30,7 +30,7 @@ defmodule TeslaApi.Vehicle do
       end

     TeslaApi.get(endpoint_url <> "/api/1/products", opts: [access_token: auth.token])
-    |> handle_response(transform: &result/1)
+    |> handle_response(transform: &list_result/1)
   end

   def get(%Auth{} = auth, id) do
@@ -61,6 +61,12 @@ defmodule TeslaApi.Vehicle do
     |> 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"],
@@ -88,7 +94,7 @@ defmodule TeslaApi.Vehicle 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))}
+        {:ok, transform.(res)}

       %Tesla.Env{status: 401} = env ->
         {:error, %Error{reason: :unauthorized, env: env}}

This is a bit more invasive then I originally imagined, because originally the transform function was called for every element in the list, but this means we can't filter out list elements. So I changed the transform function to operate on the entire result. I think it should only get a list for the list API call, so this should be OK.

We could also do the filtering after the handle_response returns, but this is after we processed the result, which seems kind of yuck.

@brianmay
Copy link
Collaborator

brianmay commented Jan 25, 2024

I have now tested this, I am not seeing any errors. Does that mean it is OK? ;-)

@brianmay
Copy link
Collaborator

It looks like this issue will only affect new installs, old installs will fall back to using the data from the database. But of course, this will be a problem if you have added/removed any vehicles.

Jan 26 09:47:34 iot2 teslamate[94614]: 2024-01-26 09:47:34.310 [warning] TeslaApi.Error / %{"error" => "Endpoint is only available on fleetapi. Visit https://developer.tesla.com/docs for more info", "error_description" => "", "response" => nil}
Jan 26 09:47:34 iot2 teslamate[94614]: 2024-01-26 09:47:34.310 [warning] Could not get vehicles: :unknown
Jan 26 09:47:34 iot2 teslamate[94614]: 2024-01-26 09:47:34.314 [warning] Using fallback vehicles:
Jan 26 09:47:34 iot2 teslamate[94614]:
Jan 26 09:47:34 iot2 teslamate[94614]: [
Jan 26 09:47:34 iot2 teslamate[94614]:   %TeslaApi.Vehicle{
Jan 26 09:47:34 iot2 teslamate[94614]:     id: <censored>,
Jan 26 09:47:34 iot2 teslamate[94614]:     vehicle_id: <censored>,
Jan 26 09:47:34 iot2 teslamate[94614]:     vin: <censored>,
Jan 26 09:47:34 iot2 teslamate[94614]:     tokens: [],
Jan 26 09:47:34 iot2 teslamate[94614]:     state: "unknown",
Jan 26 09:47:34 iot2 teslamate[94614]:     option_codes: [],
Jan 26 09:47:34 iot2 teslamate[94614]:     in_service: false,
Jan 26 09:47:34 iot2 teslamate[94614]:     display_name: nil,
Jan 26 09:47:34 iot2 teslamate[94614]:     color: nil,
Jan 26 09:47:34 iot2 teslamate[94614]:     calendar_enabled: nil,
Jan 26 09:47:34 iot2 teslamate[94614]:     backseat_token: nil,
Jan 26 09:47:34 iot2 teslamate[94614]:     backseat_token_updated_at: nil,
Jan 26 09:47:34 iot2 teslamate[94614]:     api_version: nil,
Jan 26 09:47:34 iot2 teslamate[94614]:     charge_state: nil,
Jan 26 09:47:34 iot2 teslamate[94614]:     climate_state: nil,
Jan 26 09:47:34 iot2 teslamate[94614]:     drive_state: nil,
Jan 26 09:47:34 iot2 teslamate[94614]:     gui_settings: nil,
Jan 26 09:47:34 iot2 teslamate[94614]:     vehicle_config: nil,
Jan 26 09:47:34 iot2 teslamate[94614]:     vehicle_state: nil
Jan 26 09:47:34 iot2 teslamate[94614]:   }
Jan 26 09:47:34 iot2 teslamate[94614]: ]

jankratochvil added a commit to jankratochvil/tesla-api that referenced this pull request Jan 26, 2024
 - teslamate-org/teslamate#3630

Error - Tesla API said: '412 Precondition Failed' at .../Tesla/API.pm line 713.
Not an ARRAY reference at .../Tesla/Vehicle.pm line 104.
@virtualm2000
Copy link
Contributor

I think the solution would be something like:

diff --git a/lib/tesla_api/vehicle.ex b/lib/tesla_api/vehicle.ex
index 9c25743d..c39c475b 100644
--- a/lib/tesla_api/vehicle.ex
+++ b/lib/tesla_api/vehicle.ex
@@ -30,7 +30,7 @@ defmodule TeslaApi.Vehicle do
       end

     TeslaApi.get(endpoint_url <> "/api/1/products", opts: [access_token: auth.token])
-    |> handle_response(transform: &result/1)
+    |> handle_response(transform: &list_result/1)
   end

   def get(%Auth{} = auth, id) do
@@ -61,6 +61,12 @@ defmodule TeslaApi.Vehicle do
     |> 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"],
@@ -88,7 +94,7 @@ defmodule TeslaApi.Vehicle 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))}
+        {:ok, transform.(res)}

       %Tesla.Env{status: 401} = env ->
         {:error, %Error{reason: :unauthorized, env: env}}

This is a bit more invasive then I originally imagined, because originally the transform function was called for every element in the list, but this means we can't filter out list elements. So I changed the transform function to operate on the entire result. I think it should only get a list for the list API call, so this should be OK.

We could also do the filtering after the handle_response returns, but this is after we processed the result, which seems kind of yuck.

Tested and it works. No other products on my account but only cars.

@JakobLichterfeld
Copy link
Collaborator Author

JakobLichterfeld commented Jan 26, 2024

I should have opened a branch in the main repo... Anyway, I included brians suggestion, ready for testing on test machines, DO NOT use on Production nor stop your Production: https://github.com/teslamate-org/teslamate/pkgs/container/teslamate/171474733?tag=pr-3630

So you can replace in your docker-compose.yml

  teslamate:
    image: teslamate/teslamate:latest

with

  teslamate:
    image: ghcr.io/teslamate-org/teslamate:pr-3630

To test it once the GitHub runner is completed. Edit: build successful.

@tb205gti
Copy link

This fixed my drives, did a quick'n'dirty edit as according to the PR and the last three drives where complete. I do not own other Tesla products, so cannot say if this brings adverse side effects if you have more products than cars.

@jolcese
Copy link

jolcese commented Jan 31, 2024

I do have a wall charger and vehicle. Tested this on a secondary install and works fine!

@shivaagarwal1
Copy link

Is there any way I can use this fix on Home Assistant Teslamate Add On?

https://github.com/matt-FFFFFF/hassio-addon-teslamate

@JakobLichterfeld
Copy link
Collaborator Author

I do have a wall charger and vehicle. Tested this on a secondary install and works fine!

Perfect, thanks for testing 🙏🏻

@ramonsmits
Copy link
Contributor

FYI: Running this PR image since yesterday without issues. I only have a Tesla car.

@JakobLichterfeld JakobLichterfeld marked this pull request as ready for review February 2, 2024 08:09
@JakobLichterfeld JakobLichterfeld merged commit a09a7fa into teslamate-org:master Feb 2, 2024
11 checks passed
@bikeymouse
Copy link

Hi,

I upgraded to 1.28.3 and things seem to be working fine, however I get warnings/errors ("car_id=1 [error] Error / :unknown") in my log relating to the vehicle endpoint https://owner-api.teslamotors.com/api/1/vehicles/

I would expect everything would. be using the product endpoint now, is that correct?

2024-02-04 12:29:32.826 [info] Migrations already up
2024-02-04 12:29:34.998 [info] System Info: Erlang/OTP 26 (jit)
2024-02-04 12:29:34.998 [info] Version: 1.28.3
2024-02-04 12:29:35.637 [info] POST https://auth.tesla.com/oauth2/v3/token -> 200 (592.185 ms)
2024-02-04 12:29:35.637 [info] Refreshed api tokens
2024-02-04 12:29:35.641 [info] Scheduling token refresh in 6 h
2024-02-04 12:29:35.648 [info] Running TeslaMateWeb.Endpoint with cowboy 2.10.0 at :::4000 (http)
2024-02-04 12:29:35.649 [info] Access TeslaMateWeb.Endpoint at http://localhost
2024-02-04 12:29:36.045 [info] Starting logger for 'Anonimized'
2024-02-04 12:29:36.068 [info] MQTT connection has been established
2024-02-04 12:29:36.358 car_id=1 [info] Start / :online
2024-02-04 12:29:36.393 car_id=1 [info] Connecting ...
2024-02-04 12:30:15.475 [info] GET https://owner-api.teslamotors.com/api/1/vehicles/149293xxxx/vehicle_data -> 408 (9079.515 ms)
2024-02-04 12:30:15.475 [warning] TeslaApi.Error / %{"error" => "{\"error\": \"timeout\"}", "error_description" => "", "response" => nil}
2024-02-04 12:30:15.476 car_id=1 [error] Error / :unknown
2024-02-04 12:32:21.182 [info] GET https://owner-api.teslamotors.com/api/1/vehicles/1492931211761304/vehicle_data -> 408 (9073.748 ms)
2024-02-04 12:32:21.182 [warning] TeslaApi.Error / %{"error" => "{\"error\": \"timeout\"}", "error_description" => "", "response" => nil}
2024-02-04 12:32:21.182 car_id=1 [error] Error / :unknown
2024-02-04 12:32:48.632 car_id=1 [info] Suspending logging
2024-02-04 12:43:05.971 car_id=1 [info] Fetching vehicle state ...
2024-02-04 12:43:06.090 car_id=1 [info] Start / :asleep
2024-02-04 12:43:06.095 car_id=1 [info] Disconnecting ...

FYI
I really removed all Teslamate containers and images re-downloading and recreating them with Docker-compose.
Only the database-volume was re-used. Both this log and the UI show 1.28.3.

@JakobLichterfeld
Copy link
Collaborator Author

JakobLichterfeld commented Feb 4, 2024

I would expect everything would. be using the product endpoint now, is that correct?

no

you are getting 408 errors, which means to many request

@bikeymouse
Copy link

Yes you are right about the 408. But still the https://owner-api.teslamotors.com/api/1/vehicles endpoint is used I thought that could no longer be used, or is that not correct?

@JakobLichterfeld
Copy link
Collaborator Author

JakobLichterfeld commented Feb 4, 2024

I thought that could no longer be used, or is that not correct?

Please stop asking these kinds of questions or access will really be restricted. TL;DR: Currently everything works fine with TeslaMate and other loggers even without using dev accounts.

@teslamate-org teslamate-org locked as resolved and limited conversation to collaborators Feb 4, 2024
@brianmay
Copy link
Collaborator

brianmay commented Feb 4, 2024

We need top be careful here, we want to ensure that that we are a welcoming community, and are open to feedback from everyone.

@bikeymouse The API call used above is https://owner-api.teslamotors.com/api/1/vehicles/1492931211761304/vehicle_data which is different from the https://owner-api.teslamotors.com/api/1/vehicles API call that we were forced to change.

The 408 error is the generic error that means your car is unreachable, e.g. maybe because it is asleep, or because it is outside network coverage, or something like that.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area:tesla api Related to the Tesla API WIP Work in progress
Projects
None yet
Development

Successfully merging this pull request may close these issues.