14.12.2015, 13:22
CoolMasterNet device uses TCP instead of RS-232 for communication, so we've updated the script to work with IP.
1. Create a resident script with 0 sleep interval, change 192.168.3.20 to CoolMasterNet device IP. Status information is polled every 3 seconds.
2. Create objects:
All objects must have VRV tag set.
Set object names, using the following scheme (full address and function):
L1.101 on/off - 1-bit, unit on/off control
L1.101 setpoint - unit setpoint, 2-byte floating point
L1.101 mode - unit mode, 1-byte (0 = cool, 1 = heat, 2 = fan, 3 = dry, 4 = auto)
L1.101 fspeed - fan speed, 1-byte (0 = low, 1 = medium, 2 = high, 3 = top, 4 = auto)
L1 on/off - 1-bit, on/off control for all units on a given line
L* on/off - 1-bit, on/off control for all units on all lines
You can create status objects by adding "status" to object name, example:
L1.101 on/off status
L1.101 setpoint status
L1.101 mode status
L1.101 fspeed status
L1.101 temp status - room temperature, 2-byte floating point
1. Create a resident script with 0 sleep interval, change 192.168.3.20 to CoolMasterNet device IP. Status information is polled every 3 seconds.
2. Create objects:
All objects must have VRV tag set.
Set object names, using the following scheme (full address and function):
L1.101 on/off - 1-bit, unit on/off control
L1.101 setpoint - unit setpoint, 2-byte floating point
L1.101 mode - unit mode, 1-byte (0 = cool, 1 = heat, 2 = fan, 3 = dry, 4 = auto)
L1.101 fspeed - fan speed, 1-byte (0 = low, 1 = medium, 2 = high, 3 = top, 4 = auto)
L1 on/off - 1-bit, on/off control for all units on a given line
L* on/off - 1-bit, on/off control for all units on all lines
You can create status objects by adding "status" to object name, example:
L1.101 on/off status
L1.101 setpoint status
L1.101 mode status
L1.101 fspeed status
L1.101 temp status - room temperature, 2-byte floating point
Code:
if not client then
require('socket')
require('genohm-scada.eibdgm')
IP = '192.168.3.20'
PORT = 10102
-- knx <> coolmaster mapping
knxtocm = {}
cmtoknx = {}
function reverse(src)
local res = {}
for k, v in ipairs(src) do
res[ v ] = k - 1
end
return res
end
-- integer<>text mode/fspeed value mapping
modetotext = { 'cool', 'heat', 'fan', 'dry', 'auto' }
texttomode = reverse(modetotext)
speedtotext = { 'low', 'med', 'high', 'top', 'auto' }
texttospeed = reverse(speedtotext)
-- get all tagged objects and set mapping
for _, object in ipairs(grp.tag('VRV')) do
local address, fn, stat = unpack(object.name:split(' '))
-- status object
if stat then
if not cmtoknx[ address ] then
cmtoknx[ address ] = {}
end
cmtoknx[ address ][ fn ] = {
id = object.id,
value = grp.getvalue(object.id),
}
-- control object
else
knxtocm[ object.id ] = {
address = address,
fn = fn,
}
end
end
function checkerror(res, err)
if not res then
sock:close()
sock = nil
alert('CoolMaster error: %s', tostring(err))
end
return res, err
end
function connect()
sock = socket.tcp()
sock:settimeout(5)
return checkerror(sock:connect(IP, PORT))
end
function send(data)
if not sock then
connect()
end
if sock then
checkerror(sock:send(data))
end
end
-- read single line from socket
function readline()
if not sock then
connect()
end
if sock then
return checkerror(sock:receive())
end
end
function updateknx(address, fn, value, datatype)
local object = cmtoknx[ address ] and cmtoknx[ address ][ fn ] or nil
-- object not found
if not object then
return
end
-- no value or same value, no update required
if value == nil or object.value == value then
return
end
-- save new value and write to knx
object.value = value
grp.write(object.id, value, datatype)
end
function parseline(line)
local address, status, setpoint, temp, speed, mode
line = line:gsub('>', '')
address = line:sub(1, 6)
-- address is invalid, cannot parse line
if address:sub(1, 1) ~= 'L' then
return
end
-- on/off status
status = line:sub(8, 9):lower() == 'on'
updateknx(address, 'on/off', status, dt.bool)
-- setpoint is integer
setpoint = line:sub(12, 13)
setpoint = tonumber(setpoint)
updateknx(address, 'setpoint', setpoint, dt.float16)
-- room temp is float, separated by comma
temp = line:sub(16, 17)
temp = tonumber(temp)
updateknx(address, 'temp', temp, dt.float16)
-- speed: low, med, high, auto
speed = line:sub(20, 23):lower():trim()
updateknx(address, 'fspeed', texttospeed[ speed ], dt.uint8)
-- mode: cool, heat, fan, dry, auto
mode = line:sub(25, 28):lower():trim()
updateknx(address, 'mode', texttomode[ mode ], dt.uint8)
end
-- read current status
function readstat()
send('ls\r\n')
-- read until error or end of stat
while true do
local line = readline()
-- timeout or end occured
if not line or line == 'OK' then
break
end
parseline(line)
end
end
-- handle group writes
function eventhandler(event)
local object, cmd, value, param
object = knxtocm[ event.dstraw ]
-- knx object not mapped, ignore
if not object then
return
end
-- on/off - boolean
if object.fn == 'on/off' then
value = knxdatatype.decode(event.datahex, dt.bool)
cmd = value and 'on' or 'off'
-- setpoint - floating point
elseif object.fn == 'setpoint' then
value = knxdatatype.decode(event.datahex, dt.float16)
param = string.format('%.1f', value)
cmd = 'temp'
-- mode (fan, dry, auto)
elseif object.fn == 'mode' then
value = knxdatatype.decode(event.datahex, dt.uint8)
cmd = modetotext[ value + 1 ]
-- speed (low, medium, high, auto)
elseif object.fn == 'fspeed' then
value = knxdatatype.decode(event.datahex, dt.uint8)
param = speedtotext[ value + 1 ]
if param then
cmd = 'fspeed'
end
end
-- got valid command
if cmd then
-- append address to command
cmd = cmd .. ' ' .. object.address
-- append additional parameter if set
if param then
cmd = cmd .. ' ' .. param
end
send(cmd .. '\r\n')
readline() -- command result
end
end
-- knx connection
client = eibdgm:new({ timeout = 1 })
client:sethandler('groupwrite', eventhandler)
-- start-up time
sec, usec = os.microtime()
end
-- handle knx
client:step()
-- read stats every 3 seconds
diff = os.udifftime(sec, usec)
if diff < 0 or diff >= 3 then
readstat()
sec, usec = os.microtime()
end