Posts: 81
Threads: 24
Joined: Sep 2017
Reputation:
0
Tesla recently changed its api to use v3 of the Tesla auth endpoint.
I've seen the V1 example script for logic machine, however the auth process seems a bit more complicated now!
An explanation of the auth process can be found here:
https://tesla-api.timdorr.com/api-basics/authentication and here
https://teslamotorsclub.com/tmc/threads/...st-5308136
I've seen an example script written in Python
https://github.com/fkhera/powerwallCloud...up.py#L132
Is anyone able to help me convert to lua to run on logic machine.
Specifically I'd like to automate a change in mode from 'backup-only' (which forces the Powerwall to charge from the Grid when rates are cheap) to 'self-powered' (which discharges the battery to power our home).
Many thanks in advanced
Kind Regards
James
Posts: 451
Threads: 94
Joined: Jun 2015
Reputation:
6
Posts: 8075
Threads: 43
Joined: Jun 2015
Reputation:
471
If you can provide username/password via PM then I can create an example.
Posts: 81
Threads: 24
Joined: Sep 2017
Reputation:
0
Posts: 8075
Threads: 43
Joined: Jun 2015
Reputation:
471
This should get you a request token and refresh token which can be used for further API calls. But this can stop working anytime if the login page structure changes since this example emulates user interaction with the login page.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
username =
'user@domain.com'
password =
'password'
client_id =
'ID'
client_secret =
'SECRET'
encdec =
require (
'encdec' )
http =
require (
'socket.http' )
mime =
require (
'mime' )
ltn12 =
require (
'ltn12' )
json =
require (
'json' )
function mt ()
local ts ,
tu =
os.microtime ()
return ts ..
'.' ..
tu
end
function b64url (
str )
return mime.b64 (
str ):
gsub (
'.' , {
[
'+' ] =
'-' ,
[
'/' ] =
'_' ,
[
'=' ] =
'' ,
})
end
function encodeargs (
t )
local res = {}
local esc =
require (
'socket.url' ).
escape
for k ,
v in pairs (
t )
do
res [ #
res +
1 ] =
esc (
k ) ..
'=' ..
esc (
v )
end
return table.concat (
res ,
'&' )
end
code_verifier =
encdec.sha512 (
mt ()):
sub (
1 ,
86 )
state =
b64url (
encdec.sha256 (
mt ()):
sub (
1 ,
12 ))
code_challenge =
b64url (
code_verifier )
args =
encodeargs ({
client_id =
'ownerapi' ,
code_challenge =
code_challenge ,
code_challenge_method =
'S256' ,
redirect_uri =
'https://auth.tesla.com/void/callback' ,
response_type =
'code' ,
scope =
'openid email offline_access' ,
state =
state ,
})
url =
'https://auth.tesla.com/oauth2/v3/authorize?' ..
args
res ,
code ,
headers =
http.request (
url )
if not res or code ~=
200 then
log (
'request 1 failed' ,
res ,
code )
return
end
postdata = {}
regexp =
'<input type="hidden" name="([^"]+)" value="([^"]*)"'
for name ,
value in res :
gmatch (
regexp )
do
postdata [
name ] =
value
end
postdata.identity =
username
postdata.credential =
password
cookie =
headers [
'Set-Cookie' ]
or headers [
'set-cookie' ]
or ''
body =
encodeargs (
postdata )
res ,
code ,
headers =
http.request ({
url =
url ,
method =
'POST' ,
source =
ltn12.source.string (
body ),
headers = {
[
'Content-Type' ] =
'application/x-www-form-urlencoded' ,
[
'Content-Length' ] = #
body ,
[
'Cookie' ] =
cookie ,
}
})
if not res or code ~=
302 then
log (
'request 2 failed' ,
res ,
code )
return
end
hdr =
headers.Location or headers.location
resp_code =
hdr :
match (
'code=([^&]+)' )
body =
json.encode ({
grant_type =
'authorization_code' ,
client_id =
'ownerapi' ,
code =
resp_code ,
code_verifier =
code_verifier ,
redirect_uri =
'https://auth.tesla.com/void/callback' ,
})
resp = {}
res ,
code ,
headers =
http.request ({
url =
'https://auth.tesla.com/oauth2/v3/token' ,
method =
'POST' ,
source =
ltn12.source.string (
body ),
sink =
ltn12.sink.table (
resp ),
headers = {
[
'Content-Type' ] =
'application/json' ,
[
'Accept' ] =
'application/json' ,
[
'Content-Length' ] = #
body ,
[
'User-Agent' ] =
'Mozilla/5.0 (Linux; Android 9.0.0; VS985 4G Build/LRX21Y; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36' ,
[
'X-Tesla-User-Agent' ] =
'TeslaApp/3.4.4-350/fad4a582e/android/9.0.0' ,
}
})
if not res or code ~=
200 then
log (
'request 3 failed' ,
res ,
code )
return
end
resp =
table.concat (
resp )
resp =
json.pdecode (
resp )
bearer_token =
resp.access_token
refresh_token =
resp.refresh_token
body =
json.encode ({
grant_type =
'urn:ietf:params:oauth:grant-type:jwt-bearer' ,
client_id =
client_id ,
client_secret =
client_secret ,
})
resp = {}
res ,
code ,
headers =
http.request ({
url =
'https://owner-api.teslamotors.com/oauth/token' ,
method =
'POST' ,
source =
ltn12.source.string (
body ),
sink =
ltn12.sink.table (
resp ),
headers = {
[
'Content-Type' ] =
'application/json' ,
[
'Authorization' ] =
'Bearer ' ..
bearer_token ,
[
'Content-Length' ] = #
body ,
}
})
print (
res ,
code )
if not res or code ~=
200 then
log (
'request 4 failed' ,
res ,
code )
return
end
resp =
table.concat (
resp )
resp =
json.pdecode (
resp )
access_token =
resp.access_token
log (
access_token )
Posts: 81
Threads: 24
Joined: Sep 2017
Reputation:
0
Hi
Many thanks for the example. I've loaded it onto my Schneider SHAC (which runs an older version of the logic machine os) however I get the following error
script:36: attempt to call field 'sha512' (a nil value)
Kind Regards
James
Posts: 8075
Threads: 43
Joined: Jun 2015
Reputation:
471
Try replacing these lines:
Code:
1 2 3
code_verifier =
encdec.sha512 (
mt ()):
sub (
1 ,
86 )
state =
b64url (
encdec.sha256 (
mt ()):
sub (
1 ,
12 ))
code_challenge =
b64url (
code_verifier )
With this:
Code:
1 2 3 4
rnd =
encdec.sha256 (
mt ())
code_verifier = (
rnd ..
rnd ):
sub (
1 ,
86 )
state =
b64url (
rnd :
sub (
1 ,
12 ))
code_challenge =
b64url (
code_verifier )
Posts: 81
Threads: 24
Joined: Sep 2017
Reputation:
0
Hi .. The script progresses a little further with this change, however still errors
Can you suggest what else I should update?
* arg: 1
* string: request 1 failed
* arg: 2
* nil
* arg: 3
* string: error:1409442E:lib(20):func(148):reason(1070)
Kind Regards
James
Posts: 8075
Threads: 43
Joined: Jun 2015
Reputation:
471
This is due to outdated libraries in your firmware. Unfortunately it's not possible to update these libraries separately.
See if this works for you:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
username =
'user@domain.com'
password =
'password'
client_id =
'ID'
client_secret =
'SECRET'
encdec =
require (
'encdec' )
http =
require (
'socket.http' )
mime =
require (
'mime' )
ltn12 =
require (
'ltn12' )
json =
require (
'json' )
function mt ()
local ts ,
tu =
os.microtime ()
return ts ..
'.' ..
tu
end
function b64url (
str )
return mime.b64 (
str ):
gsub (
'.' , {
[
'+' ] =
'-' ,
[
'/' ] =
'_' ,
[
'=' ] =
'' ,
})
end
function encodeargs (
t )
local res = {}
local esc =
require (
'socket.url' ).
escape
for k ,
v in pairs (
t )
do
res [ #
res +
1 ] =
esc (
k ) ..
'=' ..
esc (
v )
end
return table.concat (
res ,
'&' )
end
rnd =
encdec.sha256 (
mt ())
code_verifier = (
rnd ..
rnd ):
sub (
1 ,
86 )
state =
b64url (
rnd :
sub (
1 ,
12 ))
code_challenge =
b64url (
code_verifier )
args =
encodeargs ({
client_id =
'ownerapi' ,
code_challenge =
code_challenge ,
code_challenge_method =
'S256' ,
redirect_uri =
'https://auth.tesla.com/void/callback' ,
response_type =
'code' ,
scope =
'openid email offline_access' ,
state =
state ,
})
resp = {}
url =
'https://auth.tesla.com/oauth2/v3/authorize?' ..
args
res ,
code ,
headers =
http.request ({
url =
url ,
sink =
ltn12.sink.table (
resp ),
protocol =
'tlsv12' ,
})
if not res or code ~=
200 then
log (
'request 1 failed' ,
res ,
code )
return
end
postdata = {}
regexp =
'<input type="hidden" name="([^"]+)" value="([^"]*)"'
resp =
table.concat (
resp )
for name ,
value in resp :
gmatch (
regexp )
do
postdata [
name ] =
value
end
postdata.identity =
username
postdata.credential =
password
cookie =
headers [
'Set-Cookie' ]
or headers [
'set-cookie' ]
or ''
body =
encodeargs (
postdata )
res ,
code ,
headers =
http.request ({
url =
url ,
method =
'POST' ,
source =
ltn12.source.string (
body ),
protocol =
'tlsv12' ,
headers = {
[
'Content-Type' ] =
'application/x-www-form-urlencoded' ,
[
'Content-Length' ] = #
body ,
[
'Cookie' ] =
cookie ,
}
})
if not res or code ~=
302 then
log (
'request 2 failed' ,
res ,
code )
return
end
hdr =
headers.Location or headers.location
resp_code =
hdr :
match (
'code=([^&]+)' )
body =
json.encode ({
grant_type =
'authorization_code' ,
client_id =
'ownerapi' ,
code =
resp_code ,
code_verifier =
code_verifier ,
redirect_uri =
'https://auth.tesla.com/void/callback' ,
})
resp = {}
res ,
code ,
headers =
http.request ({
url =
'https://auth.tesla.com/oauth2/v3/token' ,
method =
'POST' ,
source =
ltn12.source.string (
body ),
sink =
ltn12.sink.table (
resp ),
protocol =
'tlsv12' ,
headers = {
[
'Content-Type' ] =
'application/json' ,
[
'Accept' ] =
'application/json' ,
[
'Content-Length' ] = #
body ,
[
'User-Agent' ] =
'Mozilla/5.0 (Linux; Android 9.0.0; VS985 4G Build/LRX21Y; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36' ,
[
'X-Tesla-User-Agent' ] =
'TeslaApp/3.4.4-350/fad4a582e/android/9.0.0' ,
}
})
if not res or code ~=
200 then
log (
'request 3 failed' ,
res ,
code )
return
end
resp =
table.concat (
resp )
resp =
json.pdecode (
resp )
bearer_token =
resp.access_token
refresh_token =
resp.refresh_token
body =
json.encode ({
grant_type =
'urn:ietf:params:oauth:grant-type:jwt-bearer' ,
client_id =
client_id ,
client_secret =
client_secret ,
})
resp = {}
res ,
code ,
headers =
http.request ({
url =
'https://owner-api.teslamotors.com/oauth/token' ,
method =
'POST' ,
source =
ltn12.source.string (
body ),
sink =
ltn12.sink.table (
resp ),
protocol =
'tlsv12' ,
headers = {
[
'Content-Type' ] =
'application/json' ,
[
'Authorization' ] =
'Bearer ' ..
bearer_token ,
[
'Content-Length' ] = #
body ,
}
})
print (
res ,
code )
if not res or code ~=
200 then
log (
'request 4 failed' ,
res ,
code )
return
end
resp =
table.concat (
resp )
resp =
json.pdecode (
resp )
access_token =
resp.access_token
log (
access_token )
Posts: 81
Threads: 24
Joined: Sep 2017
Reputation:
0
Generating the new auth_token is working on my SHAC (with the old libraries)
Thank you!!!
I'd like to use this token to automate a change in mode from 'backup-only' (which forces the Powerwall to charge from the Grid when electricity rates are cheap) to 'self-powered' (which discharges the battery to power our home).
Could you please also help me convert the python code to pass this token back to the Tesla Powerwall to change the mode to Backup
https://github.com/fkhera/powerwallCloud...up.py#L132
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
##
Set Powerwall Operation to Charge (
Backup )
or Discharge (
self_consumption )
#
Pause PERHAPS WITH (
self_consumption )
w /
Current SoC as backup_reserve_percent
def operation_set (
self ,
real_mode ,
backup_reserve_percent ):
#
auth_header = {
'Authorization' :
'Bearer ' +
self.token }
payload = {
"backup_reserve_percent" :
backup_reserve_percent }
#
payload =
json.dumps ({
"real_mode" :
real_mode ,
"backup_reserve_percent" :
backup_reserve_percent })
set_endpoint =
'/backup'
set_url =
self.energy_base_url +
str (
self.energy_site_id ) +
set_endpoint
print (
"Setting Operation for Site Id: " ,
self.energy_site_id )
print (
"Trying URL: " ,
set_url )
print (
"Setting mode: " +
json.dumps (
payload ))
try :
result =
requests.post (
set_url ,
json =
payload ,
headers =
self.auth_header ,
timeout =
50 )
print (
"Set result output: " ,
result.content )
if result.status_code ==
201 :
print (
"Successfully changed reserve mode" )
except HTTPError as err :
print (
"Error: {0}" .
format (
err ))
except Timeout as err :
print (
"Request timed out: {0}" .
format (
err ))#
Many thanks again
Kind Regards
James
Posts: 8075
Threads: 43
Joined: Jun 2015
Reputation:
471
This should provide a list of products which id should be used in the next request. Change gateway URL and token as needed.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
http =
require (
'socket.http' )
ltn12 =
require (
'ltn12' )
json =
require (
'json' )
access_token =
'ABCDEF'
url =
'https://192.168.1.1/api/1/products'
resp = {}
res ,
code ,
headers =
http.request ({
url =
url ,
method =
'GET' ,
sink =
ltn12.sink.table (
resp ),
protocol =
'tlsv12' ,
headers = {
[
'Authorization' ] =
'Bearer ' ..
access_token ,
}
})
resp =
table.concat (
resp )
log (
res ,
code ,
resp )
This request will set the battery reserve percent to 50. energy_site_id is the ID from the previous request.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
http =
require (
'socket.http' )
ltn12 =
require (
'ltn12' )
json =
require (
'json' )
access_token =
'ABCDEF'
energy_site_id =
'123456'
backup_reserve_percent =
50
url =
'https://192.168.1.1/api/1/energy_sites/' ..
energy_site_id ..
'/backup'
body =
json.encode ({
backup_reserve_percent =
backup_reserve_percent ,
})
res ,
code ,
headers =
http.request ({
url =
url ,
method =
'POST' ,
source =
ltn12.source.string (
body ),
protocol =
'tlsv12' ,
headers = {
[
'Content-Type' ] =
'application/json' ,
[
'Content-Length' ] = #
body ,
[
'Authorization' ] =
'Bearer ' ..
access_token ,
}
})
log (
res ,
code ,
headers )
Posts: 81
Threads: 24
Joined: Sep 2017
Reputation:
0
I've implemented the above and can report back that it's working!
The IP address in the URL above should be replaced with 'https://owner-api.teslamotors.com'
I'm still having a little trouble capturing the energy_site_id .. how does one traverse the returned table with a numeric (1) table name.
https://owner-api.teslamotors.com/api/1/products returns data in the form
Code:
1 2 3 4 5 6 7 8 9 10 11
*
table :
[
response ]
*
table :
[
1 ]
*
table :
[
site_name ]
*
string :
Home Energy Gateway
[
energy_site_id ]
*
string :
xxxxxxx
[
id ]
*
string :
xxxx-xxx-xxxx-xxxx
which I'd normally process as follows
Code:
1 2 3
mydata =
json.pdecode (
table.concat (
resp ))
assetSiteD =
mydata.response.1.energy_site_id
however the table name '1' in the above errors when I try to save the script
Code:
1
Lua syntax error at line 200 :
malformed number near '.1.energy_site_id'
How do I reference the value with a table name of 1?
Kind Regards
James
Posts: 8075
Threads: 43
Joined: Jun 2015
Reputation:
471
Try this, or put quotes around 1 if it does not work.
Code:
1
assetSiteD =
mydata.response [
1 ].
energy_site_id
Posts: 81
Threads: 24
Joined: Sep 2017
Reputation:
0
The [1] works ... the quotes did not.
Many thanks
Posts: 451
Threads: 94
Joined: Jun 2015
Reputation:
6
Posts: 3
Threads: 0
Joined: May 2020
Reputation:
0
hi there - I have a similar set up and need to use LUA - I am getting the following
trial access Tesla 2 05.12.2021 13:48:50
* arg: 1
* string: request 2 failed
* arg: 2
* string: <HTML><HEAD>
<TITLE>Access Denied</TITLE>
</HEAD><BODY>
<H1>Access Denied</H1>
You don't have permission to access "http://auth.tesla.com/oauth2/v3/authorize?" on this server.<P>
Reference #18.9444c717.1638672530.1633eb30
</BODY>
</HTML>
* arg: 3
* number: 403
any suggestions - with thanks
Posts: 10
Threads: 2
Joined: Sep 2016
Reputation:
0
(05.12.2021, 03:03) mikeatlorne Wrote: hi there - I have a similar set up and need to use LUA - I am getting the following
trial access Tesla 2 05.12.2021 13:48:50
* arg: 1
* string: request 2 failed
* arg: 2
* string: <HTML><HEAD>
<TITLE>Access Denied</TITLE>
</HEAD><BODY>
<H1>Access Denied</H1>
You don't have permission to access "http://auth.tesla.com/oauth2/v3/authorize?" on this server.<P>
Reference #18.9444c717.1638672530.1633eb30
</BODY>
</HTML>
* arg: 3
* number: 403
any suggestions - with thanks
Hi, anyone having the same problem or doing progress with this topic? Mine stops during the same step as above.
Best regards
Eilert Bjerkan
HW: LM5-M4(i.MX6), FW: 20200703
Posts: 8075
Threads: 43
Joined: Jun 2015
Reputation:
471
The login part has been modified so the automated way no longer works. The only way is to manually get both tokens. See this for more info:
https://teslascope.com/help/generating-tokens
But in this case another script is needed to handle token refresh because they have a limited lifetime. It can be automated but if the token cannot be refreshed then it has to be updated manually again.
Posts: 451
Threads: 94
Joined: Jun 2015
Reputation:
6
(14.01.2022, 09:29) admin Wrote: The login part has been modified so the automated way no longer works. The only way is to manually get both tokens. See this for more info: https://teslascope.com/help/generating-tokens
But in this case another script is needed to handle token refresh because they have a limited lifetime. It can be automated but if the token cannot be refreshed then it has to be updated manually again.
i installed the Auth for Tesla app and get:
- Refresh Token
- Access Token
In the old situation it generates an Api Token and thats is used in the header.
How to use above codes in my lua code to get it working again?
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
encdec =
require (
'encdec' )
http =
require (
'socket.http' )
mime =
require (
'mime' )
ltn12 =
require (
'ltn12' )
json =
require (
'json' )
https =
require (
'ssl.https' )
username =
'my@email.com'
password =
'myPassword'
client_id =
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
client_secret =
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
tesla = {}
tesla [
1 ] = {
'myCar1' ,
'myVehicleId' }
CarID =
tesla [
1 ][
2 ]
function GetToken ()
function mt ()
local ts ,
tu =
os.microtime ()
return ts ..
'.' ..
tu
end
function b64url (
str )
return mime.b64 (
str ):
gsub (
'.' , {
[
'+' ] =
'-' ,
[
'/' ] =
'_' ,
[
'=' ] =
'' ,
})
end
function encodeargs (
t )
local res = {}
local esc =
require (
'socket.url' ).
escape
for k ,
v in pairs (
t )
do
res [ #
res +
1 ] =
esc (
k ) ..
'=' ..
esc (
v )
end
return table.concat (
res ,
'&' )
end
code_verifier =
encdec.sha512 (
mt ()):
sub (
1 ,
86 )
state =
b64url (
encdec.sha256 (
mt ()):
sub (
1 ,
12 ))
code_challenge =
b64url (
code_verifier )
args =
encodeargs ({
client_id =
'ownerapi' ,
code_challenge =
code_challenge ,
code_challenge_method =
'S256' ,
redirect_uri =
'https://auth.tesla.com/void/callback' ,
response_type =
'code' ,
scope =
'openid email offline_access' ,
state =
state ,
})
url =
'https://auth.tesla.com/oauth2/v3/authorize?' ..
args
res ,
code ,
headers =
http.request (
url )
if not res or code ~=
200 then
return
end
postdata = {}
regexp =
'<input type="hidden" name="([^"]+)" value="([^"]*)"'
for name ,
value in res :
gmatch (
regexp )
do
postdata [
name ] =
value
end
postdata.identity =
username
postdata.credential =
password
cookie =
headers [
'Set-Cookie' ]
or headers [
'set-cookie' ]
or ''
body =
encodeargs (
postdata )
res ,
code ,
headers =
http.request ({
url =
url ,
method =
'POST' ,
source =
ltn12.source.string (
body ),
headers = {
[
'Content-Type' ] =
'application/x-www-form-urlencoded' ,
[
'Content-Length' ] = #
body ,
[
'Cookie' ] =
cookie ,
}
})
if not res or code ~=
302 then
return
end
hdr =
headers.Location or headers.location
resp_code =
hdr :
match (
'code=([^&]+)' )
body =
json.encode ({
grant_type =
'authorization_code' ,
client_id =
'ownerapi' ,
code =
resp_code ,
code_verifier =
code_verifier ,
redirect_uri =
'https://auth.tesla.com/void/callback' ,
})
resp = {}
res ,
code ,
headers =
http.request ({
url =
'https://auth.tesla.com/oauth2/v3/token' ,
method =
'POST' ,
source =
ltn12.source.string (
body ),
sink =
ltn12.sink.table (
resp ),
headers = {
[
'Content-Type' ] =
'application/json' ,
[
'Accept' ] =
'application/json' ,
[
'Content-Length' ] = #
body ,
[
'User-Agent' ] =
'Mozilla/5.0 (Linux; Android 9.0.0; VS985 4G Build/LRX21Y; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36' ,
[
'X-Tesla-User-Agent' ] =
'TeslaApp/3.4.4-350/fad4a582e/android/9.0.0' ,
}
})
if not res or code ~=
200 then
return
end
resp =
table.concat (
resp )
resp =
json.pdecode (
resp )
bearer_token =
resp.access_token
refresh_token =
resp.refresh_token
body =
json.encode ({
grant_type =
'urn:ietf:params:oauth:grant-type:jwt-bearer' ,
client_id =
client_id ,
client_secret =
client_secret ,
})
resp = {}
res ,
code ,
headers =
http.request ({
url =
'https://owner-api.teslamotors.com/oauth/token' ,
method =
'POST' ,
source =
ltn12.source.string (
body ),
sink =
ltn12.sink.table (
resp ),
headers = {
[
'Content-Type' ] =
'application/json' ,
[
'Authorization' ] =
'Bearer ' ..
bearer_token ,
[
'Content-Length' ] = #
body ,
}
})
if not res or code ~=
200 then
return
end
resp =
table.concat (
resp )
resp =
json.pdecode (
resp )
return resp.access_token
end
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 ()
end
end
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 ()
end
end
function get_products ()
products =
RequestFromTesla (
'products' )
return (
products )
end
function get_vehicles ()
vehicles =
RequestFromTesla (
'vehicles' )
return (
vehicles )
end
function get_onboarding_data ()
onboarding_data =
RequestFromTesla (
'users/onboarding_data' )
return (
onboarding_data )
end
function get_vehicle_data ()
vehicle_data =
RequestFromTesla (
'vehicles/' ..
CarID ..
'/vehicle_data' )
return (
vehicle_data )
end
function wake_up ()
result =
WriteToTesla (
'vehicles/' ..
CarID ..
'/wake_up' )
debugLog (
result )
end
function door_unlock ()
result =
WriteToTesla (
'vehicles/' ..
CarID ..
'/command/door_unlock' )
return (
result )
end
function door_lock ()
result =
WriteToTesla (
'vehicles/' ..
CarID ..
'/command/door_lock' )
return (
result )
end
function honk_horn ()
result =
WriteToTesla (
'vehicles/' ..
CarID ..
'/command/honk_horn' )
return (
result )
end
function flash_lights ()
result =
WriteToTesla (
'vehicles/' ..
CarID ..
'/command/flash_lights' )
return (
result )
end
function auto_conditioning_start ()
result =
WriteToTesla (
'vehicles/' ..
CarID ..
'/command/auto_conditioning_start' )
return (
result )
end
function auto_conditioning_stop ()
result =
WriteToTesla (
'vehicles/' ..
CarID ..
'/command/auto_conditioning_stop' )
return (
result )
end
function set_temps (
iDriverTemp ,
iPassengerTemp )
result =
WriteToTesla (
'vehicles/' ..
CarID ..
'/command/set_temps?driver_temp=' ..
iDriverTemp.. '&passenger_temp=' ..
iPassengerTemp )
return (
result )
end
function charge_port_door_open ()
result =
WriteToTesla (
'vehicles/' ..
CarID ..
'/command/charge_port_door_open' )
return (
result )
end
function charge_start ()
result =
WriteToTesla (
'vehicles/' ..
CarID ..
'/command/charge_start' )
return (
result )
end
function charge_stop ()
result =
WriteToTesla (
'vehicles/' ..
CarID ..
'/command/charge_stop' )
return (
result )
end
function actuate_trunk ()
result =
WriteToTesla (
'vehicles/' ..
CarID ..
'/command/actuate_trunk' ,
'{"which_trunk":"rear"}' )
return (
result )
end
function actuate_frunk ()
result =
WriteToTesla (
'vehicles/' ..
CarID ..
'/command/actuate_trunk' ,
'{"which_trunk":"front"}' )
return (
result )
end
Posts: 8075
Threads: 43
Joined: Jun 2015
Reputation:
471
Specify API_Token = 'xyz' together with other credentials then you should be able to use the RequestFromTesla/WriteToTesla functions. Keep in mind that the token has expiration time and has to be refreshed.