#!/usr/bin/env python # NPM 4000 powerbar managment script, to be used instead of the windows # application. # # XXX: Not all features are ported yet (like amp monitoring) # XXX: Some dirty poking around with hex representations and numeric representations of ports # XXX: Make proper classes for use # XXX: Documentation # # TODO: Make it work for the NPM2000 series, which are using a different codes to address individual ports. # # # Licence: BSD # Version: $Id: npm4000.py 750 2009-09-27 14:34:55Z rick $ # Rick van der Zwet import socket, time import getopt, sys MAX_BUFFER = 100 MAX_PORTS = 24 # XOR all the 8bit values to get a checksum def checksum(s): crc = 0 for p in range(0, len(s),2): crc ^= int(s[p:p+2], 16) return "%02X" % crc inet_addr = '192.168.0.178' inet_port = 4001 passwd = 0x12345678 addr_code = 0xFFFF verbose = False cmd2code = { 'login' : (0x5507, 1), 'portOn' : (0xB204, 2), 'portOff' : (0xC204, 2), 'allPortsOff' : (0xC103, 6), 'allPortsOn' : (0xB104, 15), 'status' : (0xD103, 2), 'knownError' : (0xFFFF, 1), } # Socket used troughout the code s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) def getCode(type): return(cmd2code[type][0]) def getTimeout(type): return(cmd2code[type][1]) def dprint(msg): if verbose: print time.strftime('[%Y-%m-%d %H:%M:%S]'), msg def getPortNumber(name): port_char = name[0] port_number = int(name[1]) number = -1 if port_char == 'A': number = port_number elif port_char == 'B': number = port_number + 8 elif port_char == 'C': number = port_number + 16 else: raise ValueError, "Port %s not defined" % name return(number) def getPortName(number): name = "U3" if number <= 8: name = "A%i" % number elif number <= 16: name = "B%i" % (number - 8) elif number <= 24: name = "C%i" % (number - 16) else: raise ValueError, "Port %i not defined" % number return(name) def getPortHex(number): return(int(getPortName(number),16)) def generateCommand(type,arg=None): if arg == None: command = "%04X%04X" % (getCode(type), addr_code) else: command = "%04X%04X%02X" % (getCode(type), addr_code, arg) command += checksum(command) return (command) def sendCommand(type, arg=None): cmd = generateCommand(type,arg) timeout = getTimeout(type) dprint('Send: ' + cmd + ' (%s)' % (type)) s.send(cmd) s.settimeout(timeout) try: retval = s.recv(MAX_BUFFER) dprint('Recv: ' + retval) except socket.timeout: retval = None print 'Recv: ERR timeout (wrong command, bad connection)' #XXX: XOR Checking for data correctness return(retval) def doCommand(type, arg=None): sendCommand('login', passwd) retval = sendCommand(type, arg) #XXX: Check if command returned succesfull D128FF return(retval) def getPortState(port, raw_status): """Port configuration is put into 24 bits, every port has one bit representing the statewith a awkward order: A8,A7,A6,A5,A4,A3,A2,A1,B8,..,B1,C8,..,C1""" # Portion of retval we are interested in ports_state = int(raw_status[8:14],16) port_bit_location = -1 if port <= 8: port_bit_location = (1 << 15 << port) elif port <= 16: port_bit_location = (1 << 7 << (port - 8)) elif port <= 24: port_bit_location = (1 << (port - 16 - 1)) if port_bit_location & ports_state: return True else: return False def getPortStatus(i): raw_status = doCommand('status') print "Port %02i [%s]:" % (i, getPortName(i)), if getPortState(i,raw_status): print "1" else: print "0" return(retval) def getStatusAll(): raw_status= doCommand('status') for i in range(1,MAX_PORTS+1): print "Port %02i [%s]:" % (i, getPortName(i)), if getPortState(i,raw_status): print "1" else: print "0" def togglePort(port): raw_status= doCommand('status') if getPortState(port,raw_status): doCommand('portOff',port) else: doCommand('portOn', port) def runTest(): print "TEST: Start will all port Offline" doCommand('allPortsOff') print "TEST: All On and Off again, in mass execution" doCommand('allPortsOn',0x01) doCommand('allPortsOff') print "TEST: Enable and disable ports one by one" for i in range(1,MAX_PORTS+1): print getPortName(i) doCommand('portOn', getPortHex(i)) doCommand('portOff', getPortHex(i)) print "TEST: Send known error" doCommand('knownError') def usage(): print """ Usage %s arguments Version: $Id: npm4000.py 750 2009-09-27 14:34:55Z rick $ Arguments: [-h|--help] Reading right know [-v|--verbose] Print extra communication output --host= IP adress of FQDN hostname [%s] --port= Port to connect to [%s] --password= Password to use in hex notation [0x1234568] --addresscode= Internal device number in hex notation [0xFFFF] [-s|--status] Current port configuration [-t |--toggle=] Toggle port(s) [-o |--on=] Turn on port(s) [-f |--off=] Turn off port(s) Note: has different notations: Numeric value of port 1,2,3,4,5,.. Actual value of port A1,..,A8,B1,..,B8,C1,..,C8 All ports all """ % (sys.argv[0], inet_addr, inet_port) def main(): global verbose, addr_code, inet_addr, inet_port, passwd, s try: opts, args = getopt.getopt(sys.argv[1:], "hf:s:t:o:v", ["help","verbose","host=", "port=", "password=", "addresscode=","toggle=","off=", "on=", "status="]) except getopt.GetoptError, err: # print help information and exit: print str(err) # will print something like "option -a not recognized" usage() sys.exit(2) opt_port = None opt_action = None for o, a in opts: if o in ("-v", "--verbose"): verbose = True elif o in ("-h", "--help"): usage() sys.exit() elif o in ("--addresscode"): addr_code = int(a,16) elif o in ("--host"): inet_addr = a elif o in ("--password"): passwd = int(a,16) elif o in ("--port"): inet_port = a elif o in ("-s", "--status"): opt_action = "status" opt_port = a elif o in ("-t","--toggle"): opt_action = "toggle" opt_port = a elif o in ("-f","--off"): opt_action = "off" opt_port = a elif o in ("-o","--on"): opt_action = "on" opt_port = a else: assert False, "unhandled option" if (opt_port == None or opt_action == None): usage() sys.exit(2) dprint ('action: ' + opt_action + ' port: ' + opt_port) s.connect((inet_addr, inet_port)) # Status needs real integers, hack if opt_action == "status": if opt_port == "all": getStatusAll() else: print "XXX: Implement" sys.exit(0) # Resolve port to proper number if opt_port == "all": None # Blank resolution, as it is done elsewhere elif opt_port[0] in ("A","B","C"): opt_port = int(opt_port,16) dprint('Hexcode of port: %i' % opt_port) else: # Dirty hack to have conversion and checking at the same time opt_port = getPortHex(int(opt_port)) dprint('Hexcode of port: %i' % opt_port) if opt_action == "toggle": if opt_port == "all": for i in range(1,MAX_PORTS+1): togglePort(getPortHex(i)) else: togglePort(opt_port) elif opt_action == "on": if opt_port == "all": doCommand("allPortsOn",0x01) else: doCommand("portOn", opt_port) elif opt_action == "off": if opt_port == "all": doCommand("allPortsOff"); else: doCommand("portOff", opt_port) if __name__ == "__main__": main()