summaryrefslogtreecommitdiff
path: root/addons/hw_scanner/controllers
diff options
context:
space:
mode:
authorAntoine Prieels <anp@odoo.com>2019-03-28 10:57:35 +0100
committerAntoine Prieels <anp@odoo.com>2019-08-13 15:35:38 +0200
commit4e649a8ca99ccfdd5dda7334b2682c5287cbc731 (patch)
treec89ffdbad178b81be1f82641bca50d89b62887a1 /addons/hw_scanner/controllers
parent9790c81d5b1d0a265c75f191320cb4cb22dfbd27 (diff)
[REF] hw_{driver, scanner}: Barcode scanners as IoT Devices
Barcode scanners are now part of KeyboardUSBDriver. We also add the possibility to change the keyboard/scanner layout from an action. The list of available layout can be retrieved with a new route. `hw_scanner` has been removed. Backward compatibility is ensured because the name of the route that is now used in community is the same that was used previously. TaskID: 1939455
Diffstat (limited to 'addons/hw_scanner/controllers')
-rw-r--r--addons/hw_scanner/controllers/__init__.py4
-rw-r--r--addons/hw_scanner/controllers/main.py240
2 files changed, 0 insertions, 244 deletions
diff --git a/addons/hw_scanner/controllers/__init__.py b/addons/hw_scanner/controllers/__init__.py
deleted file mode 100644
index 5d4b25db9c0..00000000000
--- a/addons/hw_scanner/controllers/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from . import main
diff --git a/addons/hw_scanner/controllers/main.py b/addons/hw_scanner/controllers/main.py
deleted file mode 100644
index 86a01497b46..00000000000
--- a/addons/hw_scanner/controllers/main.py
+++ /dev/null
@@ -1,240 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-import logging
-import time
-from os import listdir
-from os.path import join, isdir
-try:
- from queue import Queue, Empty
-except ImportError:
- from Queue import Queue, Empty # pylint: disable=deprecated-module
-from select import select
-from threading import Thread, Lock
-
-from odoo import http
-
-from odoo.addons.hw_proxy.controllers import main as hw_proxy
-
-_logger = logging.getLogger(__name__)
-
-try:
- import evdev
-except ImportError:
- _logger.error('Odoo module hw_scanner depends on the evdev python module')
- evdev = None
-
-class ScannerDevice():
- def __init__(self, path):
- self.evdev = evdev.InputDevice(path)
- self.evdev.grab()
-
- self.barcode = []
- self.shift = False
-
-class Scanner(Thread):
- def __init__(self):
- Thread.__init__(self)
- self.lock = Lock()
- self.status = {'status':'connecting', 'messages':[]}
- self.input_dir = '/dev/input/by-id/'
- self.open_devices = []
- self.barcodes = Queue()
- self.keymap = {
- 2: ("1","!"),
- 3: ("2","@"),
- 4: ("3","#"),
- 5: ("4","$"),
- 6: ("5","%"),
- 7: ("6","^"),
- 8: ("7","&"),
- 9: ("8","*"),
- 10:("9","("),
- 11:("0",")"),
- 12:("-","_"),
- 13:("=","+"),
- # 14 BACKSPACE
- # 15 TAB
- 16:("q","Q"),
- 17:("w","W"),
- 18:("e","E"),
- 19:("r","R"),
- 20:("t","T"),
- 21:("y","Y"),
- 22:("u","U"),
- 23:("i","I"),
- 24:("o","O"),
- 25:("p","P"),
- 26:("[","{"),
- 27:("]","}"),
- # 28 ENTER
- # 29 LEFT_CTRL
- 30:("a","A"),
- 31:("s","S"),
- 32:("d","D"),
- 33:("f","F"),
- 34:("g","G"),
- 35:("h","H"),
- 36:("j","J"),
- 37:("k","K"),
- 38:("l","L"),
- 39:(";",":"),
- 40:("'","\""),
- 41:("`","~"),
- # 42 LEFT SHIFT
- 43:("\\","|"),
- 44:("z","Z"),
- 45:("x","X"),
- 46:("c","C"),
- 47:("v","V"),
- 48:("b","B"),
- 49:("n","N"),
- 50:("m","M"),
- 51:(",","<"),
- 52:(".",">"),
- 53:("/","?"),
- # 54 RIGHT SHIFT
- 57:(" "," "),
- }
-
- def lockedstart(self):
- with self.lock:
- if not self.isAlive():
- self.daemon = True
- self.start()
-
- def set_status(self, status, message = None):
- if status == self.status['status']:
- if message != None and message != self.status['messages'][-1]:
- self.status['messages'].append(message)
- else:
- self.status['status'] = status
- if message:
- self.status['messages'] = [message]
- else:
- self.status['messages'] = []
-
- if status == 'error' and message:
- _logger.error('Barcode Scanner Error: '+message)
- elif status == 'disconnected' and message:
- _logger.info('Disconnected Barcode Scanner: %s', message)
-
- def get_devices(self):
- try:
- if not evdev:
- return []
-
- if not isdir(self.input_dir):
- return []
-
- new_devices = [device for device in listdir(self.input_dir)
- if join(self.input_dir, device) not in [dev.evdev.fn for dev in self.open_devices]]
- scanners = [device for device in new_devices
- if (('kbd' in device) and ('keyboard' not in device.lower()))
- or ('barcode' in device.lower()) or ('scanner' in device.lower())]
-
- for device in scanners:
- _logger.debug('opening device %s', join(self.input_dir,device))
- self.open_devices.append(ScannerDevice(join(self.input_dir,device)))
-
- if self.open_devices:
- self.set_status('connected','Connected to '+ str([dev.evdev.name for dev in self.open_devices]))
- else:
- self.set_status('disconnected','Barcode Scanner Not Found')
-
- return self.open_devices
- except Exception as e:
- self.set_status('error',str(e))
- return []
-
- def release_device(self, dev):
- self.open_devices.remove(dev)
-
- def get_barcode(self):
- """ Returns a scanned barcode. Will wait at most 5 seconds to get a barcode, and will
- return barcode scanned in the past if they are not older than 5 seconds and have not
- been returned before. This is necessary to catch barcodes scanned while the POS is
- busy reading another barcode
- """
-
- self.lockedstart()
-
- while True:
- try:
- timestamp, barcode = self.barcodes.get(True, 5)
- if timestamp > time.time() - 5:
- return barcode
- except Empty:
- return ''
-
- def get_status(self):
- self.lockedstart()
- return self.status
-
- def _get_open_device_by_fd(self, fd):
- for dev in self.open_devices:
- if dev.evdev.fd == fd:
- return dev
-
- def run(self):
- """ This will start a loop that catches all keyboard events, parse barcode
- sequences and put them on a timestamped queue that can be consumed by
- the point of sale's requests for barcode events
- """
-
- self.barcodes = Queue()
-
- barcode = []
- shift = False
- devices = None
-
- while True: # barcodes loop
- devices = self.get_devices()
-
- try:
- while True: # keycode loop
- r,w,x = select({dev.fd: dev for dev in [d.evdev for d in devices]},[],[],5)
- if len(r) == 0: # timeout
- break
-
- for fd in r:
- device = self._get_open_device_by_fd(fd)
-
- if not evdev.util.is_device(device.evdev.fn):
- _logger.info('%s disconnected', str(device.evdev))
- self.release_device(device)
- break
-
- events = device.evdev.read()
-
- for event in events:
- if event.type == evdev.ecodes.EV_KEY:
- # _logger.debug('Evdev Keyboard event %s',evdev.categorize(event))
- if event.value == 1: # keydown events
- if event.code in self.keymap:
- if device.shift:
- device.barcode.append(self.keymap[event.code][1])
- else:
- device.barcode.append(self.keymap[event.code][0])
- elif event.code == 42 or event.code == 54: # SHIFT
- device.shift = True
- elif event.code == 28: # ENTER, end of barcode
- _logger.debug('pushing barcode %s from %s', ''.join(device.barcode), str(device.evdev))
- self.barcodes.put( (time.time(),''.join(device.barcode)) )
- device.barcode = []
- elif event.value == 0: #keyup events
- if event.code == 42 or event.code == 54: # LEFT SHIFT
- device.shift = False
-
- except Exception as e:
- self.set_status('error',str(e))
-
-scanner_thread = None
-if evdev:
- scanner_thread = Scanner()
- hw_proxy.drivers['scanner'] = scanner_thread
-
-class ScannerDriver(hw_proxy.Proxy):
- @http.route('/hw_proxy/scanner', type='json', auth='none', cors='*')
- def scanner(self):
- return scanner_thread.get_barcode() if scanner_thread else None