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.

Has someone already integrated LG ThinQ
#1
Hi All,

I like to integrate my LG devices like AC for example by the LG Thinq API. Has someone already done this and could share the script here?
I found a start at this website https://forum.fibaro.com/topic/51441-hc3...martthinq/ with the script below. Would be great if we can get it to work.

The script berlow is not mine but copied from the website i shared. It seems you'll need a secret key and client key but i'm not sure where to get it as i could not find clear information about it. So all help is much appriciated to get this work.

Code:
OAUTH_SECRET_KEY = "c053c2a6ddeb7ad97cb0eed0dcb31cf8"
OAUTH_CLIENT_KEY = "LGAO221A02"
DATE_FORMAT = "!%a, %d %b %Y %H:%M:%S"
V2_AUTH_PATH = "/oauth/1.0/oauth2/token"

function oauth2_signature(message, secret)
    local hmac = sha.hmac
    local hashed = hmac(sha.sha1, secret, message)
    local binary_hash = sha.hex2bin(hmac(sha.sha1, secret, message))
    return sha.bin2base64(binary_hash)
end

function auth_request(oauth_url, reqdata)
    if string.sub(oauth_url, -1):lower() == "/" then  oauth_url = string.sub(oauth_url, 0, #oauth_url-1) end
    local url = oauth_url .. V2_AUTH_PATH
    local timestamp = tostring(os.date(DATE_FORMAT, os.time())) .. " +0000"
    local data_list = {}
    for k, v in pairs(reqdata) do  table.insert(data_list, string.format("%s=%s", k, v)) end
    local req_url = string.format("%s?%s", V2_AUTH_PATH, table.concat(data_list, "&"))
    local message = string.format("%s\n%s", req_url, timestamp)
    local sig = oauth2_signature(message, OAUTH_SECRET_KEY)
    local headers = {
        ["x-lge-appkey"] = OAUTH_CLIENT_KEY,
        ["x-lge-oauth-signature"] = sig,
        ["x-lge-oauth-date"] = timestamp,
        ["Content-Type"] = "application/x-www-form-urlencoded",
        ["Accept"] = "application/json"
    }
    castdata = table.concat(data_list, "&")
    self.http:request(url,{
        options = {
            headers = headers,
            data = castdata,
            method = "POST",
            timeout = 5000,
        },
        success = function(response)
            log("auth_request success", response.data)
        end,
        error = function(error)
            self:debug('error: ' .. json.encode(error))
        end
    })
   end
   local requestBody = {
        grant_type = "refresh_token",
        refresh_token = "4ed29cb48cc1cb6228658d50817b789d67d37dbad56bf561cc6389a4ab69070d8ee808edb3377ad3b07e243c467f1d36"
    }
    auth_request("https://us.lgeapi.com/", requestBody)
Reply
#2
With slight modifications this code should work - HTTP request via socket.http and hashing via encdec.hmacsha1
Client / secret key are fixed. You need your own access / refresh tokens to make requests and periodically update tokens.
Reply
#3
(08.05.2023, 08:55)admin Wrote: With slight modifications this code should work - HTTP request via socket.http and hashing via encdec.hmacsha1
Client / secret key are fixed. You need your own access / refresh tokens to make requests and periodically update tokens.

Could you make this modifications please just to make sure everything is set correct so far? Otherwise it will be a lot trial before it might work.
Many thanks in advance!

I changed print to log already but i have no idea how to change the hash part correct.
Also i see
Code:
self:debug
and i believe it's not valid code for the LM correct?
Reply
#4
Try this for the signature calculation. You might need to swap message and secret arguments if it does not work.
Code:
function oauth2_signature(message, secret)
  local encdec = require('encdec')
  local hashed = encdec.hmacsha1(secret, message, true)
  return encdec.base64enc(hashed)
end

Use log() instead of self:debug. There are no callbacks for HTTP requests anyway, just the return values. Search the forums, there are many similar HTTP request examples available.
Reply
#5
Here is my code so far.

I get this error when executing the code:

LG Thinq 08.05.2023 14:35:56
Library socket/http:0: bad argument #1 to 'escape' (string expected, got function)
stack traceback:
[C]: in function 'request'
Resident script:31: in function 'auth_request'

Code:
require('socket.http')
require('encdec')

OAUTH_SECRET_KEY = "c053c2a6ddeb7ad97cb0eed0dcb31cf8"
OAUTH_CLIENT_KEY = "LGAO221A02"
DATE_FORMAT = "!%a, %d %b %Y %H:%M:%S"
V2_AUTH_PATH = "/oauth/1.0/oauth2/token"

function oauth2_signature(message, secret)
  local hashed = encdec.hmacsha1(message, secret, true)
  return encdec.base64enc(hashed)
end

function auth_request(oauth_url, reqdata)
    if string.sub(oauth_url, -1):lower() == "/" then  oauth_url = string.sub(oauth_url, 0, #oauth_url-1) end
    local url = oauth_url .. V2_AUTH_PATH
    local timestamp = tostring(os.date(DATE_FORMAT, os.time())) .. " +0000"
    local data_list = {}
    for k, v in pairs(reqdata) do  table.insert(data_list, string.format("%s=%s", k, v)) end
    local req_url = string.format("%s?%s", V2_AUTH_PATH, table.concat(data_list, "&"))
    local message = string.format("%s\n%s", req_url, timestamp)
    local sig = oauth2_signature(message, OAUTH_SECRET_KEY)
    local headers = {
        ["x-lge-appkey"] = OAUTH_CLIENT_KEY,
        ["x-lge-oauth-signature"] = sig,
        ["x-lge-oauth-date"] = timestamp,
        ["Content-Type"] = "application/x-www-form-urlencoded",
        ["Accept"] = "application/json"
    }
    castdata = table.concat(data_list, "&")
    socket.http.request(url,{
        options = {
            headers = headers,
            data = castdata,
            method = "POST",
            timeout = 5000,
        },
        success = function(response)
            log("auth_request success", response.data)
        end,
        error = function(error)
            log('error: ' .. json.encode(error))
        end
    })
end
   
local requestBody = {
    grant_type = "refresh_token",
    refresh_token = "4ed29cb48cc1cb6228658d50817b789d67d37dbad56bf561cc6389a4ab69070d8ee808edb3377ad3b07e243c467f1d36"
}

auth_request("https://us.lgeapi.com/", requestBody)
Reply
#6
As I've said check other examples on what parameters the socket.http.request call accepts. But it won't work as is anyway without tokens generated using your own access credentials.
These threads might be helpful:
https://forum.logicmachine.net/showthread.php?tid=3014
https://forum.logicmachine.net/showthread.php?tid=3308
Reply
#7
No i have this code working with the below response. Do you think it's due to the wrong credentials or is it part of the code? ( Is still can't find any information on how to get my own credentials from LG..)

LG Thinq 08.05.2023 16:35:17
* arg: 1
  * string: {"error":{"request":"/oauth/1.0/oauth2/token","code":"LG.OAUTH.EC.2105","service":"oauth","message":"Signature doesn't match request."}}
* arg: 2
  * number: 406

Code:
require('json')
require('encdec')
require('socket.http')

OAUTH_SECRET_KEY = "c053c2a6ddeb7ad97cb0eed0dcb31cf8"
OAUTH_CLIENT_KEY = "LGAO221A02"
DATE_FORMAT = "!%a, %d %b %Y %H:%M:%S"
V2_AUTH_PATH = "/oauth/1.0/oauth2/token"

function oauth2_signature(message, secret)
  local hashed = encdec.hmacsha1(secret, message, true)
  return encdec.base64enc(hashed)
end

function auth_request(oauth_url, reqdata)
    if string.sub(oauth_url, -1):lower() == "/" then  oauth_url = string.sub(oauth_url, 0, #oauth_url-1) end
    local url = oauth_url .. V2_AUTH_PATH
    local timestamp = tostring(os.date(DATE_FORMAT, os.time())) .. " +0000"
    local data_list = {}
    for k, v in pairs(reqdata) do  table.insert(data_list, string.format("%s=%s", k, v)) end
    local req_url = string.format("%s?%s", V2_AUTH_PATH, table.concat(data_list, "&"))
    local message = string.format("%s\n%s", req_url, timestamp)
    local sig = oauth2_signature(message, OAUTH_SECRET_KEY)
    local headers = {
        ["x-lge-appkey"] = OAUTH_CLIENT_KEY,
        ["x-lge-oauth-signature"] = sig,
        ["x-lge-oauth-date"] = timestamp,
        ["Content-Type"] = "application/x-www-form-urlencoded",
        ["Accept"] = "application/json"
    }
    castdata = table.concat(data_list, "&")
    res, err = socket.http.request({
                url = url,
            headers = headers,
            data = castdata,
            method = "POST",
            timeout = 5000,
    })
end
   
local requestBody = {
    grant_type = "refresh_token",
    refresh_token = "4ed29cb48cc1cb6228658d50817b789d67d37dbad56bf561cc6389a4ab69070d8ee808edb3377ad3b07e243c467f1d36"
}

auth_request("https://nl.lgeapi.com/", requestBody)

log(res, err)
Reply
#8
The problem is that LG only accepts developers to use their API. I see others like Home Assistant use Wideq. I read onine (from recent posts) that is working succesfull. Only problem is that it's in Python script. Is someone interested to 'translate' it to Lua?

https://github.com/majki09/domoticz_lg_thinq_plugin
https://community.home-assistant.io/t/in.../40157/333
Reply


Forum Jump: