Index: /powerbar/npm2000.py
===================================================================
--- /powerbar/npm2000.py	(revision 359)
+++ /powerbar/npm2000.py	(revision 359)
@@ -0,0 +1,300 @@
+#!/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 <info@rickvanderzwet.nl>
+
+import socket, time
+import getopt, sys
+
+MAX_BUFFER = 100
+# NPM4000
+#MAX_PORTS = 24
+# NPM2000
+MAX_PORTS = 8
+
+# 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):
+    if MAX_PORTS == 8:
+       name = "%i" % number
+    else:
+      if number <= 8:
+          name = "%Ai" % 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 <port>|--toggle=<port>]   Toggle port(s)
+  [-o <port>|--on=<port>]       Turn on port(s)
+  [-f <port>|--off=<port>]      Turn off port(s)
+
+Note: <port> 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()
Index: /powerbar/npm4000.py
===================================================================
--- /powerbar/npm4000.py	(revision 358)
+++ /powerbar/npm4000.py	(revision 359)
@@ -19,5 +19,8 @@
 
 MAX_BUFFER = 100
+# NPM4000
 MAX_PORTS = 24
+# NPM2000
+# MAX_PORTS = 8
 
 # XOR all the 8bit values to get a checksum
@@ -74,13 +77,15 @@
 
 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
+    if MAX_PORTS == 8:
+       name = "%i" % number
+    else:
+      if number <= 8:
+          name = "%Ai" % 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)
 
