source: powerbar/npm4000.py@ 348

Last change on this file since 348 was 305, checked in by Rick van der Zwet, 14 years ago

Hacks and comments to make it work with other device.

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