Telnet LUA Script, to turn on/off device

Hi,

I’m trying to control a media switching device via telnet, and while I can do this via an app on PC or my iPad, I’m not able to get it to work via Vera. I can connect, and I get the initial connection response back, but the command I’m sending is not being acted upon.

Please could someone have a look and let me know what I might be missing.
Telnet commands, page 12 - [broken link removed]

LUA Code…

local CommandtoSend = 'S POWER 1\r'
print("test:", CommandtoSend)
luup.log("test:", CommandtoSend)

-- Connect and Send Command

local socket = require("socket")
host = "192.168.102.128" -- CYP Presentation Device
c = assert(socket.connect(host, 23))
c:settimeout(10)

local sres, serr = c:send(CommandtoSend)
print("Send:", sres, serr)
luup.log("Send:", sres, serr)

local data, rerr = c:receive(170)
print("Receive:", data, rerr)
luup.log("Receive:", data, rerr)

c:close()

The output in the log.file or via Print under LuaTest, is the following…

> Print output
> test:     S POWER 1      
> Send:     10     
> Receive:     ==================================== 
> Telnet command service  
> command '?' for help  
> command 'quit' for quit  
> ==================

I have similar code for my matrix switch. I would first try changing this to S POWER 1\n and if that doesn’t work, try S POWER 1\r\n (mine likes \r\n). It’s more common for telnet sessions to use newline than carriage return, and some servers could require both.

Hi @rigpapa

Sadly neither one works. Any ideas what the issue might be, as the same ‘S POWER 1’ command works fine when sent via a telnet client … ?

Could it be a timing issue?

Screenshot of an off the shelf telnet client (app), where I connected first, and then typed the same command, which worked - maybe the timing between those two steps is better, because it’s slower…

image

Page 21 of the manual I shared states…

Note: All command will not be executed unless followed by a carriage return. Commands are case-insensitive. If the IP is changed then the IP Address required for Telnet access will also needs to be change accordingly.

Could be timing, could be not ready for data until the prompt is sent (related/similar to timing, so you may need to gobble up the receive data until it times out, then send your command).

If you ssh into your Vera, what happens if you do this on its command line:

echo "S POWER 1" | nc 192.168.102.128 23

Thanks, while It returns the same initial response (below), that route doesn’t seem to work either…

root@MiOS_ 51234567:~# echo “S POWER 1” | nc 192.168.102.128 23
====================================
Telnet command service
command ‘?’ for help
command ‘quit’ for quit
====================================
root@MiOS_51234567:~#

While I’m an eternal novice when it comes to coding, I had a go too at something that i hoped would introduce a delay, and then send the command. Sadly that didn’t work either, but no errors at least reported about the code :slight_smile:

My logic is, connect, wait a bit, then send the command and close.

local MakeConnection = '\n'

local socket = require("socket")
host = "192.168.102.128" --CYP
c = assert(socket.connect(host, 23))
c:settimeout(10)

local sres, serr = c:send(MakeConnection)
print("Send:", sres, serr)

local data, rerr = c:receive(170)
print("Receive:", data, rerr)

function poweron()
	local CommandtoSend = 'S POWER 1\r'
	print("Commandis:", CommandtoSend)

	-- Connect and Send Command

--	local socket = require("socket")
--	host = "192.168.102.128" --CYP
--	c = assert(socket.connect(host, 23))
--	c:settimeout(10)

	local sres, serr = c:send(CommandtoSend)
	print("Send:", sres, serr)

	local data, rerr = c:receive(170)
	print("Receive:", data, rerr)
	c:close()
end

luup.call_delay("poweron", 5)

Interesting, I didn’t know that could be a thing - I recall originally starting with c:receive(200) but that would always time out. A quick check online, suggests that the response the device sends back to me upon connection has 180 characters…

UPDATE: I updated the code above to c:receive(180) , but sadly no difference, it didn’t turn on

Always risky to specify the exact number of characters to receive.

  • Is there no recognisable terminator?
  • try reading one at a time to see if you receive anything.
  • try setting the socket option tcp-nodelay

Only says I need to end them with a carriage return

Sorry @akbooer, what do you mean ? One character at a time rather that 180 ?

Ok, and is that a case of just adding one line in, after the connection is made - as I have below ?

local socket = require("socket")
host = "192.168.102.128" --CYP
c = assert(socket.connect(host, 23))
c:setoption('tcp-nodelay', true)
c:settimeout(10)

Yes, exactly that… just to see if you get any response at all.

Yes. The LuaSocket documentation is not expansive on this topic, but ‘Nagle’s algorithm’ relates to buffering of data on the socket under the condition of alternating reads and writes… which is exactly what you’re doing.

Alternately, rather than reading one at a time, you could use receive() correctly. It returns three arguments: the data (if the length condition is met), the error, and the partial result. When a timeout occurs (return value 2 == "timeout"), or any other error, any partial data received will be in the third parameter. If the length of receive specified exceeds the available data, a “timeout” occurs and the partial result contains what was available. So you can read in larger chunks than one byte. And you current code ignores the partial result, so you think you’re getting nothing back because of the error, when in fact, you are just ignoring it.

