#!/usr/bin/python
#
# hpi commandline Swiss army knife
#


usage = """hpicontrol.py [options] command <cmd arguments>

Control and query AudioScience audio adapters (local and networked)

COMMANDS:

  amode:  view or set adapter mode
  aprop:  view or set adapter properties
  controls:  list all controls with their values
  eeprom:  read or write the user eeprom
  gpio:  read or write gpios - subcommands:  get, set, status
  info:  print adapter info, including mode and properties
  list:  list available adapters
  persist:  control mixer control store - subcommands:  enable, disable, delete, save

In the following commands {ctrl_location} is specified as SOURCE index DEST index,
The source and dest names correspond to
hpi.SOURCENODE_<src> and hpi.DESTNODE_<dest>
use 0 for no source or dest.
{ctrl_type} name corresponds to hpi.CONTROL_<type>

  cget:  get single control value
    cset {ctrl_location} {ctrl_type} {ctrl_attribute}
    cget OSTREAM 0 0 0 VOLUME
  cset:  set single control value
    cset {ctrl_location} {ctrl_type} ATTRIBUTE -- VALUE [second value]
    cset OSTREAM 0 0 0 VOLUME GAIN -- -10000 -10000
    cset OSTREAM 0 0 0 VOLUME  will list known attributes
"""

from sys import exit, platform
from time import sleep
from struct import unpack


def AesRxDetails(h):
    e,status = hpi.AESEBU_Receiver_GetErrorStatus(h)
    e,rate = hpi.AESEBU_Receiver_GetSampleRate(h)
    return "errors %02x, rate %d" % (status,rate)


def ChannelModeDetails(h):
    e,cmode=hpi.ChannelMode_Get(h)
    print()
    found = False
    for i in range(0,64):
        e,mode = hpi.ChannelMode_QueryMode(h, i)
        if (e != 0):
            break
        if (cmode==mode):
            print('->', end=' ')
            found = True
        print(mode, hpi.ChannelModeDict[mode])
    if not found:
        print('Current mode %d not valid?' % cmode)
    return ''


hmi_addr_rx_channel0 = 0x40100
hmi_addr_tx_channel0 = 0x50100
hmi_addr_std_user_string = 0x7e100

def hmi_addr_rx_channel(n, i):
    return hmi_addr_rx_channel0 + n * 0x1000 + i

def hmi_addr_tx_channel(n, i):
    return hmi_addr_tx_channel0 + n * 0x1000 + i

def CobranetDetails(h):
    e,s = hpi.Cobranet_HmiRead(h, 0x100000) # sysName
    if not e:
        print('\n\tsysName : %s' % hpi.cobranet_hmi_to_string(s), end=' ')
    e,  s = hpi.Cobranet_HmiRead(h, 0x11000D) # cobraIfPhyAddress
    if not e:
        mac = (ord(s[1]), ord(s[0]),ord(s[3]),ord(s[2]),ord(s[5]),ord(s[4]))
        print('\n\tcobraIfPhyAddress : %02X:%02X:%02X:%02X:%02X:%02X' % mac, end=' ')

    e,  s = hpi.Cobranet_HmiRead(h, 0x72000, 4) # cobraIpMonCurrentIP -0x72000
    if not e:
        print('\n\tcobraIpMonCurrentIP : %d.%d.%d.%d' % (ord(s[1]), ord(s[0]), ord(s[3]), ord(s[2])), end=' ')

    e,  s = hpi.Cobranet_HmiRead(h, 0x72002, 4) # cobraIpMonPersistIP -0x72000
    if not e:
        print('\n\tcobraIpMonPersistIP : %d.%d.%d.%d' % (ord(s[1]), ord(s[0]), ord(s[3]), ord(s[2])), end=' ')

    e,  s = hpi.Cobranet_HmiRead(h, 0x1100, 4) # cobraFlashPersistEnable -x1100
    if not e:
        p = unpack('>I', s[0:4])[0]
        print('\n\tcobraFlashPersistEnable : %d' % p, end=' ')

    e,s = hpi.Cobranet_HmiRead(h, hmi_addr_std_user_string)
    if not e:
        print('\n\tstdUserString[0] : %s' % hpi.cobranet_hmi_to_string(s), end=' ')

    print('\n\tRxChannels (map)')
    for i in range(8):
        e,s = hpi.Cobranet_HmiRead(h, hmi_addr_rx_channel(i, 0))
        if not e:
            b = unpack('>I', s[0:4])[0]
            print(('\t\t[%d]=%d ') % (i, b), end=' ')
        e,s = hpi.Cobranet_HmiRead(h, hmi_addr_rx_channel(i, 0x100))
        if not e:
            fmt = '>' + 'I' * (len(s) / 4)
            b = unpack(fmt, s)
            print(b)

    print('\tTxChannels (map)')
    for i in range(4):
        e,s = hpi.Cobranet_HmiRead(h, hmi_addr_tx_channel(i, 0))
        if not e:
            b = unpack('>I', s[0:4])[0]
            print(('\t\t[%d]=%d ') % (i, b), end=' ')
        e,s = hpi.Cobranet_HmiRead(h, hmi_addr_tx_channel(i, 0x100))
        if not e:
            fmt = '>' + 'I' * (len(s) / 4)
            b = unpack(fmt, s)
            print(b)



    if False: # test writing to HMI var
        s = hpi.cobranet_string_to_hmi('hpicontrol.py was here')
        e = hpi.Cobranet_HmiWrite(h, hmi_addr_std_user_string, s)
        e,s = hpi.Cobranet_HmiRead(h, hmi_addr_std_user_string)
        if not e:
            print('\n\tstdUserString[0] : %s' % hpi.cobranet_hmi_to_string(s), end=' ')

    return ''

def CompanderDetails(h):
    e,en = hpi.Compander_GetEnable(h)
    if en: print('on')
    else: print('off')
    e,g = hpi.Compander_GetMakeupGain(h)
    print('  Makeup gain = %3.0fdB' % (g/hpi.UNITS_PER_dB))
    for i in range(10):
        e, t = hpi.Compander_GetThreshold(h, i)
        e, r = hpi.Compander_GetRatio(h, i)
        e, a = hpi.Compander_GetAttackTimeConstant(h, i)
        e, d = hpi.Compander_GetDecayTimeConstant(h, i)
        if e:
            break
        print('  %d Threshold = %3.0fdB, Ratio = %d%%' % (i, t/hpi.UNITS_PER_dB, r))
        print('    Attack = %dms, Decay = %dms' % (a,d))
    return ''


def LevelDetails(h):
    e,gain=hpi.Level_GetGain(h);
    if e: return hpi.GetErrorText(e)
    e,min,max,step = hpi.Level_QueryRange(h)
    if not e:
        min= min / hpi.UNITS_PER_dB
        max=max / hpi.UNITS_PER_dB
        step=step / hpi.UNITS_PER_dB
        return '%s %d to %d step %d' % (gain,min, max, step)
    else:
        return gain


def MeterDetails(h):
    ep,peak = hpi.Meter_GetPeak(h)
    epb,pa,pd = hpi.Meter_GetPeakBallistics(h)
    eqc, chans = hpi.Meter_QueryChannels(h)

    er,rms = hpi.Meter_GetRms(h)
    erb,ra,rd = hpi.Meter_GetRmsBallistics(h)
    peak = [p / 100.0 for p in peak]
    rms = [p / 100.0 for p in rms]
    if chans == 1:
        peak = peak[0]
        rms = rms[0]

    s = ''
    if not ep:
        s = s + 'Peak %s dBFS. ' % (peak, )
    if not epb:
        s = s + '/%dms %dms\ ' % (pa, pd)
    if not er:
        s = s + 'RMS %s dBFS. ' % (rms, )
    if not erb:
        s = s + '/%dms %dms\ ' % (ra, rd)
    return s


def MicrophoneDetails(h):
    e, pp = hpi.Microphone_GetPhantomPower(h)
    if e:
        return 'no phantom power'
    elif pp:
        return 'phantom power ON'
    else:
        return 'phantom power OFF'

def MuxDetails(h):
    e,cnode,cindex=hpi.Multiplexer_GetSource(h)
    print(h,e,cnode,cindex)
    for i in range(0,32):
        e,node,index=hpi.Multiplexer_QuerySource(h,i)
        if (e != 0):
            return ''
        if ((cnode==node) & (cindex==index)):
            print('->', end=' ')
        print(node, hpi.SourceDict[node],index)
    return ''


