1 # -*- coding: utf-8 -*-
6 from os.path import join
7 from threading import Thread, Lock
8 from select import select
9 from Queue import Queue, Empty
12 import openerp.addons.hw_proxy.controllers.main as hw_proxy
13 from openerp import http
14 from openerp.http import request
15 from openerp.tools.translate import _
17 _logger = logging.getLogger(__name__)
22 _logger.error('OpenERP module hw_scanner depends on the evdev python module')
26 class Scanner(Thread):
30 self.status = {'status':'connecting', 'messages':[]}
31 self.input_dir = '/dev/input/by-id/'
32 self.barcodes = Queue()
90 def lockedstart(self):
92 if not self.isAlive():
96 def set_status(self, status, message = None):
97 if status == self.status['status']:
98 if message != None and message != self.status['messages'][-1]:
99 self.status['messages'].append(message)
101 self.status['status'] = status
103 self.status['messages'] = [message]
105 self.status['messages'] = []
107 if status == 'error' and message:
108 _logger.error('Barcode Scanner Error: '+message)
109 elif status == 'disconnected' and message:
110 _logger.warning('Disconnected Barcode Scanner: '+message)
112 def get_device(self):
116 devices = [ device for device in listdir(self.input_dir)]
117 keyboards = [ device for device in devices if ('kbd' in device) and ('keyboard' not in device.lower())]
118 scanners = [ device for device in devices if ('barcode' in device.lower()) or ('scanner' in device.lower())]
119 if len(scanners) > 0:
120 self.set_status('connected','Connected to '+scanners[0])
121 return evdev.InputDevice(join(self.input_dir,scanners[0]))
122 elif len(keyboards) > 0:
123 self.set_status('connected','Connected to '+keyboards[0])
124 return evdev.InputDevice(join(self.input_dir,keyboards[0]))
126 self.set_status('disconnected','Barcode Scanner Not Found')
128 except Exception as e:
129 self.set_status('error',str(e))
132 def get_barcode(self):
133 """ Returns a scanned barcode. Will wait at most 5 seconds to get a barcode, and will
134 return barcode scanned in the past if they are not older than 5 seconds and have not
135 been returned before. This is necessary to catch barcodes scanned while the POS is
136 busy reading another barcode
143 timestamp, barcode = self.barcodes.get(True, 5)
144 if timestamp > time.time() - 5:
149 def get_status(self):
154 """ This will start a loop that catches all keyboard events, parse barcode
155 sequences and put them on a timestamped queue that can be consumed by
156 the point of sale's requests for barcode events
159 self.barcodes = Queue()
165 while True: # barcodes loop
166 if device: # ungrab device between barcodes and timeouts for plug & play
169 except Exception as e:
170 self.set_status('error',str(e))
171 device = self.get_device()
173 time.sleep(5) # wait until a suitable device is plugged
180 while True: # keycode loop
181 r,w,x = select([device],[],[],5)
182 if len(r) == 0: # timeout
184 events = device.read()
187 if event.type == evdev.ecodes.EV_KEY:
188 #_logger.debug('Evdev Keyboard event %s',evdev.categorize(event))
189 if event.value == 1: # keydown events
190 if event.code in self.keymap:
192 barcode.append(self.keymap[event.code][1])
194 barcode.append(self.keymap[event.code][0])
195 elif event.code == 42 or event.code == 54: # SHIFT
197 elif event.code == 28: # ENTER, end of barcode
198 self.barcodes.put( (time.time(),''.join(barcode)) )
200 elif event.value == 0: #keyup events
201 if event.code == 42 or event.code == 54: # LEFT SHIFT
204 except Exception as e:
205 self.set_status('error',str(e))
209 hw_proxy.drivers['scanner'] = s
211 class ScannerDriver(hw_proxy.Proxy):
212 @http.route('/hw_proxy/scanner', type='json', auth='none', cors='*')
214 return s.get_barcode()