CoolMasterNet integration
Example: CoolMasterNet integration
Task
CoolMasterNet device uses TCP instead of RS-232 for communication (as showed in this example), so we’ve updated the script to work with IP.
Create objects on LogicMachine
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
Note! L1 corresponds to “Line 1” and 101 corresponds to address of A/C unit which are both programmed on CoolMaster device.
Resident script
Create a resident script with 0 sleep interval, change 192.168.3.20 to CoolMasterNet device IP. Status information is polled every 3 seconds.
- 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
-
- -- integertext 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 echo
- result = readline() -- command result
- readline() -- new line prompt
- 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