def SampleClockDetails(h):
    print()
    e,csource = hpi.SampleClock_GetSource(h)
    e,a = hpi.SampleClock_GetAuto(h)
    if not e:
        print('Auto switching is ',['OFF','ON'][a])
    for i in range(0,64):
        e,source = hpi.SampleClock_QuerySource(h,i)
        if (e != 0):
            break
        if csource == source:
            print('->', end=' ')
        print('%d %s' % (source,hpi.SampleClockSourceDict[source]), end=' ')
        if source == hpi.SAMPLECLOCK_SOURCE_AESEBU_INPUT:
            e,si=hpi.SampleClock_QuerySourceIndex(h, 0, source)
            if not e:
                    e,ci=hpi.SampleClock_GetSourceIndex(h)
                    print('Indices', end=' ')
                    for j in range(0,64):
                            e,si=hpi.SampleClock_QuerySourceIndex(h, j, source)
                            if e: break
                            if ci ==si:
                                print('->', end=' ')
                            print(si, end=' ')
        print()


    e,crate=hpi.SampleClock_GetSampleRate(h)
    print('Current rate', e, crate)
    e, lrate=hpi.SampleClock_GetLocalRate(h)
    if not e:
        print('Local rate', lrate)
    print('Rates:', end=' ')
    for i in range(0,64):
        e,rate=hpi.SampleClock_QueryLocalRate(h,i)
        if (e != 0):
            break
        if crate==rate:
            print('->', end=' ')
        print(rate, end=' ')
        if lrate==rate:
            print('<~', end=' ')
    return ''


def SilenceDetectorDetails(h):
    e, b = hpi.SilenceDetector_GetEnable(h)
    e, d = hpi.SilenceDetector_GetDelay(h)
    e, t = hpi.SilenceDetector_GetThreshold(h)
    e,s = hpi.SilenceDetector_GetState(h)
    return '%s, delay=%d, threshold=%d, state=%d' % (['Disabled', 'Enabled'][b], d, t, s)


def TunerDetails(h):
    e,f = hpi.Tuner_GetFrequency(h)
    if not e:
        print('Frequency', f)

    e,l = hpi.Tuner_GetRFLevel(h)
    if e:
        print('Tuner_GetRFLevel error: ',hpi.GetErrorText(e))
    else:
        print('RFLevel %2.0f dBuV.' % (l / hpi.UNITS_PER_dB), end=' ')

    e,l = hpi.Tuner_GetRawRFLevel(h)
    if e:
        #print 'Tuner_GetRawRFLevel error: ',hpi.GetErrorText(e)
        pass
    else:
        print('RAW RFLevel %2.0f dBuV.' % (l / hpi.UNITS_PER_dB), end=' ')

    e,mask,status = hpi.Tuner_GetStatus(h)
    if e:
        print('Tuner_GetStatus error: ',hpi.GetErrorText(e))
    else:
        print('Status 0x%04x. (mask 0x%04x)' % (status, mask))
    e,cband = hpi.Tuner_GetBand(h);
    for i in range(64):
        e,band = hpi.Tuner_QueryBand(h,i)
        if (e != 0):
            return ''
        if cband == band:
            print('->', end=' ')
        print(band,hpi.TunerBandDict[band], end=' ')
        e, min = hpi.Tuner_QueryFrequency(h, 0, band)
        e, max = hpi.Tuner_QueryFrequency(h, 1, band)
        e, step = hpi.Tuner_QueryFrequency(h, 2, band)
        print(min,'to',max,'step',step, end=' ')

        e, de = hpi.Tuner_QueryDeemphasis(h, 0, band)
        if e:
            print('No deemphasis', end=' ')
        else:
            print('Deemphasis', end=' ')
            for j in range(8):
                e, de = hpi.Tuner_QueryDeemphasis(h, j, band)
                if e:
                    break
                print(de, end=' ')
        print()

    return ''


def VolumeDetails(h):
    e,gain=hpi.Volume_GetGain(h);
    e,min,max,step = hpi.Volume_QueryRange(h)

    ap = []
    try:
        for i in range(10):
            e,p = hpi.Volume_QueryAutoFadeProfile(h, i)
            if not e:
                ap.append(p)
    except AttributeError:
        pass

    if len(ap):
        af = 'Autofade profiles:%s' % ap
    else:
        af = ''

    try:
        e,mute = hpi.Volume_GetMute(h)
        if e:
            ms = '[mute error %s] ' % hpi.GetErrorText(e)
        elif mute:
            ms = '[muted] '
        else:
            ms = '[unmuted] '
    except AttributeError:
        ms = ''
    min =  min / hpi.UNITS_PER_dB
    max = max / hpi.UNITS_PER_dB
    step = step / hpi.UNITS_PER_dB
    return '%s dB %s\nRange %d to %d step %d %s' % (gain, ms, min, max, step, af)


def UniversalDetails(h):
    print('Universal_Info', end=' ')
    e, ent = hpi.Universal_Info(h)
    if not e:
        print()
        hpi.print_entity(ent)
    else:
        print('Error', hpi.GetErrorText(e))
    hpi.Entity_Free(ent)
    print('Universal_Get', end=' ')
    e, ent = hpi.Universal_Get(h)
    if not e:
        print()
        hpi.print_entity(ent)
    else:
        print('Error', hpi.GetErrorText(e))
    hpi.Entity_Free(ent)
    return ''


def PrintControl(hx,ci, handle=0):
    if handle != 0:
        ci = handle & 0xFFF
    e,srctype,srcindex,desttype,destindex,ctrltype,ch = hpi.Mixer_GetControlByIndex(hx,ci)
    if (e):
        # print "control with index %d doesn't exist" % ci
        return e

    CtrlGet = { # hpi is not defined at module load time. only after main has run
        hpi.CONTROL_AESEBU_RECEIVER:AesRxDetails,
        hpi.CONTROL_CHANNEL_MODE:ChannelModeDetails,
        hpi.CONTROL_COBRANET:CobranetDetails,
        hpi.CONTROL_COMPANDER:CompanderDetails,
        hpi.CONTROL_LEVEL:LevelDetails,
        hpi.CONTROL_METER:MeterDetails,
        hpi.CONTROL_MICROPHONE:MicrophoneDetails,
        hpi.CONTROL_MULTIPLEXER:MuxDetails,
        hpi.CONTROL_SAMPLECLOCK:SampleClockDetails,
        hpi.CONTROL_SILENCEDETECTOR:SilenceDetectorDetails,
        hpi.CONTROL_TUNER:TunerDetails,
        hpi.CONTROL_UNIVERSAL:UniversalDetails,
        hpi.CONTROL_VOLUME:VolumeDetails,
    }

    try:
        print("Control" , ci, hpi.ControlDict[ctrltype],"on", end=' ')
        if (srctype != 100):
            print(hpi.SourceDict[srctype],srcindex, end=' ')
            if (desttype != 200):
                print("to", end=' ')
        if (desttype != 200):
            print(hpi.DestDict[desttype],destindex, end=' ')
        if (ctrltype in CtrlGet):
            print(':',CtrlGet[ctrltype](ch))
        else:
            print()
    except KeyError:
        print("Control" , ci,  "type" , ctrltype, end=' ')
        print("src",srctype,srcindex, end=' ')
        print("dst",desttype,destindex)
    return 0


def EnumerateControls(adapter, args):
    hx = hpi.raise_error(hpi.Mixer_Open(adapter))

    print("Enumerate controls")
    print("Control number handle type location setting")
    e=0
    ci=0
    while (e == 0):
        e = PrintControl(hx,ci)
        if (e): break
        ci += 1


def ModuleInfo(adapter):
    r=hpi.Adapter_GetModuleByIndex(adapter,0)
    if r[0]:
        print("Adapter has no modules")
        return

    for m in range(32):
        r = hpi.Adapter_GetModuleByIndex(adapter,m)
        if r[0]:
            break;
        if not r[5]:
            break;
        print(hpi.info_string('Module ',m,r))


def ModeInfo(adapter):
    '''Print all available adapter modes, and show current one.
    '''
    e,cm = hpi.Adapter_GetMode(adapter)
    if not e:
        for m,n in list(hpi.AdapterModeDict.items()):
            e = hpi.Adapter_SetModeEx(adapter,m, hpi.ADAPTER_MODE_QUERY)
            if not e:
                if m == cm:
                    print(m,n,'<- current mode')
                else:
                    print(m,n)
    else:
        print('Adapter has no modes')


def PropInfo(adapter):
    '''Print all the adapter properties and their values
    '''
    print('Properties')
    lp = []
    for k,n in list(hpi.AdapterPropertyDict.items()):
        e,p1,p2 = hpi.Adapter_GetProperty(adapter, k)
        if not e:
            lp.append('%3d %-40s %5d %5d' % (k,n,p1,p2))
        elif  e in (hpi.ERROR_INVALID_CONTROL_ATTRIBUTE, hpi.ERROR_INVALID_OPERATION) :
           lp.append('%3d %-40s     %s' %  (k,n,'absent'))
        else:
            print('Error %s getting attribute %d == %s' % (hpi.GetErrorText(e), k, n))
            break
    lp.sort()
    for t in lp:
        print(t)


def AdapterProp(adapter, args):
    '''If property index and 2 values are given, set the property to the values.
    Then display all properties.
    '''
    if len(args) >= 3:
        prop = int(args[0], 0)
        p1 = int(args[1], 0)
        p2 = int(args[2], 0)
        e = hpi.Adapter_SetProperty(adapter, prop, p1, p2)
        if e:
            print('Failed to set property %s to %d %d' % (hpi.AdapterPropertyDict.get(prop,'Unknown'), p1, p2))
            print(hpi.GetErrorText(e))
    PropInfo(adapter)


def AdapterInfoString(adapter):
    ai = hpi.Adapter_GetInfo(adapter)
    e,p1,p2 = hpi.Adapter_GetProperty(adapter, hpi.ADAPTER_PROPERTY_SOFTWARE_VERSION)
    if not e:
        ver = "%d.%02d.%02d" % ((p1 >> 8), p1 & 0xFF, p2 & 0xFF)
    else:
        ver = '%d.%d, ' % ( ((ai[3] >> 13)& 0x7), ((ai[3] >> 7) & 0x3F))
    return hpi.info_string('',adapter,ai, swver=ver)

def AdapterDspUtilization(adapter):
    e0,h0,m0 = hpi.Profile_OpenAll(adapter, 0)
    e1,h1,m1 = hpi.Profile_OpenAll(adapter, 1)
    u0 = ''
    u1 = ''
    if not e0:
        e, u = hpi.Profile_GetUtilization(h0)
        u0 = ' Util 0 %d ' % (u / 100)
    if not e1:
        e, u = hpi.Profile_GetUtilization(h1)
        u1 = ' Util 1 %d ' % (u / 100)
    print ('DSP utilization ' + u0 + u1)

def AdapterInfo(adapter, args=None):
    print('Adapter', AdapterInfoString(adapter))
    AdapterDspUtilization(adapter)
    ModeInfo(adapter)
    ModuleInfo(adapter)
    PropInfo(adapter)


def AdapterMode(adapter, args):
    if len(args):
        e,cm = hpi.Adapter_GetMode(adapter)
        mode = int(args[0], 0)
        e = hpi.Adapter_SetModeEx(adapter, mode, hpi.ADAPTER_MODE_SET)
        if not e and cm != mode:
            print('Mode changed, driver reload required to activate')
        if e:
            print('Failed to set mode to',hpi.AdapterModeDict.get(mode,'unknown'))
    ModeInfo(adapter)


def findControl(hx, args):
    '''Convert args into hpi control info and handle.
    If fewer than 5 args, use first as control index.
    otherwise, expect source, index, dest, index, type
    '''
    if len(args) < 5:
        ci = int(args[0], 0) # Args 0 is a control index
        e,srctype,srcindex,desttype,destindex,ct,hc =\
            hpi.Mixer_GetControlByIndex(hx,ci)
        if e:
            ci = -1
            hc = 0
            ct = 0
        return ci, hc, ct, args[1:]
    else:
        hpi.DestDict[0] = 'DESTNODE_0'
        hpi.SourceDict[0] = 'SOURCENODE_0'
        c = hpi.reverse_dict(hpi.ControlDict)
        s = hpi.reverse_dict(hpi.SourceDict)
        d = hpi.reverse_dict(hpi.DestDict)
       # expect e.g. LINE_IN
        st = s['SOURCENODE_' + args[0].upper()]
        si = int(args[1])
        dt = d['DESTNODE_' + args[2].upper()]
        di = int(args[3])
        ct = c['CONTROL_' + args[4].upper()]

        e,hc = hpi.Mixer_GetControl(hx, st, si , dt, di, ct)
        ci = hc & 0xFFF
        if e: ci = -1
        return ci, hc, ct, args[5:]


