Luup plugins and bi-directional communication with a remote server

I am trying to write a plugin which communicates with a remote Squeezebox Server.

Definition file:

<?xml version="1.0" encoding="UTF-8"?> 0 1 urn:schemas-ap15e-com:device:SqueezeboxUI:1 Squeezebox UI for Vera Ap15e http://www.ap15e.com Line-oriented UI for Vera using Squeezebox Server CLI via TCP port 9090 LOUIS4Vera (Line-Oriented User Interface on Squeezebox) 0.1 uuid:upnp-ap15e-1_0-0000000000001 raw 0 I_LOUIS4Vera1.xml

Implementation file (version 1):

<?xml version="1.0" encoding="UTF-8"?> raw function LOUIS4VeraStartup(DevNo) luup.log("LOUIS4Vera: Starting ... device #"..tostring(DevNo).." starting up with id "..luup.devices[DevNo].id) local command = '00:04:20:XX:XX:XX subscribe button\n' if (luup.io.write(command, DevNo) == false) then luup.log("LOUIS4Vera: Cannot send command " .. command .. " communications error", 1) --luup.set_failure(true) end end luup.log("LOUIS4Vera: Incoming " .. tostring(lul_data)) LOUIS4VeraStartup

Implementation file (version 2):

<?xml version="1.0" encoding="UTF-8"?> raw function LOUIS4VeraStartup(DevNo) luup.log("LOUIS4Vera: Starting ... device #"..tostring(DevNo).." starting up with id "..luup.devices[DevNo].id) local SBS_IP_address = '192.168.178.22' local playerid = '00:04:20:XX:XX:XX' local SBS_CLI_port = 9090 socket=require('socket') local SBS_socket_timeout = 0.1 local client = socket.connect( SBS_IP_address, SBS_CLI_port ) client:settimeout( SBS_socket_timeout ) client:send(playerid..' subscribe button\n') end luup.log("LOUIS4Vera: Incoming " .. tostring(lul_data)) LOUIS4VeraStartup

Version 1 (luup.io.write) does not work (from Squeezebox Server log, reversed timeline):

29208: [10-05-28 15:20:23.1742] Slim::Plugin::CLI::Plugin::client_socket_close (296) Closed connection with 192.168.178.34:4338 (0 active connections) 29207: [10-05-28 15:20:23.1640] Slim::Plugin::CLI::Plugin::client_socket_close (278) Begin Function 29206: [10-05-28 15:20:23.1574] Slim::Plugin::CLI::Plugin::client_socket_read (332) Connection with 192.168.178.34:4338 half-closed by peer 29205: [10-05-28 15:20:23.1506] Slim::Plugin::CLI::Plugin::client_socket_read (307) Begin Function

Version 2 (socket.connect) does work (from Squeezebox Server log, reversed timeline):

