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.

Help with Tesla Powerwall API V3
#5
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:
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)
Reply


Messages In This Thread
Help with Tesla Powerwall API V3 - by jamesng - 21.04.2021, 14:18
RE: Help with Tesla Powerwall API V3 - by admin - 22.04.2021, 12:32

Forum Jump: