source: powerbar/npm4000.py

Last change on this file was 359, checked in by rick, 2 years ago

Add support for npm2000 power bars

  • Property svn:executable set to *
File size: 8.3 KB
Line 
1#!/usr/bin/env python
2# NPM 4000 powerbar managment script, to be used instead of the windows
3# application.
4#
5# XXX: Not all features are ported yet (like amp monitoring)
6# XXX: Some dirty poking around with hex representations and numeric representations of ports
7# XXX: Make proper classes for use
8# XXX: Documentation
9#
10# TODO: Make it work for the NPM2000 series, which are using a different codes to address individual ports.
11#
12#
13# Licence: BSD
14# Version: $Id: npm4000.py 750 2009-09-27 14:34:55Z rick $
15# Rick van der Zwet <info@rickvanderzwet.nl>
16
17import socket, time
18import getopt, sys
19
20MAX_BUFFER = 100
21# NPM4000
22MAX_PORTS = 24
23# NPM2000
24# MAX_PORTS = 8
25
26# XOR all the 8bit values to get a checksum
27def checksum(s):
28    crc = 0
29    for p in range(0, len(s),2):
30        crc ^= int(s[p:p+2], 16)
31    return "%02X" % crc
32
33inet_addr = '192.168.0.178'
34inet_port = 4001
35
36passwd = 0x12345678
37addr_code = 0xFFFF 
38
39verbose = False
40
41cmd2code = { 'login'       : (0x5507, 1),
42         'portOn'      : (0xB204, 2),
43         'portOff'     : (0xC204, 2),
44         'allPortsOff' : (0xC103, 6),
45         'allPortsOn'  : (0xB104, 15),
46         'status'      : (0xD103, 2),
47         'knownError'  : (0xFFFF, 1),
48       }
49
50# Socket used troughout the code
51s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
52
53def getCode(type):
54    return(cmd2code[type][0])
55
56def getTimeout(type):
57    return(cmd2code[type][1])
58
59def dprint(msg):
60    if verbose:
61        print time.strftime('[%Y-%m-%d %H:%M:%S]'), msg
62
63def getPortNumber(name):
64    port_char = name[0]
65    port_number = int(name[1])
66    number = -1
67    if port_char == 'A':
68        number = port_number
69    elif port_char == 'B':
70        number = port_number + 8
71    elif port_char == 'C':
72        number = port_number + 16
73    else:
74        raise ValueError, "Port %s not defined" % name
75
76    return(number)
77
78def getPortName(number):
79    if MAX_PORTS == 8:
80       name = "%i" % number
81    else:
82      if number <= 8:
83          name = "%Ai" % number
84      elif number <= 16:
85          name = "B%i" % (number - 8)
86      elif number <= 24:
87          name = "C%i" % (number - 16)
88      else:
89          raise ValueError, "Port %i not defined" % number
90    return(name)
91
92def getPortHex(number):
93    return(int(getPortName(number),16))
94
95def generateCommand(type,arg=None):
96    if arg == None:
97        command = "%04X%04X" % (getCode(type), addr_code)
98    else:
99        command = "%04X%04X%02X" % (getCode(type), addr_code, arg)
100    command += checksum(command)
101    return (command)
102
103
104def sendCommand(type, arg=None):
105    cmd = generateCommand(type,arg)
106    timeout = getTimeout(type)
107    dprint('Send: ' + cmd + ' (%s)' % (type))
108    s.send(cmd)
109    s.settimeout(timeout)
110    try:
111        retval = s.recv(MAX_BUFFER)
112        dprint('Recv: ' + retval)
113    except socket.timeout:
114        retval = None
115        print 'Recv: ERR timeout (wrong command, bad connection)'
116        #XXX: XOR Checking for data correctness
117    return(retval)
118
119def doCommand(type, arg=None):
120    sendCommand('login', passwd)
121    retval = sendCommand(type, arg)
122    #XXX: Check if command returned succesfull D128FF
123    return(retval)
124
125
126def getPortState(port, raw_status):
127    """Port configuration is put into 24 bits, every port has one bit
128    representing the statewith a awkward order:
129    A8,A7,A6,A5,A4,A3,A2,A1,B8,..,B1,C8,..,C1"""
130    # Portion of retval we are interested in
131    ports_state = int(raw_status[8:14],16)
132    port_bit_location = -1
133    if port <= 8:
134        port_bit_location =  (1 << 15 << port)
135    elif port <= 16:
136        port_bit_location = (1 << 7 << (port - 8))
137    elif port <= 24:
138        port_bit_location = (1 << (port - 16 - 1))
139
140    if port_bit_location & ports_state:
141        return True
142    else:
143        return False
144
145def getPortStatus(i):
146    raw_status = doCommand('status')
147    print "Port %02i [%s]:" % (i, getPortName(i)),
148    if getPortState(i,raw_status):
149        print "1"
150    else:
151        print "0"
152    return(retval)
153
154def getStatusAll():
155    raw_status= doCommand('status')
156    for i in range(1,MAX_PORTS+1):
157        print "Port %02i [%s]:" % (i, getPortName(i)),
158        if getPortState(i,raw_status):
159            print "1"
160        else:
161            print "0"
162
163def togglePort(port):
164    raw_status= doCommand('status')
165    if getPortState(port,raw_status):
166        doCommand('portOff',port)
167    else:
168        doCommand('portOn', port)
169
170
171def runTest():
172    print "TEST: Start will all port Offline"
173    doCommand('allPortsOff')
174
175    print "TEST: All On and Off again, in mass execution"
176    doCommand('allPortsOn',0x01)
177    doCommand('allPortsOff')
178
179    print "TEST: Enable and disable ports one by one"
180    for i in range(1,MAX_PORTS+1):
181        print getPortName(i)
182        doCommand('portOn', getPortHex(i))
183        doCommand('portOff', getPortHex(i))
184
185    print "TEST: Send known error"
186    doCommand('knownError')
187
188
189def usage():
190    print """
191Usage %s arguments
192Version: $Id: npm4000.py 750 2009-09-27 14:34:55Z rick $
193
194Arguments:
195  [-h|--help]                   Reading right know
196  [-v|--verbose]                Print extra communication output
197  --host=                       IP adress of FQDN hostname [%s]
198  --port=                       Port to connect to [%s]
199  --password=                   Password to use in hex notation [0x1234568]
200  --addresscode=                Internal device number in hex notation [0xFFFF]
201  [-s|--status]                 Current port configuration
202  [-t <port>|--toggle=<port>]   Toggle port(s)
203  [-o <port>|--on=<port>]       Turn on port(s)
204  [-f <port>|--off=<port>]      Turn off port(s)
205
206Note: <port> has different notations:
207  Numeric value of port         1,2,3,4,5,..
208  Actual value of port          A1,..,A8,B1,..,B8,C1,..,C8
209  All ports                     all
210    """ % (sys.argv[0], inet_addr, inet_port)
211
212def main():
213    global verbose, addr_code, inet_addr, inet_port, passwd, s
214    try:
215        opts, args = getopt.getopt(sys.argv[1:], 
216            "hf:s:t:o:v", ["help","verbose","host=", "port=", "password=", "addresscode=","toggle=","off=", "on=", "status="])
217    except getopt.GetoptError, err:
218        # print help information and exit:
219        print str(err) # will print something like "option -a not recognized"
220        usage()
221        sys.exit(2)
222
223    opt_port = None
224    opt_action = None
225    for o, a in opts:
226        if o in ("-v", "--verbose"):
227            verbose = True
228        elif o in ("-h", "--help"):
229            usage()
230            sys.exit()
231        elif o in ("--addresscode"):
232            addr_code = int(a,16)
233        elif o in ("--host"):
234            inet_addr = a
235        elif o in ("--password"):
236            passwd = int(a,16)
237        elif o in ("--port"):
238            inet_port = a
239        elif o in ("-s", "--status"):
240            opt_action = "status"
241            opt_port = a
242        elif o in ("-t","--toggle"):
243            opt_action = "toggle"
244            opt_port = a
245        elif o in ("-f","--off"):
246            opt_action = "off"
247            opt_port = a
248        elif o in ("-o","--on"):
249            opt_action = "on"
250            opt_port = a
251        else:
252            assert False, "unhandled option"
253
254    if (opt_port == None or opt_action == None):
255        usage()
256        sys.exit(2)
257
258    dprint ('action: ' + opt_action + ' port: ' + opt_port) 
259
260    s.connect((inet_addr, inet_port))
261
262    # Status needs real integers, hack
263    if opt_action == "status":
264        if opt_port == "all":
265            getStatusAll()
266        else:
267            print "XXX: Implement"
268        sys.exit(0)
269
270    # Resolve port to proper number
271    if opt_port == "all":
272        None # Blank resolution, as it is done elsewhere
273    elif opt_port[0] in ("A","B","C"):
274        opt_port = int(opt_port,16)
275        dprint('Hexcode of port: %i' % opt_port)
276    else:
277        # Dirty hack to have conversion and checking at the same time
278        opt_port = getPortHex(int(opt_port))
279        dprint('Hexcode of port: %i' % opt_port)
280
281    if opt_action == "toggle":
282        if opt_port == "all":
283            for i in range(1,MAX_PORTS+1):
284                togglePort(getPortHex(i))
285        else:
286                togglePort(opt_port)
287    elif opt_action == "on":
288        if opt_port == "all":
289            doCommand("allPortsOn",0x01)
290        else:
291            doCommand("portOn", opt_port)
292    elif opt_action == "off":
293        if opt_port == "all":
294            doCommand("allPortsOff");
295        else:
296            doCommand("portOff", opt_port)
297
298
299if __name__ == "__main__":
300    main()
Note: See TracBrowser for help on using the repository browser.