This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

Simple HTTP - Tesla API
#1
Hi,

I have Tesla and recently discovered the possibility to use HTTP to send commands and receive status from the car.
Unfortunately I am not so good with scripts/coding.

Hopefully someone here can give me a helping hand.

I wold like to have the spacelynk to send a post the following HTTP url, with this header...

https://owner-api.teslamotors.com/api/1/...ning_start

Header: bearer (string) 987654321

This I would like to incorporate in an event script.

if value == true then
post HTTP
end


Anyone?
BR,
Mr.D
Reply
#2
Hi,

When i have the API documentation i will probably need few minutes to create it. 

I just created a script for our Ecostruxure Building Operations SmartConnector and this also uses a bearer token that you need to request first before you can use the API and that took me 25 minutes.

The link you sended is not working..

Please send the API document and i see what i can do, holidays are comming (:

BR,

Erwin
Reply
#3
Hi Erwin,

I already have the bearer token so basically all I have to do is POST this HTTP command:
"https://owner-api.teslamotors.com/api/1/vehicles/123/command/auto_conditioning_start"
but I need to include the bearer token in the header...

something like this????

curl --request POST \
--url "https://owner-api.teslamotors.com/api/1/vehicles/123/command/auto_conditioning_start \"
--header 'Authorization: Bearer 123

Here is the link to the API
https://www.teslaapi.io/vehicles/commands
BR,
Mr.D
Reply
#4
Hi,

Usually the bearer token is a response on the token request and is valid for x minutes (mostly 60 minutes), so i doubt that 123 is the bearer code (: 

Can you try this?
Code:
-- Load modules
require('json')
require('ssl.https')
require('ltn12')

-- Set credentials for Tesla API
username = "admin"
password = "admin"

function GetToken()
 request_token_url = 'https://owner-api.teslamotors.com/oauth/token'
 local response_body = {}
 local request_body = "grant_type=password&username=" .. username .. "&password=" .. password
 local body, code, hdrs, stat = ssl.https.request{
   url = request_token_url;
   method = "POST";
   headers =
   {
     ["Content-Type"] = "application/x-www-form-urlencoded";
     ["Content-Length"] = #request_body;
   };
   source = ltn12.source.string(request_body);
   sink = ltn12.sink.table(response_body);
 }
 if code == 200 then
   ret = table.concat(response_body)
   ret = json.pdecode(ret)
   return ret.access_token -- could be some other field, try to log ret to be sure
 end
end

-- Request token
if not API_Token then
    API_Token = GetToken()
end

function RequestFromTesla(request)
 request_url = 'https://owner-api.teslamotors.com/api/1/' .. request
 local response_body = {}
 local request_body = ""
 local body, code, hdrs, stat = ssl.https.request{
   url = request_url;
   method = "GET";
   headers =
   {
     ["Content-Type"] = "application/json";
     ["Authorization"] = "Bearer " .. API_Token;
   };
   source = ltn12.source.string(request_body);
   sink = ltn12.sink.table(response_body);
 }
 if code == 200 then
   ret = table.concat(response_body)
   ret = json.pdecode(ret)
   return ret
 else
   API_Token = GetToken() -- request a new token
 end
end

function WriteToTesla(request)
  request_url = 'https://owner-api.teslamotors.com/api/1/' .. request
  local response_body = {}
  local request_body = ""
  local body, code, hdrs, stat = ssl.https.request{
    url = request_url;
    method = "POST";
    headers =
    {
      ["Content-Type"] = "application/json";
      ["Authorization"] = "Bearer " .. API_Token;
    };
    source = ltn12.source.string(request_body);
    sink = ltn12.sink.table(response_body);
  }
  if code == 200 then
    ret = table.concat(response_body)
    ret = json.pdecode(ret)
    return ret
  else 
    API_Token = GetToken() -- request a new token
  end
end

-- Get products
products = RequestFromTesla('products')
log(products) -- Check here your Car ID

CarID = 1 -- products[1].id?

-- Get onboarding data
onboarding_data = RequestFromTesla('users/onboarding_data')
log(onboarding_data)

-- Wake up car
result = WriteToTesla('vehicles/' .. CarID .. '/wake_up')
log(result)
BR,

Erwin
Reply
#5
(13.12.2018, 22:50)Erwin van der Zwart Wrote: Hi,

Usually the bearer token is a response on the token request and is valid for x minutes (mostly 60 minutes), so i doubt that 123 is the bearer code (: 

Can you try this?
Code:
-- Load modules
require('json')
require('ssl.https')
require('ltn12')

-- Set credentials for Tesla API
username = "admin"
password = "admin"

function GetToken()
 request_token_url = 'https://owner-api.teslamotors.com/oauth/token'
 local response_body = {}
 local request_body = "grant_type=password&username=" .. username .. "&password=" .. password
 local body, code, hdrs, stat = ssl.https.request{
   url = request_token_url;
   method = "POST";
   headers =
   {
     ["Content-Type"] = "application/x-www-form-urlencoded";
     ["Content-Length"] = #request_body;
   };
   source = ltn12.source.string(request_body);
   sink = ltn12.sink.table(response_body);
 }
 if code == 200 then
   ret = table.concat(response_body)
   ret = json.pdecode(ret)
   return ret.access_token -- could be some other field, try to log ret to be sure
 end
end

-- Request token
if not API_Token then
    API_Token = GetToken()
end

function RequestFromTesla(request)
 request_url = 'https://owner-api.teslamotors.com/api/1/' .. request
 local response_body = {}
 local request_body = ""
 local body, code, hdrs, stat = ssl.https.request{
   url = request_url;
   method = "GET";
   headers =
   {
     ["Content-Type"] = "application/json";
     ["Authorization"] = "Bearer " .. API_Token;
   };
   source = ltn12.source.string(request_body);
   sink = ltn12.sink.table(response_body);
 }
 if code == 200 then
   ret = table.concat(response_body)
   ret = json.pdecode(ret)
   return ret
 else
   API_Token = GetToken() -- request a new token
 end
end

function WriteToTesla(request)
  request_url = 'https://owner-api.teslamotors.com/api/1/' .. request
  local response_body = {}
  local request_body = ""
  local body, code, hdrs, stat = ssl.https.request{
    url = request_url;
    method = "POST";
    headers =
    {
      ["Content-Type"] = "application/json";
      ["Authorization"] = "Bearer " .. API_Token;
    };
    source = ltn12.source.string(request_body);
    sink = ltn12.sink.table(response_body);
  }
  if code == 200 then
    ret = table.concat(response_body)
    ret = json.pdecode(ret)
    return ret
  else 
    API_Token = GetToken() -- request a new token
  end
end

-- Get products
products = RequestFromTesla('products')
log(products) -- Check here your Car ID

CarID = 1 -- products[1].id?

-- Get onboarding data
onboarding_data = RequestFromTesla('users/onboarding_data')
log(onboarding_data)

-- Wake up car
result = WriteToTesla('vehicles/' .. CarID .. '/wake_up')
log(result)
BR,

Erwin

Hi,

If I understand your script correctly this will get me the bearer token? and then some?
I already have the token, and you are correct, it is not 123. I just wrote that because I did not want to expose the token. Doing so, the token together with the Vehicle ID/Car ID I would give everyone access to my car Smile
In terms of how long the token last, a Tesla token lasts for 30 days before it needs to be refreshed.

I already have the Vehicle ID, which is part of the URL.
What I need is to understand how I send a POST request that includes the URL but also the bearer token... I do not know how to add the bearer token as part of my URL request...


But I see your script is way more advanced, in fear of asking too much could you explain what your script does? in terms of the different functions? I see that storing and using the response from the car adds some further potentials in terms of adding "features" to my smarthouse/tesla Smile
BR,
Mr.D
Reply
#6
Hi,

The function GetToken() does a request to the API with your credentials and returns the Bearer Token so you don't have to look it up all the time. Its returned in the table 'ret' inside de function, i think the field is access_token but you need to check that by adding log(ret) in the function to see what fields are returned. the variable API_Token should be automatically filled with a valid token and will be used in the further API requests. (is already include in the other functions as Authorization header so you dont need to change anything)

The function RequestFromTesla(request) should be used for all GET requests and the static part of the URL is already included to the function so you only have to pass the latest part of the URL as request variable so RequestFromTesla("products") will actually request the URL https://owner-api.teslamotors.com/api/1/products and returns the response as a table where all your Tesla product should be listed. I think the ID is also a field in this returned table that you could use in next requests as variable as 'product.id'

The function WriteToTesla(request) should be used for all POST commands and here is also the static part of the URL already included to the function, here you also only need to pass the latest part of the URL as request variable and returns the response as table (probably returns 'success' or something)

So with these 3 functions you should be able to request the token, request data and write data from/to the vehicle.

Basically you only need to set your username & passwords and use the 2 functions RequestFromTesla(request) + WriteToTesla(request) to exchange data with your car. 

Succes!

BR,

Erwin
Reply
#7
Hi,

In terms of the Token... this only last for 45 days. The script you made, will this ask for a new token every time it is run?

For some commands they require and extra "field/input"...
Like the Start Drive Command, this one requires a password as well
https://www.teslaapi.io/vehicles/commands

How do I incorporate this into the script?

I also made som changes to the script, for anyone that plans to do this, I can confirm this works:
PS: you must run the script once, so you can get your vehicle ID. Look in the logs and replace the 123 for CarID

Code:
-- Load modules
require('json')
require('ssl.https')
require('ltn12')

-- Set credentials for Tesla API
username = "abc" -- your email for tesla account
password = "123" -- your password for tesla account
password1 = "password" -- default do not change
client_id = "81527cff06843c8634fdc09e8ac0abefb46ac849f38fe1e431c2ef2106796384" -- default, do not change unless Tesla makes changes
client_secret = "c7257eb71a564034f9419ee651c7d0e5f7aa6bfbd18bafb5c5c033b093bb2fa3" -- default, do not change unless Tesla makes changes

function GetToken()
 request_token_url = 'https://owner-api.teslamotors.com/oauth/token'
 local response_body = {}
 local request_body = "grant_type=" .. password1 .. "&client_id=" .. client_id .. "&client_secret=" .. client_secret .. "&email=" .. username .. "&password=" .. password
 local body, code, hdrs, stat = ssl.https.request{
   url = request_token_url;
   method = "POST";
   headers =
   {
     ["Content-Type"] = "application/x-www-form-urlencoded";
     ["Content-Length"] = #request_body;
   };
   source = ltn12.source.string(request_body);
   sink = ltn12.sink.table(response_body);
 }
 if code == 200 then
   ret = table.concat(response_body)
   ret = json.pdecode(ret)
   return ret.access_token
 end
end

-- Request token
if not API_Token then
    API_Token = GetToken()
end

function RequestFromTesla(request)
 request_url = 'https://owner-api.teslamotors.com/api/1/' .. request
 local response_body = {}
 local request_body = ""
 local body, code, hdrs, stat = ssl.https.request{
   url = request_url;
   method = "GET";
   headers =
   {
     ["Content-Type"] = "application/json";
     ["Authorization"] = "Bearer " .. API_Token;
   };
   source = ltn12.source.string(request_body);
   sink = ltn12.sink.table(response_body);
 }
 if code == 200 then
   ret = table.concat(response_body)
   ret = json.pdecode(ret)
   return ret
 else
   API_Token = GetToken() -- request a new token
 end
end

function WriteToTesla(request)
  request_url = 'https://owner-api.teslamotors.com/api/1/' .. request
  local response_body = {}
  local request_body = ""
  local body, code, hdrs, stat = ssl.https.request{
    url = request_url;
    method = "POST";
    headers =
    {
      ["Content-Type"] = "application/json";
      ["Authorization"] = "Bearer " .. API_Token;
    };
    source = ltn12.source.string(request_body);
    sink = ltn12.sink.table(response_body);
  }
  if code == 200 then
    ret = table.concat(response_body)
    ret = json.pdecode(ret)
    return ret
  else 
    API_Token = GetToken() -- request a new token
  end
end

-- Get products
products = RequestFromTesla('products')
log(products) -- Check here your Car ID

CarID = 123 -- run the script once, and look in the logs you will find a string called id_s this number is your vehicle ID, this should be replaced with the 123 in this field, remember to add "", then run script again, and you will get it to work!

-- Get onboarding data
onboarding_data = RequestFromTesla('users/onboarding_data')
log(onboarding_data)

-- unlock car
result = WriteToTesla('vehicles/' .. CarID .. '/command/door_unlock')
log(result)
BR,
Mr.D
Reply
#8
Hi,

If you use this script as event based it will request the token every run, if you use it as resident then the token is reqeusted once and when the connection is responding with a error code (other code then 200) then the token is requested again and the script will use this new token until the next error code (other code then 200).

BR,

Erwin
Reply
#9
Anyone got this working? I try to connect to my Tesla but when i execute a RequestFromTesla call it returns always nil.

Before it logs my carid and filled it in the script.

Onboard date returns:
* table:
[response]
* table:
[entry_point_text]
* string: Video Guides
[display_page_on_startup]
* userdata: NULL
[show_in_menu]
* bool: false
[show_on_login]
* bool: false
[show_postdelivery_on_startup]
* bool: false

solved: CarId between ""
Reply
#10
And how to open de trunk or frunc?

You need to specify which trunc but the following code doesn't work:
result = WriteToTesla('vehicles/' .. CarID .. '/command/actuate_trunk?which_trunk=rear')

I read something to specify this in the body, like: {"which_trunk":"rear"}
But how to do this?

See: https://tesla-api.timdorr.com/vehicle/commands/trunk
Reply
#11
Try this:

Code:
function WriteToTesla(request, request_body)
  request_url = 'https://owner-api.teslamotors.com/api/1/' .. request
  local response_body = {}
  local content_length

  if type(request_body) == "string" then
    content_length = #request_body
  else
    request_body = ""
  end

  local body, code, hdrs, stat = ssl.https.request{
    url = request_url;
    method = "POST";
    headers =
    {
      ["Content-Type"] = "application/json";
      ["Authorization"] = "Bearer " .. API_Token;
      ["Content-Length"] = content_length;
    };
    source = ltn12.source.string(request_body);
    sink = ltn12.sink.table(response_body);
  }
  if code == 200 then
    ret = table.concat(response_body)
    ret = json.pdecode(ret)
    return ret
  else
    API_Token = GetToken() -- request a new token
  end
end

result = WriteToTesla('vehicles/' .. CarID .. '/command/actuate_trunk', '{"which_trunk":"rear"}')
Reply
#12
Dear friends,
Please help to create a https.request
How should I build a request_body with next parameters:

Requests are sent to the following endpoint: POST https://api.windy.com/api/point-forecast/v2 with the following body:
{
"lat": 49.809,
"lon": 16.787,
"model": "gfs",
"parameters": ["wind", "dewpoint", "rh", "pressure"],
"levels": ["surface", "800h", "300h"],
"key": "abcd123"
}

Thank you!!
Reply
#13
Use this:
Code:
https = require('ssl.https')

url = 'https://api.windy.com/api/point-forecast/v2'
body = [[{
"lat": 49.809,
"lon": 16.787,
"model": "gfs",
"parameters": ["wind", "dewpoint", "rh", "pressure"],
"levels": ["surface", "800h", "300h"],
"key": "abcd123"
}]]

resp = {}

res, code, hdrs, stat = https.request({
  url = url,
  method = 'POST',
  headers = {
    ['Content-Type'] = 'application/json',
    ['Content-Length'] = #body,
  },
  source = ltn12.source.string(body),
  sink = ltn12.sink.table(resp),
})

resp = table.concat(resp)
log(res, code, hdrs, stat, resp)
Reply
#14
Thank you! Tried some similar way, tried your script, log is:
* arg: 1
* nil
* arg: 2
* string: error:1409442E:lib(20):func(148):reason(1070)
* arg: 3
* nil
* arg: 4
* nil
* arg: 5
* string:
Reply
#15
This is due to older FW that you're using. Change request parameters to this:
Code:
res, code, hdrs, stat = https.request({
  url = url,
  method = 'POST',
  protocol = 'tlsv12',
  headers = {
    ['Content-Type'] = 'application/json',
    ['Content-Length'] = #body,
  },
  source = ltn12.source.string(body),
  sink = ltn12.sink.table(resp),
})
Reply
#16
Thank you!!! Now it works!!!
Reply


Forum Jump: