You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

936 lines
25 KiB

#!/usr/bin/python
import sys
import struct
import argparse
import logging
from logging import debug, warning, error
from enum import IntEnum, IntFlag
import mido
# the default rtmidi seems to have terrible memory corruption issues
mido.set_backend('mido.backends.portmidi')
class cmdId(IntEnum):
SYS = 0x00
DEVICE = 0x01
INFO = 0x02
PRESET = 0x03
PHYSPORT = 0x04
PORTINFO = 0x05
MIDIPORT = 0x06
class ackId(IntEnum):
OK = 0x00
ERR_1 = 0x01
ERR_2 = 0x02
ERR_CMD = 0x03 # ??? on unknown command
ERR_4 = 0x04
ERR_5 = 0x05
ERR_6 = 0x06
ERR_7 = 0x07
ERR_8 = 0x08
ERR_PORT = 0x09 # ??? on invalid port number
ERR_a = 0x0a
ERR_NAME = 0x0b # ??? on name too long
ERR_c = 0x0c
ERR_d = 0x0d
ERR_e = 0x0e
ERR_PRESET = 0x0f # ??? on invalid preset number
class cmdFl(IntFlag):
VARARG = 0x01 # ??
GET = 0x02
SET = 0x10
RESPONSE = 0x40
QUERY = (GET|VARARG)
class sysFilter1(IntFlag):
CLOCK = 0x0001
START = 0x0002
CONTINUE = 0x0004
STOP = 0x0008
QUARTER_FRAME = 0x0010
SONG_POSITION = 0x0020
SONG_SELECT = 0x0040
class sysFilter2(IntFlag):
TUNE_REQUEST = 0x01
ACTIVE_SENSE = 0x02
RESET = 0x04
SYSEX = 0x08
class chnFilter(IntFlag):
NOTE_ON = 0x01
NOTE_OFF = 0x02
POLY_KEY = 0x04
CONTROL_CHANGE = 0x08
PROGRAM_CHANGE = 0x10
CHANNEL_PRESSURE= 0x20
PITCH_BEND = 0x40
class portId(IntEnum):
TOTAL = 0x01
DIN = 0x02
INTERNAL = 0x03
USB_DEVICE = 0x04
USB_HOST = 0x05
RTP = 0x06
class infoId(IntEnum):
DEVICE_NAME = 0x01
MANUFACTURER = 0x02
SERIAL = 0x04
FW_VERSION = 0x05
HW_VERSION = 0x06
NUM_PRESETS = 0x14
class remapId(IntEnum):
NOTE_ON = 0x01
NOTE_OFF = 0x02
POLY_KEY = 0x03
CONTROL_CHANGE = 0x04
PROGRAM_CHANGE = 0x05
CHANNEL_PRESSURE= 0x06
PITCH_BEND = 0x07
sysFltMap = {
1: sysFilter1,
2: sysFilter2,
}
chnFltMap = {
1: chnFilter,
}
class MioError(Exception):
pass
class ProtocolError(Exception):
pass
class mioCmd(object):
def __init__(self, flags, cmd, args):
self.flags = flags
self.cmd = cmd
self.args = args
def msg(self):
cmd = bytearray((self.flags, self.cmd, len(self.args)))
for a in self.args:
cmd.extend(a)
clen = encode7(len(cmd), 2)
return clen + cmd
def dump(self):
if self.flags & cmdFl.RESPONSE:
dd = '<<'
else:
dd = '>>'
print(dd, '%s (%s)' % (self.cmd, self.flags))
n = 1
for a in self.args:
print(' arg %d:' % n)
print(' ', a)
print(' ', a.hex())
print(' ', toascii(a))
def chk(self, other):
if self.cmd == cmdId.SYS and not self.flags & cmdFl.VARARG:
try:
c = cmdId(self.args[0])
except ValueError:
c = self.args[0]
try:
v = ackId(self.args[1])
except ValueError:
v = self.args[1]
if c != other.cmd or v != 0:
raise MioError('error code', c, v)
class mioRsp(mioCmd):
def __init__(self, flags, cmd, nargs, args):
if not flags & cmdFl.RESPONSE:
raise ProtocolError('unknown response: %s', sx.hex())
ret = []
dd = "<<"
if flags & cmdFl.VARARG:
debug('%s command: %s (%s) args %d flags: %s (%s)',
dd, hex(cmd), cmd,
nargs, hex(flags), flags)
a = args
for i in range(0, nargs):
alen = a[0]
arg = a[1:alen]
ret.append(arg)
debug('%s\targ #%d len %d %s', dd, i+1, alen, arg.hex(':'))
debug('%s\t %s', dd, toascii(arg))
a = args[alen:]
else:
debug('%s command: %s (%s) flags: %s (%s)',
dd, hex(cmd), cmd,
hex(flags), flags)
debug('%s\targ : %s', dd, args.hex(':'))
debug('%s\t : %s', dd, toascii(args))
ret = args
return super().__init__(flags, cmd, ret)
def value(self, fmt='b'):
val = None
if self.flags & cmdFl.VARARG:
# whole lotta crap for just one value...?
#for r in self.args[-1:]:
v1 = self.args[-1]
vn = v1[1]
if vn > 0:
vlen = v1[3]
ret = v1[:2]
val = v1[4:]
debug('response %s %s', ret, val)
debug(' s -> %s', val.decode('ascii'))
if len(val) == 1:
debug(' h -> 0x%s (%d)' % (val.hex(), ord(val)))
# default 'b' needs no conversion
if fmt == 's':
val = val.decode('ascii')
elif fmt == 'o':
val = ord(val)
else:
debug('empty arg %s', v1)
return val
def route2set(rmap):
s = set()
offset = 1
for i in range(0, len(rmap)):
v = rmap[i]
for i in range(0, 4):
if v & (1 << i):
pno = offset + i
s.add(pno)
offset += 4
return s
def set2route(portset, maxport):
rmap = bytearray(maxport)
for p in portset:
v = p - 1
offset = v // 4
n = (v - (offset * 4))
val = 1 << n
rmap[offset] |= val
return rmap
def flt2map(emap, flt):
fmap = {}
n = len(flt) // 2
for i in range(1, n + 1):
fv = flt[i*2-1]
fmap[i] = emap[i](fv)
return fmap
def str2map(emap, arg):
args = arg.split(',')
fmap = {}
for k in emap.keys():
if k not in fmap:
fmap[k] = 0
for a in args:
# stupid IntFlag does not support 'in' operator
try:
v = emap[k][a]
fmap[k] |= v
except KeyError:
pass
return fmap
def map2str(fmap):
def e2s(e):
s = '%s' % e
return s.split('.')[1].split('|')
vals = []
for i in range(1, len(fmap)+1):
if fmap[i]:
vals.extend(e2s(fmap[i]))
return vals
def map2ba(fltmap):
ba = bytearray()
for k in fltmap.keys():
ba.append(k)
ba.append(fltmap[k])
return ba
def toascii(u):
a = []
for c in u[:]:
if c < 32 or c > 128:
a.append('.')
else:
a.append(chr(c))
return ''.join(a)
def decode7(bd):
c = 0
for v in bd[:]:
c <<= 7
c += v
return c
def encode7(val, size = 2):
bd = bytearray()
while val > 0:
c = val & 0x7f
val >>= 7
bd.insert(0, c)
while len(bd) < size:
bd.insert(0, 0x00)
return bd
def decodeip(val):
return tuple(decode7(val).to_bytes(4, 'big'))
def encodeip(ip):
t = []
for s in ip.split('.'):
t.append(int(s))
i = int.from_bytes(t, 'big')
return encode7(i, 4)
def chksum(data):
cs = 0
for v in data:
cs += v
if cs > 127:
cs -= 128
if cs != 0:
cs = 128 - cs
return cs
def makearg(data):
arg = bytearray(data)
arg.insert(0, len(arg) + 1)
return arg
def makeSysex(devid, fooid, txnid, cmd):
mfid = bytearray((0x00, 0x01, 0x73))
msgcls = bytearray((0x7d,))
body = fooid + encode7(txnid, 2) + cmd
debug("cmd: %s" % cmd)
sysex = mfid + msgcls + devid + body
sysex.append(chksum(body))
msg = mido.Message('sysex', data = sysex)
return msg
def parseSysex(sysex):
sx = bytearray(sysex.data)
head = sx[:4]
addr = sx[4:17]
body = sx[17:]
mfid = head[:3]
cls = head[3]
if cls != 0x7d:
debug('unknown response: %s', sx.hex())
return
devid = decode7(addr[:7])
unknown = decode7(addr[7:])
txid = decode7(body[:2])
msg = body[2:-1]
dlen = decode7(msg[:2])
flags = cmdFl(msg[2])
cmd = cmdId(msg[3])
nargs = msg[4]
args = msg[5:]
csum = body[-1]
cc = chksum(sx[4:-1])
if cc != csum:
raise ProtocolError('checksum mismatch: 0x%x != 0x%x' % (cc, csum))
return mioRsp(flags, cmd, nargs, args)
class mioX(object):
def __init__(self):
self.inp = None
self.out = None
self.ports = {}
self.txid = 0x0000
def connect(self, pname):
for n in mido.get_input_names():
if n.startswith(pname):
self.inp = mido.open_input(n)
for n in mido.get_output_names():
if n.startswith(pname):
self.out = mido.open_output(n)
debug("IN", mido.get_input_names())
debug("OUT", mido.get_output_names())
debug('inport %s, outport %s', self.inp, self.out)
if not (self.out and self.inp):
raise MioError('connect failed %s', pname)
# not currently interesting
#d = self.getDevice()
def gettxn(self):
self.txid += 1
if self.txid == 16384:
self.txid = 0
return self.txid
def send(self, cmd):
devid = bytearray(7)
fooid = bytearray(6)
txnid = self.gettxn()
msg = makeSysex(devid, fooid, txnid, cmd.msg())
debug("SYSEX:\n%s", bytearray(msg.data).hex())
self.out.send(msg)
def recv(self):
msg = self.inp.receive()
b = bytearray(msg.data)
rsp = parseSysex(msg)
return rsp
def call(self, flags, cmd, args, fmt='b'):
sc = mioCmd(flags, cmd, args)
self.send(sc)
rsp = self.recv()
rsp.chk(sc)
#sc.dump()
#rsp.dump()
return rsp.value(fmt)
def getDevice(self, infoid=0x04):
arg1 = makearg((0x3, 0x1, 0x4, 0x01, infoid, 0x0))
return self.call(cmdFl.VARARG, cmdId.DEVICE, [arg1])
def getInfo(self, infoid, fmt='b'):
arg1 = makearg((0x1, 0x1, infoid))
return self.call(cmdFl.QUERY, cmdId.INFO, [arg1], fmt)
def getFwVer(self):
b = self.getInfo(infoId.FW_VERSION, fmt='b')
return '%s.%s.%s.%s' % (b[0], b[1], b[2], b[3])
def getDeviceName(self):
return self.getInfo(infoId.DEVICE_NAME, fmt='s')
def getNPresets(self):
return self.getInfo(infoId.NUM_PRESETS, fmt='o')
def getPresetName(self, n):
arg1 = makearg((0x4, 0x1, 0x9, n))
arg2 = makearg((0x1, 0x1, 0x2))
return self.call(cmdFl.QUERY, cmdId.PRESET, [arg1, arg2], fmt='s')
def getPresets(self):
p = {}
for i in range(1, self.getNPresets()+1):
p[i] = self.getPresetName(i)
return p
def getCurPreset(self):
arg1 = makearg((0x1, 0x1, 0x1))
return self.call(cmdFl.QUERY, cmdId.PRESET, [arg1], fmt='o')
# This sets the name in a temporary buffer, needs savePreset to save
def setPresetName(self, name=None):
if not name:
name = self.getPresetName(n)
n = bytearray(name.encode('ascii'))
n.insert(0, 0x2)
subarg = makearg(n)
arg1 = makearg(bytearray((0x03, 0x01)) + subarg)
return self.call(cmdFl.SET, cmdId.PRESET, [arg1])
def getNPorts(self, infoid=portId.TOTAL):
arg1 = makearg((0x1, 0x1, infoid))
return self.call(cmdFl.QUERY, cmdId.PORTINFO, [arg1], fmt='o')
def getNUsbHost(self):
arg1 = makearg((0x04, 0x02, 0x03, 0x04, 0x04, 0x01))
arg2 = makearg((0x01, 0x01, 0x22))
return self.call(cmdFl.QUERY, cmdId.PHYSPORT, [arg1, arg2], fmt='o')
def getUsbInfo(self, n, infoid, fmt='b'):
arg1 = makearg((0x4, 0x03, 0x03, 0x04, 0x04, 0x01, 0x08, n))
arg2 = makearg((0x1, 0x1, infoid))
return self.call(cmdFl.QUERY, cmdId.PHYSPORT, [arg1, arg2], fmt)
def getUsbVendorName(self, n):
return self.getUsbInfo(p, 0x2a, fmt='s')
def getUsbDevName(self, n):
return self.getUsbInfo(p, 0x2b, fmt='s')
def getPortInfo(self, n, infoid, fmt='b'):
arg1 = makearg((0x4, 0x1, 0x5, n))
arg2 = makearg((0x1, 0x1, infoid))
return self.call(cmdFl.QUERY, cmdId.MIDIPORT, [arg1, arg2], fmt)
def setPortInfo(self, n, infoid, val):
if isinstance(val, str):
val = val.encode('ascii')
elif isinstance(val, int):
val = bytearray((val,))
debug("set info %d, %d, %s" % (n, infoid, val))
arg1 = makearg((0x4, 0x1, 0x5, n))
iarg = makearg(bytearray((infoid,)) + val)
arg2 = makearg(bytearray((0x3, 0x1,)) + iarg)
return self.call(cmdFl.SET, cmdId.MIDIPORT, [arg1, arg2])
def setPortChnInfo(self, n, chn, infoid, val):
arg1 = makearg((0x4, 0x2, 0x5, n, 0x6, chn))
iarg = makearg(bytearray((infoid,)) + val)
arg2 = makearg(bytearray((0x3, 0x1,)) + iarg)
return self.call(cmdFl.SET, cmdId.MIDIPORT, [arg1, arg2])
def getPortChnInfo(self, n, chn, infoid):
arg1 = makearg((0x4, 0x2, 0x5, n, 0x6, chn))
arg2 = makearg((0x1, 0x1, infoid))
return self.call(cmdFl.QUERY, cmdId.MIDIPORT, [arg1, arg2])
def getPortName(self, n):
return self.getPortInfo(n, 0x0a, fmt='s')
def setPortName(self, n, name):
# Auracle sets both - input and output sides I presume
self.setPortInfo(n, 0x0a, name)
self.setPortInfo(n, 0x0b, name)
def getPortConn(self, n):
# only really works for non-DIN ports
return (self.getPortInfo(n, 0x03, fmt='o') != 0)
def getPortRemap(self, n, chn):
r = self.getPortChnInfo(n, chn, 0x10)
rmap = {}
for i in range(0, len(r), 2):
e = remapId(r[i]).name
c = r[i+1]
if c != chn:
rmap[e] = c
return rmap
def setPortRemap(self, n, chn, rmap):
ba = map2ba(rmap)
return self.setPortChnInfo(n, chn, 0x10, ba)
def getPortRoute(self, n):
omap = self.getPortInfo(n, 0x07)
return route2set(omap)
def setPortRoute(self, n, s):
omap = self.getPortInfo(n, 0x07)
nmap = set2route(s, len(omap))
return self.setPortInfo(n, 0x07, nmap)
def getPortFilter(self, n, t=0x0c):
flt = self.getPortInfo(n, t)
fmap = flt2map(sysFltMap, flt)
return fmap
def getPortChnFilter(self, n, chn, t=0x0e):
flt = self.getPortChnInfo(n, chn, 0x0e)
fmap = flt2map(chnFltMap, flt)
return fmap
def getPorts(self, infoid=0x01):
if not self.ports:
n = self.getNPorts(infoid)
for i in range(1, n+1):
self.ports[i] = self.getPortName(i)
return self.ports
def getNetInfo(self, infoid, fmt='b'):
arg1 = makearg((0x04, 0x02, 0x03, 0x05, 0x04, 0x01))
arg2 = makearg((0x01, 0x01, infoid))
return self.call(cmdFl.QUERY, cmdId.PHYSPORT, [arg1, arg2], fmt)
def setNetInfo(self, infoid, val):
arg1 = makearg((0x04, 0x02, 0x03, 0x05, 0x04, 0x01))
iarg = makearg(bytearray((infoid,)) + val)
arg2 = makearg(bytearray((0x3, 0x1,)) + iarg)
return self.call(cmdFl.SET, cmdId.PHYSPORT, [arg1, arg2])
def getNetwork(self):
r = self.getNetInfo(0x34, fmt='o')
return ['static', 'dynamic'][r]
def setNetwork(self, conf=[]):
if len(conf) == 3:
val = bytearray()
for ip in conf:
e = encodeip(ip)
val += e
self.setNetInfo(0x35, val)
else:
self.setNetInfo(0x34, bytearray((0x0,)))
def getIP(self):
r = self.getNetInfo(0x32)
net = {
'ip' : decodeip(r[:5]),
'net' : decodeip(r[5:10]),
'gw' : decodeip(r[10:])
}
return net
def loadPreset(self, n=None):
if not n:
n = self.getCurPreset()
arg1 = makearg((0x6, 0x1, 0x5, 0x2, 0x43, 0x00, n))
r = self.call((cmdFl.SET|cmdFl.VARARG), cmdId.SYS, [arg1], fmt='b')
return (self.getCurPreset() == n)
def saveGlobal(self):
arg1 = makearg((0x6, 0x1, 0x4, 0x2, 0x2, 0x0))
return self.call((cmdFl.SET|cmdFl.VARARG), cmdId.SYS, [arg1], fmt='b')
def savePreset(self, n=None, name=None):
if not n:
n = mio.getCurPreset()
if name:
mio.setPresetName(args.name)
arg1 = makearg((0x6, 0x1, 0x5, 0x2, 0x03, 0x00, n))
return self.call((cmdFl.SET|cmdFl.VARARG), cmdId.SYS, [arg1], fmt='b')
def argRange(args):
rng = []
for p in args.split(','):
r = p.split('-')
if len(r) > 1:
ar = range(int(r[0]), int(r[1])+1)
else:
ar = r
for i in ar:
rng.append(int(i))
return rng
def portArgs(mio, args, defall=True):
allports = mio.getPorts()
ret = {}
if args.ports:
rng = argRange(args.ports)
ret = {}
for r in rng:
ret[r] = allports[r]
elif defall:
ret = allports
return ret
def chnArgs(args):
if args.channel:
chns = argRange(args.channel)
else:
chns = range(1, 17)
return chns
def printDict(data, prefix=''):
for d in data.keys():
print('%s%2s: %s' % (prefix, d, data[d]))
def printPresets(mio, args):
printDict(mio.getPresets())
def printPorts(mio, args):
printDict(portArgs(mio, args))
def printRoute(mio, n):
ports = mio.getPorts()
rts = mio.getPortRoute(n)
print('%2d: %s' % (n, ports[n]))
for r in rts:
print('\t --> %2d: %s' % (r, ports[r]))
def printRoutes(mio, args):
ports = portArgs(mio, args)
for p in ports:
printRoute(mio, p)
def printFilters(mio, args):
ports = portArgs(mio, args)
for p in ports:
print('%2d: %s' % (p, ports[p]))
fmap = mio.getPortFilter(p)
vals = map2str(fmap)
if vals:
print('\tSys: %s' % ','.join(vals))
for c in chnArgs(args):
cmap = mio.getPortChnFilter(p, c)
vals = map2str(cmap)
if vals:
print('\t%3d. %s' % (c, ','.join(vals)))
def printChnFilter(mio, n, chn):
flt = mio.getPortChnFilter(n, chn)
chf = chnFilter(flt[1])
print('%2d: %s' % (n, mio.getPortName(n)))
print('\t channel %2d: %s' % (n, chf.name))
def printRemaps(mio, args):
ports = portArgs(mio, args)
for p in ports:
print('%s:' % ports[p])
for c in chnArgs(args):
r = mio.getPortRemap(p, c)
for d in r.keys():
print('\t%2d. %s:\t%2d' % (c, d, r[d]))
def doPort(mio, args):
if args.set:
ports = portArgs(mio, args, False)
suffix = len(ports) > 1
n = 1
s = ''
for p in ports:
if suffix:
s = ' %d' % n
n += 1
name = '%s%s' % (args.set[0], s)
mio.setPortName(p, name)
elif args.list:
printPorts(mio, args)
def doInfo(mio, args):
print('Model : %s' % mio.getDeviceName())
print('Serial : %s' % mio.getInfo(infoId.SERIAL, 's'))
print('Firmware version : %s' % mio.getFwVer())
def prange(s):
ss = set([int(i) for i in s.split(',')])
return ss
def routeOp(func):
def inner(mio, dst):
sr, dr = dst.split(':')
ss = prange(sr)
ds = prange(dr)
for src in ss:
routes = mio.getPortRoute(src)
for dst in ds:
func(routes, int(dst))
mio.setPortRoute(src, routes)
return inner
@routeOp
def addRoute(routes, dst):
routes.add(dst)
@routeOp
def delRoute(routes, dst):
routes.remove(dst)
def doRoute(mio, args):
if args.add:
addRoute(mio, args.add)
elif args.delete:
delRoute(mio, args.delete)
elif args.list:
printRoutes(mio, args)
def doFilter(mio, args):
ports = portArgs(mio, args)
if args.channel:
emap = chnFltMap
else:
emap = sysFltMap
if args.set:
fmap = str2map(emap, args.set)
val = map2ba(fmap)
for p in ports:
if args.channel:
for c in chnArgs(args):
mio.setPortChnInfo(p, c, 0xe, val)
else:
mio.setPortInfo(p, 0xc, val)
elif args.list:
printFilters(mio, args)
def doPreset(mio, args):
if args.list:
printPresets(mio, args)
else:
n = mio.getCurPreset()
print('%2d: %s' % (n, mio.getPresetName(n)))
def doSave(mio, args):
if hasattr(args, 'global'):
mio.saveGlobal()
mio.savePreset(args.preset, args.name)
def doLoad(mio, args):
mio.loadPreset(args.preset)
def doNet(mio, args):
if args.dynamic:
mio.setNetwork()
elif args.static:
mio.setNetwork(tuple(args.static))
elif args.list:
print(mio.getNetwork())
net = mio.getIP()
for k in net:
print('\t%s' % k, '%d.%d.%d.%d' % net[k])
def doRemap(mio, args):
def newmap(c, remap = {}):
nmap = {}
for i in remapId:
nmap[i] = remap.get(i, c)
return nmap
if args.set:
ports = portArgs(mio, args)
chn = chnArgs(args)
remap = {}
w, c = args.set.split(':')
for k in w.split(','):
e = remapId[k]
remap[e] = int(c)
for p in ports:
for c in chn:
nmap = newmap(c, remap)
mio.setPortRemap(p, c, nmap)
elif args.list:
printRemaps(mio, args)
def doUsbhost(mio, args):
nport = mio.getNUsbHost()
for p in range(1, nport+1):
print('%2d. %s' % (p, mio.getUsbInfo(p, 0x2b, fmt='s')))
def doRtp(mio, args):
pass
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Mio X-series control')
parser.add_argument('--mioport', default='mioX')
parser.add_argument('--debug', action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.WARNING)
subparsers = parser.add_subparsers(title='subcommands')
parser_info = subparsers.add_parser('info')
parser_info.set_defaults(func=doInfo)
parser_port = subparsers.add_parser('port')
parser_port.add_argument('--list', action='store_true', default=True)
parser_port.add_argument('--set', nargs=1)
parser_port.add_argument('ports', nargs='?')
parser_port.set_defaults(func=doPort)
parser_route = subparsers.add_parser('route')
parser_route.add_argument('--list', action='store_true', default=True)
parser_route.add_argument('--add')
parser_route.add_argument('--delete')
parser_route.add_argument('ports', nargs='?')
parser_route.set_defaults(func=doRoute)
parser_filter = subparsers.add_parser('filter')
parser_filter.add_argument('--list', action='store_true', default=True)
parser_filter.add_argument('--set', nargs='?')
parser_filter.add_argument('--channel')
parser_filter.add_argument('ports', nargs='?')
parser_filter.set_defaults(func=doFilter)
parser_remap = subparsers.add_parser('remap')
parser_remap.add_argument('--list', action='store_true', default=True)
parser_remap.add_argument('--channel')
parser_remap.add_argument('--set', nargs='?')
parser_remap.add_argument('ports', nargs='?')
parser_remap.set_defaults(func=doRemap)
parser_preset = subparsers.add_parser('preset')
parser_preset.add_argument('--list', action='store_true')
parser_preset.add_argument('--show', action='store_true', default=True)
parser_preset.set_defaults(func=doPreset)
parser_save = subparsers.add_parser('save')
parser_save.add_argument('preset', type=int, nargs='?')
parser_save.add_argument('--name')
parser_save.add_argument('--global', action='store_true')
parser_save.set_defaults(func=doSave)
parser_load = subparsers.add_parser('load')
parser_load.add_argument('preset', type=int, nargs='?')
parser_load.set_defaults(func=doLoad)
parser_usbhost = subparsers.add_parser('usbhost')
parser_usbhost.add_argument('--list', action='store_true', default=True)
parser_usbhost.set_defaults(func=doUsbhost)
parser_net = subparsers.add_parser('net')
parser_net.add_argument('--list', action='store_true', default=True)
parser_net.add_argument('--dynamic', action='store_true')
parser_net.add_argument('--static', nargs=3)
parser_net.set_defaults(func=doNet)
#parser_rtp = subparsers.add_parser('rtp')
#parser_rtp.add_argument('--list', action='store_true', default=True)
#parser_rtp.set_defaults(func=doRtp)
args = parser.parse_args()
logging.basicConfig(level=args.loglevel, format='%(message)s')
debug(args)
if not hasattr(args, "func"):
parser.print_help()
exit(1)
mio = mioX()
mio.connect(args.mioport)
args.func(mio, args)