Logic Machine Forum
curl to LUA - Printable Version

+- Logic Machine Forum (https://forum.logicmachine.net)
+-- Forum: LogicMachine eco-system (https://forum.logicmachine.net/forumdisplay.php?fid=1)
+--- Forum: Scripting (https://forum.logicmachine.net/forumdisplay.php?fid=8)
+--- Thread: curl to LUA (/showthread.php?tid=3779)



curl to LUA - KoBra - 29.12.2021

Dear, as I am a novice in scripting. Anyone that can help me in reading out doorbell status to link to object in KNX?

Hik doorbell KV6113-WPE1 uses  a curl command:

Code:
curl -i --digest -u admin:xxx http://192.168.x.x/ISAPI/VideoIntercom/callStatus?format=json

Should give as output : “idle” , “ring” , “onCall”

during "ring" it must set a boolean to 1 All others 0.


RE: curl to LUA - admin - 29.12.2021

See this example: https://forum.logicmachine.net/showthread.php?tid=1061&pid=9364#pid9364


RE: curl to LUA - KoBra - 29.12.2021

I found that one using the search function but this is about getting a snapshot not a datapoint. So i don't understand the link. Sorry i am really novice in scripting ;-)


RE: curl to LUA - admin - 29.12.2021

Copy the script for the example and change two last lines and log the result. Change 123456789 to your device password and change the IP address as needed.
Code:
url = 'http://admin:123456789@192.168.1.1/ISAPI/VideoIntercom/callStatus?format=json'
res, err, hdrs = request(url)

log(res, err, hdrs)



RE: curl to LUA - KoBra - 01.01.2022

Thx for the help.

I made a script from some copy pasting of other scripts i have running. But i don't get the different value.

The reply from the bell in the JSON is the following: 

Code:
{
    "CallStatus":    {
        "status":    "idle"
    }
}

But with the following script i get no value from the doorbell:

Code:
require('json')
require('socket.http')
data = socket.http.request('http://admin:xxxx@192.168.0.0/ISAPI/VideoIntercom/callStatus?format=json')
data = json.pdecode(data)

if type(data) == 'table' then
  data = data['CallStatus'][ 1 ]
  log(data)
  -- Status
grp.checkwrite('32/5/1', data.status)
 
else
  alert('request failed')
end


Sorry i am just not good at scripting (yet)


RE: curl to LUA - admin - 02.01.2022

Try this:
Code:
if type(data) == 'table' then
  status = data.CallStatus.status
  grp.checkwrite('32/5/1', status)
else
  alert('request failed')
end



RE: curl to LUA - KoBra - 02.01.2022

Tried it both as Resident or as Event based script but both don't write a value at the 32/5/1 address... "Request Failed". No idea where it goes wrong.

I tested the login with credential in Chrome on a incognito window. There it accepts the HTTP including the login credentials.


RE: curl to LUA - admin - 03.01.2022

It does not work because the standard HTTP library does not support digest authentication scheme. You need to use code from this example instead: https://forum.logicmachine.net/showthread.php?tid=1061&pid=9364#pid9364


RE: curl to LUA - KoBra - 03.01.2022

(03.01.2022, 06:27)admin Wrote: It does not work because the standard HTTP library does not support digest authentication scheme. You need to use code from this example instead: https://forum.logicmachine.net/showthread.php?tid=1061&pid=9364#pid9364

If i test with script below i get the same error...

Code:
local skthttp = require('socket.http')
local skturl = require('socket.url')
local ltn12 = require('ltn12')
local md5sum = require('encdec').md5

local hash = function(...)
  return md5sum(table.concat({...}, ':'))
end

local parse_header = function(header)
  local result = {}
  for key, value in (header .. ','):gmatch('(%w+)=(.-),') do
    if value:sub(1, 1) == '"' then -- strip quotes
      result[ key:lower() ] = value:sub(2, -2)
    else
      result[ key:lower() ] = value
    end
  end
  return result
end

local make_digest_header = function(headers)
  local digest = {}
  for _, header in ipairs(headers) do
    if not header.unquote then
      header[ 2 ] = '"' .. header[ 2 ] .. '"'
    end

    digest[ #digest + 1 ] = header[ 1 ] .. '=' .. header[ 2 ]
  end
  return 'Digest ' .. table.concat(digest, ', ')
end

local _request = function(req)
  if not req.url then
    return nil, 'missing url'
  end

  local url = skturl.parse(req.url)
  local user, password = url.user, url.password
  local sink = req.sink

  if not user or not password then
    return nil, 'missing credentials in url'
  end

  url.user, url.password, url.authority, url.userinfo = nil, nil, nil, nil
  req.url = skturl.build(url)
  local source
  if req.source then
    local chunks = {}
    local capture = function(chunk)
      if chunk then
        chunks[ #chunks + 1 ] = chunk
      end
      return chunk
    end
    local chunk_id = 0
    source = function()
      chunk_id = chunk_id + 1
      return chunks[ chunk_id ]
    end
    req.source = ltn12.source.chain(req.source, capture)
  end
  req.sink = nil
  local body, code, hdrs = skthttp.request(req)
  if code == 401 and hdrs['www-authenticate'] then
    local ht = parse_header(hdrs['www-authenticate'])
    if not ht.realm or not ht.nonce then
      return nil, 'missing realm/nonce from response'
    end
    local qop = ht.qop
    if qop and qop ~= 'auth' then
      return nil, 'unsupported qop ' .. tostring(qop)
    end
    if ht.algorithm and ht.algorithm:lower() ~= 'md5' then
      return nil, 'unsupported algo ' .. tostring(ht.algorithm)
    end
    local nc = '00000001'
    local cnonce = string.format('%08x', os.time())
    local uri = skturl.build({ path = url.path, query = url.query })
    local method = req.method or 'GET'
    local response = hash(
      hash(user, ht.realm, password),
      ht.nonce,
      nc,
      cnonce,
      'auth',
      hash(method, uri)
    )
    req.headers = req.headers or {}
    local auth = {
      { 'username', user },
      { 'realm', ht.realm },
      { 'nonce', ht.nonce },
      { 'uri', uri },
      { 'cnonce', cnonce },
      { 'nc', nc, unquote = true },
      { 'qop', 'auth' },
      { 'algorithm', 'MD5' },
      { 'response', response },
    }
    if ht.opaque then
      table.insert(auth, { 'opaque', ht.opaque })
    end
    req.headers.authorization = make_digest_header(auth)
    if not req.headers.cookie and hdrs['set-cookie'] then
      -- not really correct but enough for httpbin
      local cookie = (hdrs['set-cookie'] .. ';'):match('(.-=.-)[;,]')
      if cookie then
        req.headers.cookie = '$Version: 0; ' .. cookie .. ';'
      end
    end
    if req.source then
      req.source = source
    end
    req.sink = sink
    body, code, hdrs = skthttp.request(req)
  end

  return body, code, hdrs
end

local request = function(url)
  local t = type(url)
  if t == 'table' then
    return _request(table.clone(url))
  elseif t == 'string' then
    local req = {}
    local _, code, headers = _request({ url = url, sink = ltn12.sink.table(req) })
    return table.concat(req), code, headers
  end
end

url = 'http://admin:XXXX@192.168.0.1/ISAPI/VideoIntercom/callStatus?format=json'
image, err, hdrs = request(url)

require('json')
data = json.pdecode(data)

if type(data) == 'table' then
  status = data.CallStatus.status
  grp.checkwrite('32/5/1', data.CallStatus.status)
else
  alert('request failed doorbell')
end



RE: curl to LUA - admin - 03.01.2022

Line 136 should be:
data, err, hdrs = request(url)

Otherwise result is assigned to the image variable not data


RE: curl to LUA - KoBra - 03.01.2022

Angel Hoe could I be that stupid… tnx it works!


RE: curl to LUA - Krstfr2k - 03.03.2022

I am sorry to hijack the thread, but how would implement the following in the script example that is provided by Admin?

Code:
curl --location --request PUT 'https://exampleadr.com/pms_api/:subscription_id/:account_id/rooms/:room_id' \
--data-raw '{
    "room": {
        "housekeeping_status": "inspect"
    }
}'



RE: curl to LUA - admin - 03.03.2022

Use this, adjust URL (including id parameters) as needed:
Code:
http = require('socket.http')
json = require('json')

url = 'https://exampleadr.com/pms_api/:subscription_id/:account_id/rooms/:room_id'

body = json.encode({
  room = {
    housekeeping_status = 'inspect'
  }
})

res, code = http.request({
  url = url,
  method = 'PUT',
  body = body
})

log(res, code)



RE: curl to LUA - Krstfr2k - 03.03.2022

(03.03.2022, 08:33)admin Wrote: Use this, adjust URL (including id parameters) as needed:
Code:
http = require('socket.http')
json = require('json')

url = 'https://exampleadr.com/pms_api/:subscription_id/:account_id/rooms/:room_id'

body = json.encode({
  room = {
    housekeeping_status = 'inspect'
  }
})

res, code = http.request({
  url = url,
  method = 'PUT',
  body = body
})

log(res, code)

The API uses Digest Auth, would the example above work in that case or would the earlier example in this thread need to be adjusted?


RE: curl to LUA - admin - 03.03.2022

In this case you need to pass request source differently and also put username and password into the URL. Also don't forget to include digest library code before this.
Code:
json = require('json')

url = 'https://username:password@exampleadr.com/pms_api/:subscription_id/:account_id/rooms/:room_id'

body = json.encode({
  room = {
    housekeeping_status = 'inspect'
  }
})

resp = {}

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

print(res, code, resp)



RE: curl to LUA - Krstfr2k - 04.03.2022

(03.03.2022, 09:04)admin Wrote: In this case you need to pass request source differently and also put username and password into the URL. Also don't forget to include digest library code before this.
Code:
json = require('json')

url = 'https://username:password@exampleadr.com/pms_api/:subscription_id/:account_id/rooms/:room_id'

body = json.encode({
  room = {
    housekeeping_status = 'inspect'
  }
})

resp = {}

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

print(res, code, resp)

Thank you so much for the help, worked perfectly!


RE: curl to LUA - cekca - 27.01.2023

How i can send a command for open a door / Relay:

curl -i --digest -u admin:xxx -X PUT -d '<RemoteControlDoor><cmd>open</cmd></RemoteControlDoor>' http://192.168.0.xx/ISAPI/AccessControl/RemoteControl/door/1

thank you


RE: curl to LUA - admin - 27.01.2023

You need to use digest auth request function from this example: https://forum.logicmachine.net/showthread.php?tid=1061&pid=9364#pid9364
Code:
body = '<RemoteControlDoor><cmd>open</cmd></RemoteControlDoor>'
res, code = request({
  url = 'http://admin:123@IP/...',
  method = 'PUT',
  headers = {
    ['Content-Length'] = #body,
  },
  source = ltn12.source.string(body),
})
log(res, code)