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.

Creation of custom package
#1
How can I create custom package for LM?
I need custom websocket connections with TV and other stuff and I want to use this connection inside LUA resident or event scripts.
The main idea is I create a package which works as HTTP server on some local port and it has and ability to create websocket clients inside.

e.g.

event script

Code:
1234
ws = require('lua.websocket'tvConnection, errws.connect('ws://my.tv.ip:3000'-- (blocking with t/o) send to local HTTP server                                                      --  a command to connect if no connection tvConnection.send('raw data') -- send text or array of bytes to 'ws://my.tv.ip:3000'


resident script 0 sec

Code:
1234
ws = require('lua.websocket') tvConnection, errws.connect('ws://my.tv.ip:3000') -- (blocking with t/o) send to local HTTP server                                                     -- a command to connect if no connection datatvConnection.receive() -- receive array of data came from 'ws://my.tv.ip:3000' 


Could you please create kind of this stuff? Or give me instruction how to build own package, I know C/C++, Golang, JS, Lua

Thank you in advance!
Reply
#2
There's a pure Lua websocket client library. It should work on LM with some minimal changes.

As for custom packages we do not provide build tools but we can provide some custom packages by request.
Reply
#3
Attached is websocket client library ported from https://github.com/lipp/lua-websockets/

Main changes:
1. Support for basic auth in URL
2. Receive timeout does not close the websocket connection
3. Server support is removed

Add it as a user library named websocket

Minimal example (resident script):
Code:
123456789
if not ws then   ws = require('user.websocket')   url = 'ws://admin:admin@192.168.1.12/apps/localbus.lp'   -- mode is either sync or copas, second parameter sets timeout in seconds   client = ws.client('sync', 10)   client:connect(url) end log(client:receive())

Attached Files
.lua   websocket.lua (Size: 13.64 KB / Downloads: 228)
Reply
#4
Hi,

Thank you. This is great. Is there an recommend way for connecting to multiple websocket servers with this client?

Thanks,

Roger
Reply
#5
Hi,

I am trying to use the copas part because being a resident script i need to run a udp server to accept an event script client.  

On connecting with the copas parameter I get the following in the error log.


Library copas:0: attempt to yield across C-call boundary
stack traceback:
[C]: in function 'yield'
Library copas: in function 'connect'
User library websocket:500: in function 'sock_connect'
User library websocket:438: in function 'connect'



Code:
123456
 ws = require("user.websocket")  url = 'ws://demos.kaazing.com/echo'  -- mode is either sync or copas, second parameter sets timeout in seconds    client = ws.client('copas', 10)  client:connect(url)
Reply
#6
Hi,

I am using the following UDP server code for taking requests from the LM objects.  effectively the websockets client needs to running asynchronously  so it doesn't block the udp server running.  In the past I have used copas to do this.

Code:
1234567891011121314151617181920
 local server = usocket.udp()  server:setsockname("127.0.0.1",53450)  function handler(skts)    skts = copas.wrap(skts)    alert("UDP connection handler")    while true do      local s, err      alert("UDP receiving..")      s, erro = skts:receive(2048)      if not s then        alert("Receive error: %s", erro)        break      end      alert("Received data, bytes: %s",s)       -- send to the websocket      client:send(s)    end  end      copas.addserver(server, handler, 1)
Reply
#7
You need to wrap websocket client in copas mode with function passed to copas.addthread
Reply
#8
Hi,

Is there an example from this?  Because that's what I am trying. thx Roger

Code:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
if not ready then  socket = require("user.websocket")  usocket = require("socket")  copas = require("copas")  ready=true  apps = {}  --helper functions to round and increment values    function increment(n)    n = n + 1    return n  end    function parse(data)    alert('parsing: %s', data)    local response = string.split(data,' = ')  end    function sendCommand(command)    skt:send(command)    sleep(1)      end      function init()  end    function fromKNX(command)    local telegram = string.split(command,',')    log(command)    sendCommand(command)  end    local server = usocket.udp()  server:setsockname("127.0.0.1",53450)  function handler(skts)    skts = copas.wrap(skts)    alert("UDP connection handler")    while true do      local s, err      alert("UDP receiving..")      s, erro = skts:receive(2048)      if not s then        alert("Receive error: %s", erro)        break      end      alert("Received data, bytes: %s",s)      fromKNX(s)    end  end      copas.addserver(server, handler, 1) end if not skt then url= 'ws://demos.kaazing.com/echo'  skt,err = socket.client('copas',5)  skt:connect(url,80)  skt:send('hello')  -- when theres no error connect ok, initialize  if(not err) then    -- add receive thread    copas.addthread(function()        while true do          local resp,err = copas.receive(skt)          -- if theres no connection start a new connection          if not resp then            alert("[tcp-client] Receive error: %s", err)            copas.removeserver(skt)            skt = nil            break          end          local fd,prtd = pcall(parse,resp)          if(fd==false)then            alert("Error with parsemsg %s ",prtd)          end        end      end)    if skt then      alert('[tcp-client] connection ok')      init()      -- error while connecting,    else      if warningfailed then alert('[tcp-client] connection failed (conn): %s', err) end      return    end  else    alert('[tcp-client] error connecting %s',err)    return  end end copas.loop()
Reply
#9
connect part must be inside copas thread
Reply
#10
Hi,

Ok adding the connect in the thread works. Thank you. The timeout parameter doesn't seem to have any effect. The connection always lasts about 10 seconds disconnects and reconnects. Is there no way to keep it permanently connected?

Another thing I need to account for is that the webservice is not always running. It's live when the device is powered on. So what I see is that the script would be constantly trying to reconnect. I guess the only way to handle this is by disabling the resident script when the device is powered off. Unless you have any other idea?

thanks

Roger
Reply
#11
Disconnect might happen from the server side. To keep connection alive you probably need to request data periodically or send ping frames.

As for reconnect, enable/disable is one approach, another one is just to keep script running and check if connect is ok or not. When device is offline then connect function will return once timeout is reached.
Reply
#12
(26.03.2018, 08:42)rocfusion Wrote: Hi,

Ok adding the connect in the thread works.  Thank you.   The timeout parameter doesn't seem to have any effect.  The connection always lasts about 10 seconds disconnects and reconnects.  Is there no way to keep it permanently connected?

Another thing I need to account for is that the webservice is not always running.  It's live when the device is powered on.  So what I see is that the script would be constantly trying to reconnect.  I guess the only way to handle this is by disabling the resident script when the device is powered off.  Unless you have any other idea?

thanks

Roger

Hi Roger,

Did you have any success getting your LG TV to work with this? Also did you manage to support connecting to multiple clients?

I'm trying to do the same (I have 2 LG WebOS TVs I'd like to control).. but I'm a complete newbie to Lua.

Nick
Reply
#13
Referring to the WebSocket post, we are able to connect to the web-socket server, but in the CASAMBI API documentation, they have asked to pass the "API-Key" (a string) as "protocol" (under "Create WebSocket Connection" section of documentation). They have also given an example in JavaScript which is shown below. 
How shall this be implemented using LM WebSocket Library. ? we tried but it is not working (in LM Ambient)

Code:
1
webSocket = new WebSocket("url", "api_key");
 https://developer.casambi.com/   
Reply
#14
You need to modify the upgrade_request function in websocket library:
Code:
1234567891011121314151617181920212223
local upgrade_request = function(req, key)   local format = string.format   local lines = {     format('GET %s HTTP/1.1',req.path or ''),     format('Host: %s',req.host),     'Upgrade: websocket',     'Connection: Upgrade',     format('Sec-WebSocket-Key: %s',key),     'Sec-WebSocket-Version: 13',   }   if self.protocol then     tinsert(lines, format('Sec-WebSocket-Protocol: %s', self.protocol))   end   if req.port and req.port ~= 80 then     lines[2] = format('Host: %s:%d',req.host,req.port)   end   if req.userinfo then     local auth = format('Authorization: Basic %s', base64enc(req.userinfo))     tinsert(lines, auth)   end   tinsert(lines,'\r\n')   return tconcat(lines,'\r\n') end

Then create connection like this (change API key):
Code:
12345678910
ws = require('user.websocket') json = require('json') url = 'wss://door.casambi.com/v1/bridge/' client, err = ws.client('sync', 10) client.protocol = 'API-key' res, err = client:connect(url) log(res, err)
Reply
#15
(11.05.2020, 06:52)admin Wrote: You need to modify the upgrade_request function in websocket library:
Code:
1234567891011121314151617181920212223
local upgrade_request = function(req, key)   local format = string.format   local lines = {     format('GET %s HTTP/1.1',req.path or ''),     format('Host: %s',req.host),     'Upgrade: websocket',     'Connection: Upgrade',     format('Sec-WebSocket-Key: %s',key),     'Sec-WebSocket-Version: 13',   }   if self.protocol then     tinsert(lines, format('Sec-WebSocket-Protocol: %s', self.protocol))   end   if req.port and req.port ~= 80 then     lines[2] = format('Host: %s:%d',req.host,req.port)   end   if req.userinfo then     local auth = format('Authorization: Basic %s', base64enc(req.userinfo))     tinsert(lines, auth)   end   tinsert(lines,'\r\n')   return tconcat(lines,'\r\n') end

Then create connection like this (change API key):
Code:
12345678910
ws = require('user.websocket') json = require('json') url = 'wss://door.casambi.com/v1/bridge/' client, err = ws.client('sync', 10) client.protocol = 'API-key' res, err = client:connect(url) log(res, err)

After making the changes in library, it throws the following error:-

User library websocket:292: attempt to index global 'self' (a nil value)
stack traceback:
User library websocket:292: in function 'upgrade_request'
User library websocket:462: in function 'connect'
User script:8: in main chunk
Reply
#16
Try this:
Code:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
local bit = require('bit') local ssl = require('ssl') local socket = require('socket') local encdec = require('encdec') local parse_url = require('socket.url').parse local bxor = bit.bxor local bor = bit.bor local band = bit.band local lshift = bit.lshift local rshift = bit.rshift local ssub = string.sub local sbyte = string.byte local schar = string.char local tinsert = table.insert local tconcat = table.concat local mmin = math.min local mfloor = math.floor local mrandom = math.random local base64enc = encdec.base64enc local sha1 = encdec.sha1 local unpack = unpack local CONTINUATION = 0 local TEXT = 1 local BINARY = 2 local CLOSE = 8 local PING = 9 local PONG = 10 local guid = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' local read_n_bytes = function(str, pos, n)   pos = pos or 1   return pos+n, string.byte(str, pos, pos + n - 1) end local read_int8 = function(str, pos)   return read_n_bytes(str, pos, 1) end local read_int16 = function(str, pos)   local new_pos,a,b = read_n_bytes(str, pos, 2)   return new_pos, lshift(a, 8) + b end local read_int32 = function(str, pos)   local new_pos,a,b,c,d = read_n_bytes(str, pos, 4)   return new_pos,   lshift(a, 24) +   lshift(b, 16) +   lshift(c, 8 ) +   d end local write_int8 = schar local write_int16 = function(v)   return schar(rshift(v, 8), band(v, 0xFF)) end local write_int32 = function(v)   return schar(     band(rshift(v, 24), 0xFF),     band(rshift(v, 16), 0xFF),     band(rshift(v,  8), 0xFF),     band(v, 0xFF)   ) end local generate_key = function()   math.randomseed(os.time())   local r1 = mrandom(0,0xfffffff)   local r2 = mrandom(0,0xfffffff)   local r3 = mrandom(0,0xfffffff)   local r4 = mrandom(0,0xfffffff)   local key = write_int32(r1)..write_int32(r2)..write_int32(r3)..write_int32(r4)   return base64enc(key) end local bits = function(...)   local n = 0   for _,bitn in pairs{...} do     n = n + 2^bitn   end   return n end local bit_7 = bits(7) local bit_0_3 = bits(0,1,2,3) local bit_0_6 = bits(0,1,2,3,4,5,6) -- TODO: improve performance local xor_mask = function(encoded,mask,payload)   local transformed,transformed_arr = {},{}   -- xor chunk-wise to prevent stack overflow.   -- sbyte and schar multiple in/out values   -- which require stack   for p=1,payload,2000 do     local last = mmin(p+1999,payload)     local original = {sbyte(encoded,p,last)}     for i=1,#original do       local j = (i-1) % 4 + 1       transformed[i] = bxor(original[i],mask[j])     end     local xored = schar(unpack(transformed,1,#original))     tinsert(transformed_arr,xored)   end   return tconcat(transformed_arr) end local encode_header_small = function(header, payload)   return schar(header, payload) end local encode_header_medium = function(header, payload, len)   return schar(header, payload, band(rshift(len, 8), 0xFF), band(len, 0xFF)) end local encode_header_big = function(header, payload, high, low)   return schar(header, payload)..write_int32(high)..write_int32(low) end local encode = function(data,opcode,masked,fin)   local header = opcode or 1-- TEXT is default opcode   if fin == nil or fin == true then     header = bor(header,bit_7)   end   local payload = 0   if masked then     payload = bor(payload,bit_7)   end   local len = #data   local chunks = {}   if len < 126 then     payload = bor(payload,len)     tinsert(chunks,encode_header_small(header,payload))   elseif len <= 0xffff then     payload = bor(payload,126)     tinsert(chunks,encode_header_medium(header,payload,len))   elseif len < 2^53 then     local high = mfloor(len/2^32)     local low = len - high*2^32     payload = bor(payload,127)     tinsert(chunks,encode_header_big(header,payload,high,low))   end   if not masked then     tinsert(chunks,data)   else     local m1 = mrandom(0,0xff)     local m2 = mrandom(0,0xff)     local m3 = mrandom(0,0xff)     local m4 = mrandom(0,0xff)     local mask = {m1,m2,m3,m4}     tinsert(chunks,write_int8(m1,m2,m3,m4))     tinsert(chunks,xor_mask(data,mask,#data))   end   return tconcat(chunks) end local decode = function(encoded)   local encoded_bak = encoded   if #encoded < 2 then     return nil,2-#encoded   end   local pos,header,payload   pos,header = read_int8(encoded,1)   pos,payload = read_int8(encoded,pos)   local high,low   encoded = ssub(encoded,pos)   local bytes = 2   local fin = band(header,bit_7) > 0   local opcode = band(header,bit_0_3)   local mask = band(payload,bit_7) > 0   payload = band(payload,bit_0_6)   if payload > 125 then     if payload == 126 then       if #encoded < 2 then         return nil,2-#encoded       end       pos,payload = read_int16(encoded,1)     elseif payload == 127 then       if #encoded < 8 then         return nil,8-#encoded       end       pos,high = read_int32(encoded,1)       pos,low = read_int32(encoded,pos)       payload = high*2^32 + low       if payload < 0xffff or payload > 2^53 then         assert(false,'INVALID PAYLOAD '..payload)       end     else       assert(false,'INVALID PAYLOAD '..payload)     end     encoded = ssub(encoded,pos)     bytes = bytes + pos - 1   end   local decoded   if mask then     local bytes_short = payload + 4 - #encoded     if bytes_short > 0 then       return nil,bytes_short     end     local m1,m2,m3,m4     pos,m1 = read_int8(encoded,1)     pos,m2 = read_int8(encoded,pos)     pos,m3 = read_int8(encoded,pos)     pos,m4 = read_int8(encoded,pos)     encoded = ssub(encoded,pos)     local mask = {       m1,m2,m3,m4     }     decoded = xor_mask(encoded,mask,payload)     bytes = bytes + 4 + payload   else     local bytes_short = payload - #encoded     if bytes_short > 0 then       return nil,bytes_short     end     if #encoded > payload then       decoded = ssub(encoded,1,payload)     else       decoded = encoded     end     bytes = bytes + payload   end   return decoded,fin,opcode,encoded_bak:sub(bytes+1),mask end local encode_close = function(code,reason)   if code then     local data = write_int16(code)     if reason then       data = data..tostring(reason)     end     return data   end   return '' end local decode_close = function(data)   local _,code,reason   if data then     if #data > 1 then       _,code = read_int16(data,1)     end     if #data > 2 then       reason = data:sub(3)     end   end   return code,reason end local sec_websocket_accept = function(sec_websocket_key)   local enc = sha1(sec_websocket_key..guid, true)   return base64enc(enc) end local http_headers = function(request)   local headers = {}   if not request:match('.*HTTP/1%.1') then     return headers   end   request = request:match('[^\r\n]+\r\n(.*)')   for line in request:gmatch('[^\r\n]*\r\n') do     local name,val = line:match('([^%s]+)%s*:%s*([^\r\n]+)')     if name and val then       name = name:lower()       if not name:match('sec%-websocket') then         val = val:lower()       end       if not headers[name] then         headers[name] = val       else         headers[name] = headers[name]..','..val       end     elseif line ~= '\r\n' then       assert(false,line..'('..#line..')')     end   end   return headers,request:match('\r\n\r\n(.*)') end local upgrade_request = function(req, key, protocol)   local format = string.format   local lines = {     format('GET %s HTTP/1.1',req.path or ''),     format('Host: %s',req.host),     'Upgrade: websocket',     'Connection: Upgrade',     format('Sec-WebSocket-Key: %s',key),     'Sec-WebSocket-Version: 13',   }   if protocol then     tinsert(lines, format('Sec-WebSocket-Protocol: %s', protocol))   end   if req.port and req.port ~= 80 then     lines[2] = format('Host: %s:%d',req.host,req.port)   end   if req.userinfo then     local auth = format('Authorization: Basic %s', base64enc(req.userinfo))     tinsert(lines, auth)   end   tinsert(lines,'\r\n')   return tconcat(lines,'\r\n') end local receive = function(self)   if self.state ~= 'OPEN' and not self.is_closing then     return nil,nil,false,1006,'wrong state'   end   local first_opcode   local frames   local bytes = 3   local encoded = ''   local clean = function(was_clean,code,reason)     self.state = 'CLOSED'     self:sock_close()     if self.on_close then       self:on_close()     end     return nil,nil,was_clean,code,reason or 'closed'   end   while true do     local chunk,err = self:sock_receive(bytes)     if err then       if err == 'timeout' then         return nil,nil,false,1006,err       else         return clean(false,1006,err)       end     end     encoded = encoded..chunk     local decoded,fin,opcode,_,masked = decode(encoded)     if masked then       return clean(false,1006,'Websocket receive failed: frame was not masked')     end     if decoded then       if opcode == CLOSE then         if not self.is_closing then           local code,reason = decode_close(decoded)           -- echo code           local msg = encode_close(code)           local encoded = encode(msg,CLOSE,true)           local n,err = self:sock_send(encoded)           if n == #encoded then             return clean(true,code,reason)           else             return clean(false,code,err)           end         else           return decoded,opcode         end       end       if not first_opcode then         first_opcode = opcode       end       if not fin then         if not frames then           frames = {}         elseif opcode ~= CONTINUATION then           return clean(false,1002,'protocol error')         end         bytes = 3         encoded = ''         tinsert(frames,decoded)       elseif not frames then         return decoded,first_opcode       else         tinsert(frames,decoded)         return tconcat(frames),first_opcode       end     else       assert(type(fin) == 'number' and fin > 0)       bytes = fin     end   end end local send = function(self,data,opcode)   if self.state ~= 'OPEN' then     return nil,false,1006,'wrong state'   end   local encoded = encode(data,opcode or TEXT,true)   local n,err = self:sock_send(encoded)   if n ~= #encoded then     return nil,self:close(1006,err)   end   return true end local close = function(self,code,reason)   if self.state ~= 'OPEN' then     return false,1006,'wrong state'   end   if self.state == 'CLOSED' then     return false,1006,'wrong state'   end   local msg = encode_close(code or 1000,reason)   local encoded = encode(msg,CLOSE,true)   local n,err = self:sock_send(encoded)   local was_clean = false   code = 1005   reason = ''   if n == #encoded then     self.is_closing = true     local rmsg,opcode = self:receive()     if rmsg and opcode == CLOSE then       code,reason = decode_close(rmsg)       was_clean = true     end   else     reason = err   end   self:sock_close()   if self.on_close then     self:on_close()   end   self.state = 'CLOSED'   return was_clean,code,reason or '' end local DEFAULT_PORTS = {ws = 80, wss = 443} local connect = function(self,ws_url,ssl_params)   if self.state ~= 'CLOSED' then     return nil,'wrong state',nil   end   local parsed = parse_url(ws_url)   if parsed.scheme ~= 'wss' and parsed.scheme ~= 'ws' then     return nil, 'bad protocol'   end   if not parsed.port then     parsed.port = DEFAULT_PORTS[ parsed.scheme ]   end   -- Preconnect (for SSL if needed)   local _,err = self:sock_connect(parsed.host, parsed.port)   if err then     return nil,err,nil   end   if parsed.scheme == 'wss' then     if type(ssl_params) ~= 'table' then       ssl_params = {         protocol = 'tlsv1',         options  = {'all', 'no_sslv2', 'no_sslv3'},         verify   = 'none',       }     end     ssl_params.mode = 'client'     self.sock = ssl.wrap(self.sock, ssl_params)     self.sock:dohandshake()   elseif parsed.scheme ~= 'ws' then     return nil, 'bad protocol'   end   local key = generate_key()   local req = upgrade_request(parsed, key, self.protocol)   local n,err = self:sock_send(req)   if n ~= #req then     return nil,err,nil   end   local resp = {}   repeat     local line,err = self:sock_receive('*l')     resp[#resp+1] = line     if err then       return nil,err,nil     end   until line == ''   local response = tconcat(resp,'\r\n')   local headers = http_headers(response)   local expected_accept = sec_websocket_accept(key)   if headers['sec-websocket-accept'] ~= expected_accept then     local msg = 'Websocket Handshake failed: Invalid Sec-Websocket-Accept (expected %s got %s)'     return nil,msg:format(expected_accept,headers['sec-websocket-accept'] or 'nil'),headers   end   self.state = 'OPEN'   return true,headers['sec-websocket-protocol'],headers end local extend = function(obj)   obj.state = 'CLOSED'   obj.receive = receive   obj.send = send   obj.close = close   obj.connect = connect   return obj end local client_copas = function(timeout)   local copas = require('copas')   local self = {}   self.sock_connect = function(self,host,port)     self.sock = socket.tcp()     self.sock:settimeout(timeout or 5)     local _,err = copas.connect(self.sock,host,port)     if err and err ~= 'already connected' then       self.sock:close()       return nil,err     end   end   self.sock_send = function(self,...)     return copas.send(self.sock,...)   end   self.sock_receive = function(self,...)     return copas.receive(self.sock,...)   end   self.sock_close = function(self)     self.sock:close()   end   self = extend(self)   return self end local client_sync = function(timeout)   local self = {}   self.sock_connect = function(self,host,port)     self.sock = socket.tcp()     self.sock:settimeout(timeout or 5)     local _,err = self.sock:connect(host,port)     if err then       self.sock:close()       return nil,err     end   end   self.sock_send = function(self,...)     return self.sock:send(...)   end   self.sock_receive = function(self,...)     return self.sock:receive(...)   end   self.sock_close = function(self)     self.sock:close()   end   self = extend(self)   return self end local client = function(mode, timeout)   if mode == 'copas' then     return client_copas(timeout)   else     return client_sync(timeout)   end end return {   client = client,   CONTINUATION = CONTINUATION,   TEXT = TEXT,   BINARY = BINARY,   CLOSE = CLOSE,   PING = PING,   PONG = PONG }
Reply
#17
(20.03.2018, 14:27)admin Wrote: Attached is websocket client library ported from https://github.com/lipp/lua-websockets/

Main changes:
1. Support for basic auth in URL
2. Receive timeout does not close the websocket connection
3. Server support is removed

Add it as a user library named websocket

Minimal example (resident script):
Code:
123456789
if not ws then   ws = require('user.websocket')   url = 'ws://admin:admin@192.168.1.12/apps/localbus.lp'   -- mode is either sync or copas, second parameter sets timeout in seconds   client = ws.client('sync', 10)   client:connect(url) end log(client:receive())

Hi Admin,
Can I ask what the websocket.lua is meant for? What do you do with that?
Reply
#18
This is a client library for connecting to a websocket server.
Reply
#19
refreshing old thread as i'm having troubles implementing websocket client. The problem is that client does not timeout – i.e. it runs indefinitely until it received data from the socket, and also it does not timeout if socket disconnects (for ex. when server this client connects to goes offline).
i've traced it down to websockets library, specifically to copas.receive() call which hangs there indefinitely. Is there a way to make it work? It seems this goes into the library itself, which is built-in into LM and doesn't seem to be up to date.
Reply
#20
You can add newer copas library as a user library. It requires some extra Lua libraries which can added the same way. Just make sure to all require calls reference 'user.libname' instead of 'libname'.
Built-in copas in LM won't be upgraded to keep backward compatibility with existing scripts that rely on a certain behavior.
Reply


Forum Jump: