Mercurial > code > home > repos > homeauto
annotate service/usbReset/usbReset.py @ 1153:e4f49cd9dda3
add :pointsAtLeastEvery control
Ignore-this: 9d0236b56b2a7592211ca68b87b4a5d1
darcs-hash:76e4d358cb6b039351c9b6f8e3bb825aaaefcc57
author | drewp <drewp@bigasterisk.com> |
---|---|
date | Sun, 15 Apr 2018 04:41:00 -0700 |
parents | 22af7ea8c401 |
children |
rev | line source |
---|---|
846 | 1 #!bin/python |
2 """ | |
3 resets usb ports and restarts other services. Must run as root for the usb part. | |
4 | |
5 Other systems that might be able to do conditional tests and commands like this: | |
6 https://github.com/azkaban/azkaban | |
7 nagios | |
8 | |
9 | |
10 | |
11 run this as root to reset a whole usb device. Ported from | |
12 http://marc.info/?l=linux-usb&m=121459435621262&w=2 | |
13 | |
14 how to learn what device to reset? lsusb or lsusb -t | |
15 | |
16 how to learn the ioctl number? cpp on this file: | |
17 | |
18 #include "/usr/include/linux/usbdevice_fs.h" | |
19 #include "/usr/include/asm-generic/ioctl.h" | |
20 USBDEVFS_RESET | |
21 | |
22 last line comes out like this: | |
23 (((0U) << (((0 +8)+8)+14)) | ((('U')) << (0 +8)) | (((20)) << 0) | ((0) << ((0 +8)+8))) | |
24 | |
25 this is a py expression: | |
26 (((0) << (((0 +8)+8)+14)) | ((ord('U')) << (0 +8)) | (((20)) << 0) | ((0) << ((0 +8)+8))) | |
27 | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
28 ---------------- |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
29 |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
30 also this other usb reset: |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
31 |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
32 http://davidjb.com/blog/2012/06/restartreset-usb-in-ubuntu-12-04-without-rebooting |
846 | 33 |
34 """ | |
35 | |
36 from __future__ import division | |
37 | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
38 import cyclone.web, json, traceback, os, sys, time, logging, re |
846 | 39 import os, fcntl, commands, socket, logging, time, xmlrpclib, subprocess |
40 | |
41 from twisted.internet import reactor, task | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
42 from twisted.internet.defer import inlineCallbacks, returnValue, Deferred |
846 | 43 from twisted.web.client import getPage |
44 sys.path.append("/my/proj/house/frontdoor") | |
45 from loggingserial import LoggingSerial | |
46 sys.path.append("/my/proj/homeauto/lib") | |
47 from cycloneerr import PrettyErrorHandler | |
48 from logsetup import log | |
49 | |
50 USBDEVFS_RESET = 21780 | |
51 | |
52 class Id(object): | |
53 ftdi = "0403:6001" | |
54 frontDoorHub0 = "8087:0024" # bus2 dev 2 | |
55 frontDoorHub1 = "0451:2046" # bus2 dev 3 | |
56 frontDoorHub2 = "1a40:0101" # bus2 dev 7 | |
57 frontDoorHub3 = "0409:0058" # bus2 dev 62 | |
58 frontDoorCam = "0ac8:307b" | |
59 | |
60 bedroomHub0 = "8087:0020" | |
61 bedroomHub1 = "05e3:0608" | |
62 bedroomHub2 = "058f:6254" | |
63 bedroomHub3 = "0409:005a" | |
64 bedroomCam = "046d:08aa" | |
65 bedroomSba = "04d8:000a" | |
66 bedroomArduino = "0403:6001" | |
67 | |
68 garageHub0 = "1d6b:0002" # bus2 dev1 | |
69 garageHub1 = "05e3:0604" # bus2 dev4 | |
70 garageArduino = "2341:0001" | |
71 garagePowerSerial = "0557:2008" | |
72 | |
73 blueHub = "1a40:0101" | |
74 | |
75 hostname = socket.gethostname() | |
76 | |
77 @inlineCallbacks | |
78 def getOk(url, timeout=1): | |
79 """can we get a successful status from this url in a short time?""" | |
80 log.debug("testing %s" % url) | |
81 try: | |
82 resp = yield getPage(url, timeout=timeout) | |
83 except Exception, e: | |
84 log.warn("getPage %s", e) | |
85 returnValue(False) | |
86 | |
87 returnValue(True) | |
88 | |
89 def hubDevice(usbId="1a40:0101"): | |
90 """ | |
91 what's the /dev path to the device with this usb id | |
92 """ | |
93 for line in commands.getoutput("lsusb").splitlines(): | |
94 if 'ID '+usbId in line: | |
95 words = line.split() | |
96 return "/dev/bus/usb/%s/%s" % (words[1], words[3].rstrip(':')) | |
97 raise ValueError("no usb device found with id %r" % usbId) | |
98 | |
99 def haveDevice(usbId): | |
100 try: | |
101 log.debug("checking for %s", usbId) | |
847
a93be1639c98
catch another broken-usb state
drewp <drewp@bigasterisk.com>
parents:
846
diff
changeset
|
102 dev = hubDevice(usbId) |
a93be1639c98
catch another broken-usb state
drewp <drewp@bigasterisk.com>
parents:
846
diff
changeset
|
103 # sometimes the dev will exist but fail to open |
a93be1639c98
catch another broken-usb state
drewp <drewp@bigasterisk.com>
parents:
846
diff
changeset
|
104 open(dev, "r") |
846 | 105 return True |
847
a93be1639c98
catch another broken-usb state
drewp <drewp@bigasterisk.com>
parents:
846
diff
changeset
|
106 except (ValueError, IOError): |
846 | 107 return False |
108 | |
109 def resetDevice(dev): | |
110 """ | |
111 send USBDEVFS_RESET to the given /dev address | |
112 """ | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
113 d = Deferred() |
846 | 114 log.debug("resetting %s" % dev) |
115 f=os.open(dev, os.O_WRONLY) | |
116 ret = fcntl.ioctl(f, USBDEVFS_RESET, 0) | |
117 if ret != 0: | |
118 raise ValueError("ioctl failed with %s" % ret) | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
119 reactor.callLater(3, d.callback, None) |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
120 return d |
846 | 121 |
122 def supervisorRestart(cmds, supervisor="http://localhost:9001"): | |
123 serv = xmlrpclib.ServerProxy(supervisor) | |
124 for c in cmds: | |
125 log.info("restarting %s", c) | |
126 try: | |
127 serv.supervisor.stopProcessGroup(c) | |
128 except xmlrpclib.ResponseError, e: | |
129 log.warn("supervisor.stopProcessGroup error %r, ignoring", e) | |
130 serv.supervisor.startProcess(c) | |
131 | |
132 def resetModules(modules): | |
133 log.info("reloading modules: %s", modules) | |
134 for m in modules: | |
135 subprocess.call(['modprobe', '-r', m]) | |
136 for m in modules: | |
137 subprocess.check_call(['modprobe', m]) | |
138 | |
139 | |
140 class Background(object): | |
141 def __init__(self, config, period): | |
142 self.config = config | |
143 self.period = period | |
144 self.lastPollTime = 0 | |
145 | |
146 def assertIsCurrent(self): | |
147 """raise an error if the poll data is not fresh""" | |
148 dt = time.time() - self.lastPollTime | |
149 if dt > self.period * 2: | |
150 raise ValueError("last poll time was too old: %.1f sec ago" % dt) | |
151 | |
152 @inlineCallbacks | |
153 def step(self): | |
154 now = time.time() | |
155 try: | |
156 if hostname == 'bang': | |
157 if (not haveDevice(Id.bedroomCam) or | |
158 not haveDevice(Id.bedroomArduino)): | |
159 if haveDevice(Id.bedroomHub3): | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
160 yield resetDevice(hubDevice(Id.bedroomHub3)) |
846 | 161 else: |
162 if haveDevice(Id.bedroomHub2): | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
163 yield resetDevice(hubDevice(Id.bedroomHub2)) |
846 | 164 else: |
165 if haveDevice(Id.bedroomHub1): | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
166 yield resetDevice(hubDevice(Id.bedroomHub1)) |
846 | 167 else: |
168 if haveDevice(Id.bedroomHub0): | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
169 yield resetDevice(hubDevice(Id.bedroomHub0)) |
846 | 170 else: |
171 raise ValueError( | |
172 "don't even have the first hub") | |
173 resetModules(['gspca_zc3xx']) | |
926
22af7ea8c401
don't start webcam on bang for now
drewp <drewp@bigasterisk.com>
parents:
885
diff
changeset
|
174 #supervisorRestart(['webcam_9053']) |
846 | 175 else: |
176 log.debug("usb devices look ok") | |
177 | |
178 elif hostname == 'slash': | |
179 haveFrontHub0 = haveDevice(Id.frontDoorHub0) | |
180 haveFrontHub1 = haveDevice(Id.frontDoorHub1) | |
181 haveFrontHub2 = haveDevice(Id.frontDoorHub2) | |
182 haveFrontHub3 = haveDevice(Id.frontDoorHub3) | |
183 haveGarageHub0 = haveDevice(Id.garageHub0) | |
184 haveGarageHub1 = haveDevice(Id.garageHub1) | |
185 haveFrontDoorCam = haveDevice(Id.frontDoorCam) | |
186 haveV4lDevice = os.path.exists( | |
187 "/dev/v4l/by-id/usb-Vimicro_Corp._PC_Camera-video-index0") | |
188 haveFrontArduinoServe = (yield getOk('http://slash:9080/')) | |
189 haveFrontWebcamImage = (yield getOk( | |
190 "http://slash:9023/frontDoor", timeout=10)) | |
191 | |
192 log.info(str(vars())) | |
193 | |
194 if not haveDevice(Id.ftdi): | |
195 if haveFrontHub3: | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
196 yield resetDevice(hubDevice(Id.frontDoorHub3)) |
846 | 197 else: |
198 if haveFrontHub2: | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
199 yield resetDevice(hubDevice(Id.frontDoorHub2)) |
846 | 200 else: |
201 if haveFrontHub1: | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
202 yield resetDevice(hubDevice(Id.frontDoorHub1)) |
846 | 203 else: |
204 if haveFrontHub0: | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
205 yield resetDevice(hubDevice(Id.frontDoorHub0)) |
846 | 206 else: |
207 raise ValueError("don't have the first hub") | |
208 else: | |
209 log.debug("front door chain looks ok") | |
210 | |
211 if not haveDevice(Id.garagePowerSerial): | |
212 if haveGarageHub1: | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
213 yield resetDevice(hubDevice(Id.garageHub1)) |
846 | 214 else: |
215 if haveGarageHub0: | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
216 yield resetDevice(hubDevice(Id.garageHub0)) |
846 | 217 else: |
218 raise ValueError("don't have the first hub") | |
219 else: | |
220 log.debug("garage chain looks ok") | |
221 | |
222 if not haveDevice(Id.garageArduino): | |
223 if haveGarageHub1: | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
224 yield resetDevice(hubDevice(Id.garageHub1)) |
846 | 225 else: |
226 raise ValueError("don't even have the first hub") | |
227 resetModules(['gspca_zc3xx']) | |
228 supervisorRestart(['frontDoorArduino_9080']) | |
229 else: | |
230 if not haveFrontArduinoServe: | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
231 yield resetDevice(hubDevice(Id.frontDoorHub3)) |
846 | 232 supervisorRestart(['frontDoorArduino_9080']) |
233 time.sleep(10) | |
234 else: | |
235 log.debug("frontDoorArduino looks ok") | |
236 | |
237 if not haveFrontWebcamImage: | |
238 supervisorRestart(['webcam_frontDoor_9023']) | |
239 else: | |
240 log.debug("front webcam looks ok") | |
241 | |
242 elif hostname == 'dash': | |
243 if not os.path.exists("/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A900gbcG-if00-port0"): | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
244 yield resetDevice(hubDevice("/dev/bus/usb/003/001")) |
846 | 245 |
246 else: | |
247 raise NotImplementedError | |
248 | |
249 log.debug(" -- finished") | |
250 self.lastPollTime = now | |
251 | |
252 except Exception, e: | |
253 print "poll error", e | |
254 traceback.print_exc() | |
255 | |
256 class Index(PrettyErrorHandler, cyclone.web.RequestHandler): | |
257 def get(self): | |
258 self.settings.background.assertIsCurrent() | |
259 | |
260 self.set_header("Content-Type", "application/xhtml+xml") | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
261 self.write(open("index.html").read()) |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
262 |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
263 class Devices(PrettyErrorHandler, cyclone.web.RequestHandler): |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
264 def get(self): |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
265 out = [] |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
266 for line in commands.getoutput("lsusb").splitlines(): |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
267 words = line.split(None, 6) |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
268 for name, usbId in Id.__dict__.items(): |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
269 if usbId == words[5]: |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
270 break |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
271 else: |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
272 name = "?" |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
273 out.append(dict(dev="/dev/bus/usb/%s/%s" % (words[1], words[3].rstrip(':')), |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
274 name=name, |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
275 usbId=words[5], |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
276 usbName=words[6] if len(words) > 6 else "")) |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
277 |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
278 self.set_header("Content-Type", "application/json") |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
279 self.write(json.dumps({'devices':out})) |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
280 |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
281 class Reset(PrettyErrorHandler, cyclone.web.RequestHandler): |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
282 @inlineCallbacks |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
283 def post(self): |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
284 dev = self.get_argument('dev') |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
285 assert re.match("^[a-z0-9/]+$", dev), dev |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
286 yield resetDevice(dev) |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
287 |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
288 self.set_header("Content-Type", "text/plain") |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
289 self.write("ok") |
846 | 290 |
291 if __name__ == '__main__': | |
292 config = { # to be read from a file | |
293 'servePort' : 9100, | |
294 'checkPeriod' : 30, | |
295 } | |
296 | |
297 from twisted.python import log as twlog | |
298 #twlog.startLogging(sys.stdout) | |
299 | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
300 log.setLevel(logging.DEBUG) |
846 | 301 |
302 p = Background(config, config['checkPeriod']) | |
303 task.LoopingCall(p.step).start(config['checkPeriod']) | |
304 | |
305 reactor.listenTCP(config['servePort'], cyclone.web.Application([ | |
306 (r"/", Index), | |
885
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
307 (r"/devices", Devices), |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
308 (r"/devices/reset", Reset), |
096ecee75e0b
updated device tree. web ui for resetting things
drewp <drewp@bigasterisk.com>
parents:
847
diff
changeset
|
309 ], background=p, static_path="static")) |
846 | 310 reactor.run() |