source: powerbar/npm4000.py@ 381

Last change on this file since 381 was 359, checked in by Rick van der Zwet, 13 years ago

Add support for npm2000 power bars

  • Property svn:executable set to *
File size: 8.3 KB
RevLine 
[358]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
[359]21# NPM4000
[358]22MAX_PORTS = 24
[359]23# NPM2000
24# MAX_PORTS = 8
[358]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):
[359]79 if MAX_PORTS == 8:
80 name = "%i" % number
[358]81 else:
[359]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
[358]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.