29143: [10-05-28 15:18:21.0200] Slim::Plugin::CLI::Plugin::client_socket_buf_parse (380) 192.168.178.34:4338 29142: ] 29141: [10-05-28 15:18:21.0121] Slim::Plugin::CLI::Plugin::client_socket_read (341) 192.168.178.34:4338 - Buffered [00:04:20:XX:XX:XX subscribe button 29140: [10-05-28 15:18:21.0050] Slim::Plugin::CLI::Plugin::client_socket_read (307) Begin Function 29139: [10-05-28 15:18:20.9881] Slim::Plugin::CLI::Plugin::cli_socket_accept (257) Accepted connection from 192.168.178.34:4338 (1 active connections) 29138: [10-05-28 15:18:20.9641] Slim::Plugin::CLI::Plugin::cli_socket_accept (225) Begin Function

The block never gets called (neither with version 1 nor with version 2).

Questions:

[ul][li]What is the difference between luup.io.write and direct communication via a socket?[/li]

[li]Why doesn’t the block work?[/li]

[li]Which syntax is the correct one: [Lua code] or [Lua code]?[/li][/ul]

To answer questions 1 & 2 myself:

luup.io.write(command) without luup.io.open does not work. May be caused by an IP address with a port number (IP is 192.168.178.22:9090, so Vera might fail to get the socket connection right). New MCV bug #1105.

Let’s use luup.io.open - but luup.devices[DevNo].id fails to return the IP address. New MCV bug #1106.

We have to resort to user-defined variables SBS_IP_Address and SBS_CLI_Port …

The following code does work (even the block gets called):

<?xml version="1.0" encoding="UTF-8"?> crlf function LOUIS4VeraStartup( deviceId )
     selfId = 'urn:schemas-ap15e-com:deviceId:SqueezeboxUI1'
     
     variables_set = true;

     SBS_IP_address = luup.variable_get( selfId, "SBS_IP_address", deviceId )
     if ( SBS_IP_address == nil ) or ( SBS_IP_address == '' ) 
      then
       luup.variable_set( selfId, "SBS_IP_address", '', deviceId)
       variables_set = false
      end

     SBS_CLI_port = luup.variable_get( selfId, "SBS_CLI_port", deviceId )
     if ( SBS_CLI_port == nil ) or ( SBS_CLI_port == '' ) 
      then
       luup.variable_set( selfId, "SBS_CLI_port", '', deviceId)
       variables_set = false
      end

     SBS_playerid = luup.variable_get( selfId, "SBS_playerid", deviceId )
     if ( SBS_playerid == nil ) or ( SBS_playerid == '' ) 
      then
       luup.variable_set( selfId, "SBS_playerid", '', deviceId)
       variables_set = false
      end

     luup.log("LOUIS4Vera: Starting ... device #"..tostring(deviceId).." starting up with id "..luup.devices[deviceId].id)

     if variables_set
      then
       luup.io.open( SBS_handle, SBS_IP_address, SBS_CLI_port )
       command = SBS_playerid ..' subscribe button\n'
       res = luup.io.write(command, SBS_handle )
       if ( res == false ) or ( res == nil )
        then 
         luup.log( "LOUIS4Vera: Cannot send command " .. command .. " - communication error" )
         luup.task( "Communication with SBS doesn't work. Is SBS running?", 2, "LOUIS4Vera", -1 )
         luup.set_failure( true, deviceId )
         return false
        end
      else
       luup.log( "LOUIS4Vera: Variables SBS_IP_address, SBS_CLI_port, SBS_playerid not set" )
       luup.task( "Set the variables SBS_IP_address, SBS_CLI_port, SBS_playerid.", 2, "LOUIS4Vera", -1 )
       luup.set_failure( true, deviceId )
       return false
      end
    end
</functions>
<incoming>
    <lua>
      luup.log("LOUIS4Vera: Incoming " .. tostring(lul_data))
    </lua>
</incoming>
<startup>LOUIS4VeraStartup</startup>

Question 3 remains open, but seems to work …

@Ap15e,
When I’m doing TCP stuff, I use:

luup.io.open(lul_device, ipAddress, ipPort)

as in seen in my Onkyo plugin:

http://code.mios.com/trac/mios_onkyo-media-control/browser/I_OnkyoReceiver1.xml#L71

I don’t think you can format the IP Address attribute as “:” in the Vera UI (but I could be wrong)

Note that luup.devices[…].id is just the DeviceId, you want luup.devices[…].ip, which is the [standard] IP Address variable that’s presented in Advanced.

The Onkyo driver is working fine, so it’s probably a good sample to look at for this type of stuff.

Generally the difference between model 1 and model 2 is that in Model 1, Vera looks after maintaining the connection and you handle the data. In Model 2, you handle both the connection and the data.

Also, in Model 1, Vera does some rudimentary chunking of data read using the “boundaries” specified in the tag. If protocol=cr, you get a line’s worth of data (and the CR is stripped out). If protocol=stxetx, then you get all data between the STX and ETX characters. For RAW, you get every byte on the stream, one call (to the code in the block) for every byte.

Similarly, when using luup.io.write() calls, it wrappers the data with the delimiter

I don't think you can format the IP Address attribute as ":" in the Vera UI (but I could be wrong)

It does work for my Squeezebox cover art hack:
http://forum.micasaverde.com/index.php?topic=3604.0

I think Vera does not check for the correct syntax of IP addresses, so it depends on the context whether “:” does work or not.

Note that luup.devices[...].id is just the DeviceId, you want luup.devices[...].ip, which is the [standard] IP Address variable that's presented in Advanced.

Yes, you are right.

My plugin is working fine now.