1 Like

Thanks all, please keep in mind my limited xperience in all this :slight_smile:

To help my learning, looking up "client:receive() " I found this on GitHub, which claims to be -

“Identical to client:receive(), except that if partial data was read, it returns it instead of returning nil. This is convenient for binary/non-line-delimited protocols.“

function read(client, pattern, prefix)
  local data, emsg, partial = client:receive(pattern, prefix)
  if data then
    return data
  end
  if partial and #partial > 0 then
    return partial
  end
  return nil, emsg
end

Is that of use/interest?

Should I use the above read function, and add 2 others; (1) connect (2) send a command ?

function connect()
  local socket = require("socket")
  host = "192.168.102.128" --CYP
  c = assert(socket.connect(host, 23))
  c:setoption('tcp-nodelay', true)
  c:settimeout(10)
end

function send_power_on()
  local CommandtoSend = 'S POWER 1\r'
  print("Command is:", CommandtoSend)
  local sres, serr = c:send(CommandtoSend)
  print("Send:", sres, serr)
  c:close()
end

Can I then do one function after another - e.g. connect >> read >> send command >> read

That looks like it will get the job done (the read() function). You do not need to make functions for the other steps, that only makes it more complicated/minimal re-use benefit in this case.

Ok, factoring all the advice so far, I’ve got this, but I keep getting an error in the code…

Code error: Line 20: ‘’ expected near ‘print’

local CommandtoSend = 'S POWER 1\r' 
print("Command is:", CommandtoSend) 

local socket = require("socket") 
host = "192.168.102.128" -- CYP 

c = assert(socket.connect(host, 23)) 
c:setoption('tcp-nodelay', true) 
c:settimeout(10) 

local data, emsg, partial = client:receive(pattern, prefix)
  if data then
    return data
  end
  if partial and #partial > 0 then
    return partial
  end
  return nil, emsg

print("data=", data)
print("emsg=", emsg)
print("partial=", partial)

local sres, serr = c:send(CommandtoSend) 
print("Send:", sres, serr)

local data, emsg, partial = client:receive(pattern, prefix)
  if data then
    return data
  end
  if partial and #partial > 0 then
    return partial
  end
  return nil, emsg

print("data=", data)
print("emsg=", emsg)
print("partial=", partial)

c:close()

Your print statement comes directly after a return statement. This is illegal syntax since there’s obviously no way to reach that statement.

You’ve left the function wrapper off from your copied code.

Thanks @akbooer

I assumed I shouldn’t use the function wrapper to avoid making it too complicated, but ok, I’ve tried to address that now; and while the code runs without any errors, it does not return anything.

local CommandtoSend = 'S POWER 1\r' 
print("Command is:", CommandtoSend) 

function read(client, pattern, prefix)
	local data, emsg, partial = c:receive(pattern, prefix)
 	if data then
		return data
	end
	if partial and #partial > 0 then
		return partial
	end
		return nil, emsg
end

local socket = require("socket") 
host = "192.168.102.128" -- CYP 
c = assert(socket.connect(host, 23)) 
c:setoption('tcp-nodelay', true) 
c:settimeout(10) 

read()

print("data=", data)
print("emsg=", emsg)
print("partial=", partial)

local sres, serr = c:send(CommandtoSend) 
print("Send:", sres, serr)

read()

print("data=", data)
print("emsg=", emsg)
print("partial=", partial)

c:close()

LuaTest output is as follows…

Print output

Command is: S POWER 1
data=
emsg=
partial=
Send: nil closed
data=
emsg=
partial=

You aren’t receiving the returned values from read(), and the variables used inside the function are local, so they are out of scope where you are trying to print them. You also are not passing in any of the required arguments to read().

Try changing your read() statement (the one right before the print statements only) to:

local data,emsg = read(c,2048,nil)

Well, let me mention to you, for at least the third time*, that if you’re going to do this type of stuff, then you really need to knuckle down and get to know Lua a little better… :wink:

*see:

To be honest it’s likely way more than three times :joy:
(I can’t believe you would drag up our past like that :wink:)

Sadly it’s the same old story with me; a recurring theme - my job/workload and other personal matters just take up so much of my time - but then when I find nice extended periods to commit/focus, i do feel like I’m getting better at Lua, only then for something to come up that requires all my time again, and I end up taking loads of steps back…

You’ll be pleased to hear that I can do a few things now in Lua, and I assumed this telnet one, would be the same as the hdmi matrix set up (another one you helped me with I think :wink:) but it didn’t work and I couldn’t find out why - i had to submit a post here.

1 Like

Thanks @rigpapa - I’ve updated the read() items to what you suggested above, and while it took some time to run, it sadly didn’t change anything on the presentation switch.

FYI - LuaTest reported the following

> **Results**
> No errors
> Runtime: 20022.7 ms
> Code returned: nil
> 
> **Print output**
> Command is:     S POWER 1      
> data=     ==================================== 
> Telnet command service  
> command '?' for help  
> command 'quit' for quit  
> ==================================== 
> 
> emsg=     
> partial=     
> Send:     10     
> data=     
> emsg=     timeout     
> partial=

Any further thoughts ?