def ControlInfo(adapter, args):
    hx = hpi.raise_error(hpi.Mixer_Open(adapter))

    ci, hc, ct, a = findControl(hx, args)
    if (ci >= 0):
        PrintControl(hx,ci)
    else:
        print("Could not find control",args)


def UniversalSet(hc, sa):
    '''Convert strings into numeric representations of various types
    '''
    e, current= hpi.Universal_Get(hc)
    e,t,i,r,v = hpi.Entity_Unpack(current)

    if t == hpi.entity_type_int:
        sa = [int(s) for s in sa]
    elif t == hpi.entity_type_float:
        sa = [float(s) for s in sa]
    elif t == hpi.entity_type_cstring:
        sa = sa[0] # only one string
    elif t == hpi.entity_type_ip4_address:
        sa = [tuple([int(d) for d in s.split('.')] ) for s in sa]
    elif t == hpi.entity_type_mac_address:
        sa = [tuple([int(d) for d in s.split(':')] ) for s in sa]
    # BOOLEAN 'T','F' work directly

    e, ent = hpi.Entity_AllocAndPack(t, r, sa)
    e = hpi.Universal_Set(hc, ent)
    return e

def ControlSet(adapter, args):
    hx = hpi.raise_error(hpi.Mixer_Open(adapter))

    ci, hc, ct, sargs = findControl(hx, args)
    if (ci < 0):
        print('Control not found')
        return

    err = 0
    if hpi.CONTROL_UNIVERSAL == ct:
        err = UniversalSet(hc, sargs)
    else:
        attrib = sargs[0].upper()
        if len(sargs) > 1:
            ia = int(sargs[1])
            sa = [ia, ia]

        if len(sargs) > 2:
            sa[1] = int(sargs[2])

    if hpi.CONTROL_LEVEL == ct:
        if 'GAIN' == attrib:
            err = hpi.Level_SetGain(hc, sa)
        else:
            print('Valid LEVEL attribute is GAIN')
    elif hpi.CONTROL_VOLUME == ct:
        if 'GAIN' == attrib:
            err = hpi.Volume_SetGain(hc, sa)
        elif 'FADE' == attrib:
            err = hpi.Volume_AutoFadeProfile(hc, sa, int(sargs[3]), hpi.VOLUME_AUTOFADE_LOG)
        elif 'MUTE' == attrib:
            if int(sargs[1]):
                mute = hpi.BITMASK_ALL_CHANNELS
            else:
                mute = 0
            try:
                err = hpi.Volume_SetMute(hc, mute)
            except AttributeError:
                print('Volume mute not present in this driver')
        else:
            print('Valid VOLUME attributes are GAIN, FADE')
    elif hpi.CONTROL_SAMPLECLOCK == ct:
        if 'SOURCE' == attrib:
            err = hpi.SampleClock_SetSource(hc, ia)
        elif 'INDEX' == attrib:
            err = hpi.SampleClock_SetSourceIndex(hc, ia)
        elif 'RATE' == attrib:
            err = hpi.SampleClock_SetLocalRate(hc, ia)
        else:
            print('Valid SAMPLECLOCK attributes are SOURCE, INDEX, RATE')
    elif hpi.CONTROL_CHANNEL_MODE == ct:
        if 'MODE' == attrib:
            err = hpi.ChannelMode_Set(hc, ia)
        else:
            print('Valid CHANNEL_MODE attribute is MODE')
    elif hpi.CONTROL_MICROPHONE == ct:
        if 'PHANTOM' == attrib:
            err = hpi.Microphone_SetPhantomPower(hc, int(sargs[1]))
        else:
            print('Valid MICROPHONE attribute is PHANTOM')
    elif hpi.CONTROL_MULTIPLEXER == ct:
        if 'SOURCE' == attrib:
            err = hpi.Multiplexer_SetSource(hc, int(sargs[1]), int(sargs[2]))
        else:
            print('Valid MULTIPLEXER attribute is SOURCE')
    elif hpi.CONTROL_METER == ct:
        if 'RMS_BALLISTICS' == attrib:
            err = hpi.Meter_SetRmsBallistics(hc, int(sargs[1]), int(sargs[2]))
        elif 'PEAK_BALLISTICS' == attrib:
            err = hpi.Meter_SetPeakBallistics(hc , int(sargs[1]), int(sargs[2]))
        else:
            print('Valid METER attributes are RMS_BALLISTICS (attack, decay) and PEAK_BALLISTICS')
    elif hpi.CONTROL_COMPANDER == ct:
        if 'ENABLE' == attrib:
            err = hpi.Compander_SetEnable(hc, int(sargs[1]))
        elif 'MAKEUP_GAIN' == attrib:
            err = hpi.Compander_SetMakeupGain(hc , int(sargs[1]))
        elif 'THRESHOLD' == attrib:
            err = hpi.Compander_SetThreshold(hc, int(sargs[1]), int(sargs[2]))
        elif 'RATIO' == attrib:
            err = hpi.Compander_SetRatio(hc, int(sargs[1]), int(sargs[2]))
        else:
            print('Valid COMPANDER attribtes are ENABLE(0/1), MAKEUP_GAIN (dB*100)')
            print('THRESHOLD (idx, dB*100), RATIO (idx, %)')
    elif hpi.CONTROL_SILENCEDETECTOR == ct:
        if 'ENABLE' == attrib:
            err = hpi.SilenceDetector_SetEnable(hc, int(sargs[1]))
        elif 'THRESHOLD' == attrib:
            err = hpi.SilenceDetector_SetThreshold(hc, int(sargs[1]))
        elif 'DELAY' == attrib:
            err = hpi.SilenceDetector_SetDelay(hc, int(sargs[1]))
        else:
            print('Valid SILENCEDETECTOR attribtes are ENABLE(0/1), THRESHOLD (dB*100)')
    elif hpi.CONTROL_TUNER == ct:
        if 'BAND' == attrib:
            err = hpi.Tuner_SetBand(hc, int(sargs[1]))
        elif 'FREQ' == attrib:
            err = hpi.Tuner_SetFrequency(hc , int(sargs[1]))
        else:
            print('Valid TUNER attribtes are BAND(AM=1,FM=2,FMSTEREO=4), FREQ')
    elif hpi.CONTROL_UNIVERSAL == ct:
        pass
    else:
        print('Control setting  not currently supported')

    if err:
        print('****', hpi.GetErrorText(err), '****')

    print()
    ControlInfo(adapter,args)


