I was successful in making the squeezebox plugin work in altui/openLuup!
Solution
Open device file D_SqueezeboxControl1.xml
Add the following as a child node of <device></device>
<protocol>crlf</protocol>
The file should be similar to this:
[code]<?xml version="1.0"?>
2
0
urn:schemas-micasaverde-com:device:SqueezeBox:1
SqueezeBox Controller
D_SqueezeboxControl1.json
crlf
1
urn:schemas-micasaverde-com:service:HaDevice:1
urn:micasaverde-com:serviceId:HaDevice1
S_HaDevice1.xml
urn:schemas-micasaverde-com:service:SqueezeBox:1
urn:micasaverde-com:serviceId:SqueezeBox1
S_SqueezeboxControl1.xml
I_SqueezeboxControl1.xml
[/code]
Explanation:
The issue wasn’t on the receive end, nor was it in the tcp stack, etc. It was actually caused by not terminating the command string sent to the squeezebox server, which in turn was buffering commands in a single line and never replying. This lead to the socket timing out in the end. After analyzing the io code under openLuup, the solution became obvious
local function send (sock, data, protocol)
local eol = {cr = "\r", crlf = "\r\n"} -- TODO: 'stxetx' mode for write NOT currently supported
local fmt = "message length: %s, bytes sent: %d, status: %s %s"
if eol[protocol] then data = data .. eol[protocol] end
local status, msg, last = sock: send (data) -- send the message
_log (fmt: format (#data, status or last-1, msg or "OK", tostring(sock)), "luup.io.write")
return status
end
As it turns out, the Squeezebox plugin does not have a default protocol defined and therefore relies on ALTUI’s protocol during device load. As you might have guessed if you read up till this point, ALTUI’s default protocol is “cr”. Adding protocol “crlf” as the Squeezebox protocol means each command will get “\r\n” appended and will make the squeezebox reply back. There is no difference in implementation on the receiving end (there is no need as the lua socket ignores asccii 13 anyways).
I’m happy to report that with this simple change the Squeezebox plugin can be added to the list of compatible
Bonus points: test code which helped me diagnose. Note that each command is terminated with \n
socket = require('socket')
function sleep(n)
print("sleeping "..n.." sec")
os.execute("sleep " .. tonumber(n))
print("done sleeping")
end
local ok, msg,sock2, recvt
sock, msg = socket.tcp ()
if sock then
sock:settimeout (5)
sock:setoption ("tcp-nodelay", true) -- so that alternate read/write works as expected (no buffering)
sock:setoption ("keepalive", true) -- keepalive, thanks to @martynwendon for testing this solution
ok, msg = sock:connect ('192.168.99.99', 9090)
end
sleep(1)
print(tostring(sock))
sock:send("listen 1\n")
sock:send("player count ?\n")
sleep(1) -- simulate some scheduler activity in openluup
recvt = socket.select ({sock}, nil, 0.5) -- wait for something to happen (but not for too long)
while table.getn(recvt)>0 do
sleep(1) -- simulate some scheduler activity in openluup
for _,sock2 in ipairs (recvt) do
local data, err = sock: receive "*l"
print ("socket_callback -> "..tostring(sock2) .. " data = " .. data .. " error = " .. (err or "?"))
end
recvt = socket.select ({sock}, nil, 0.5) -- wait for something to happen (but not for too long)
end
sock:close()
output
lua testsocket.lua
sleeping 1 sec
done sleeping
tcp{client}: 0x307970
sleeping 1 sec
done sleeping
sleeping 1 sec
done sleeping
socket_callback -> tcp{client}: 0x307970 data = listen 1 error = ?
sleeping 1 sec
done sleeping
socket_callback -> tcp{client}: 0x307970 data = player count 7 error = ?
can we mark the subject as RESOLVED and post the fix at the top?