def MixerStore(adapter, args):
    hx = hpi.raise_error(hpi.Mixer_Open(adapter))
    cmd = args[0].upper()

    e = 0
    if 'SAVE' == cmd:
        e = hpi.Mixer_Store(hx, hpi.MIXER_STORE_SAVE, 0)
    elif 'DELETE' == cmd:
        e = hpi.Mixer_Store(hx, hpi.MIXER_STORE_DELETE, 0)
    elif 'ENABLE' == cmd:
        e = hpi.Mixer_Store(hx, hpi.MIXER_STORE_ENABLE, 0)
    elif 'DISABLE' == cmd:
        e = hpi.Mixer_Store(hx, hpi.MIXER_STORE_DISABLE, 0)
    else:
        print('Unknown mixer store command:',cmd)
        print('Valid commands are: save, delete, enable, disable.')

    if e:
        print('****', hpi.GetErrorText(e), '****')


def Gpio(adapter, args):
    e, hg, ni, no = hpi.Gpio_Open(adapter)
    if e:
        print('**** Error opening GPIO ****')
        return

    cmd = args[0].upper()
    if 'GET' == cmd:
        if len(args) > 1:
            gi = int(args[1], 0)
            e, b = hpi.Gpio_ReadBit(hg, gi)
            print(b)
        else:
            e, bl = hpi.Gpio_ReadAllBits(hg)
            print(hex(bl))

    elif 'SET' == cmd:
            gi = int(args[1], 0)
            b = int(args[2], 0)
            e = hpi.Gpio_WriteBit(hg, gi, b)
    elif 'STATUS' == cmd:
            e, bl = hpi.Gpio_WriteStatus(hg)
            print(hex(bl))
    else:
        print('Unknown gpio command:',cmd)
        print('Valid commands are: get, set, status')


def Eeprom(adapter, args):
    e, h, n = hpi.NvMemory_Open(adapter)
    if e:
        print('**** Error opening NV memory ****')
        return

    if len(args):
        cmd = args[0].upper()
    else:
        cmd = 'DUMP'

    if 'READ' == cmd:
        a = int(args[1], 0)
        e, b = hpi.NvMemory_ReadByte(h, a)
        print('nvmem[0x%x] = 0x%x' % (a,b))
    elif 'WRITE' == cmd:
        a = int(args[1], 0)
        b = int(args[2], 0)
        e = hpi.NvMemory_WriteByte(h, a, b)
    elif 'DUMP' == cmd:
        print('NV memory contents (hex)')
        for l in range(0,n,16):
            print('%02X :' % l, end=' ')
            for a in range(16):
                e, b = hpi.NvMemory_ReadByte(h, l + a)
                print('%02X' % b, end=' ')
            print()
    else:
        print('Valid commands are: DUMP, READ addr, WRITE addr data')


def print_adapter_list(title='Reachable adapters'):
    n = hpi.raise_error(hpi.SubSys_GetNumAdapters())
    print(title, end=' ')
    print(n)

    a = 0
    while True:
        e,i,t = hpi.SubSys_GetAdapter(a)
        if e == hpi.ERROR_INVALID_OBJ_INDEX:
            break

        if not e:
            print(AdapterInfoString(i))
        else:
            print("Unreachable adapter ASI%X (iter index %d) with HW index %d. Error %s" % (t, a, i, hpi.GetErrorText(e)))
        a += 1

commands = {
    'CONTROLS' :  EnumerateControls,
    'INFO' :  AdapterInfo,
    'CGET' :  ControlInfo,
    'CSET' :  ControlSet,
    'GPIO' :  Gpio,
    'AMODE' :  AdapterMode,
    'APROP' : AdapterProp,
    'PERSIST' : MixerStore,
    'EEPROM' : Eeprom,
    'LIST' : None
}


############################################################################
if __name__ == '__main__':
    from optparse import OptionParser

    parser = OptionParser(usage = usage, version='4.20')
    parser.add_option('-a','--adapter',type='int',dest='adapter',
                      help='Adapter index. Default=%default', default=0)
    parser.add_option('-I','--interface-ip-address',type='string',dest='ipaddr',
                      help='Interface IP address.', default=None)


    opts,args = parser.parse_args()

    adapter = opts.adapter

    if len(args) == 0:
        parser.print_usage()
        exit()
    else:
        #args = [s.upper() for s in args]
        command = args[0].upper()

    import audioscience.hpi as hpi

    hpi.setup(opts.adapter, opts.ipaddr)

    if 'LIST' == command:
        try:
            print_adapter_list('Local adapters:')
        except hpi.HpiError:
            print('No local adapters or driver?')
        if not platform.startswith('win'):
            hpi.setup(1000)
            print_adapter_list('Network Adapters on default interface:')
            if opts.ipaddr:
                hpi.setup(1000, opts.ipaddr)
                print_adapter_list('Network Adapters on %s:' % opts.ipaddr)

        exit()

    n = hpi.raise_error(hpi.SubSys_GetNumAdapters())

    #print 'Found',n,'adapters'
    #for a in range(n):
    #    print 'GetAdapter',a,hpi.SubSysGetAdapter(a)

    hpi.raise_error(hpi.Adapter_Open(adapter))

    # sleep(1) # time for ctrl cache to update?
    try:
        commands[command](adapter, args[1:])
    except KeyError:
        c = list(commands.keys())
        c.sort()
        print('Unknown command:',command)
        print('Valid commands are %s\n' % c)
        parser.print_usage()

# END_OF_CODE
