So. According my own suggestion to create a german keyboard mapping here are my two scripts, which are still DEV VERSIONS, not yet in production, but work correctly on console output.
The first one I named hidraw_survey.py
because it is wondering all the time, if new HIDs are connected or unplugged. Both events cause a pkill -f
and restart of the second script read_hidraw_scanner.py
, which handles the keyboard events of all connected HIDs at once.
As there are the following:
device /dev/input/event4, name "OPTO-E Barcode Device", phys "usb-3f980000.usb-1.5.4/input0"
device /dev/input/event0, name "CHICONY HP Basic USB Keyboard", phys "usb-3f980000.usb-1.5.1/input0"
device /dev/input/event1, name "Sycreader USB Reader", phys "usb-3f980000.usb-1.5.2/input0"
device /dev/input/event2, name "HID 04d9:1203", phys "usb-3f980000.usb-1.5.3/input0"
device /dev/input/event3, name "HID 04d9:1203", phys "usb-3f980000.usb-1.5.3/input1"
Why the numerical keypad (last on list) is counted twice... no idea, but the input events are processed correctly.
Both scripts try to find out and guess the numbers of the input event connections between 0 and 9 and skip these numbers, on which the tests are not successful. The language specific mapping itself is realized by LANG = locale.getdefaultlocale()[0].split('_')[0].upper()
which delivers me DE
.
The first script hidraw_survey.py
:
import sys
import os
from pathlib import Path
import time
logfile = '/ddpos/log/hidraw_scanner.txt'
f = open(logfile, 'a')
f.write('hidraw_scanner started: '+time.strftime("%d.%m.%Y %H:%M:%S")+'\n')
f.close()
def try_kill_script(script):
try:
os.system('pkill -f '+script)
except OSError:
return False
else:
return True
old = ''
new = ''
while 1:
out = ''
for i in range(10):
hidrawfile = '/dev/hidraw'+str(i)
hidrawpath = Path(hidrawfile)
if hidrawpath.exists():
out += str(i)
#print (out)
new = out
if new != old:
print ('change registered')
f = open(logfile, 'a')
f.write('USB changed to: '+out+'\n')
f.close()
try_kill_script('read_hidraw_scanner.py')
os.system('python3 /ddpos/test/read_hidraw_scanner.py &')
old = out
The 2nd script read_hidraw_scanner.py
:
import os
import sys
from evdev import InputDevice, list_devices, ecodes, categorize
from select import select
from pathlib import Path
import locale
LANG = locale.getdefaultlocale()[0].split('_')[0].upper()
CODE_MAP_CHAR = {
'DE': {
'KEY_A': "a",
'KEY_B': "b",
'KEY_C': "c",
'KEY_D': "d",
'KEY_E': "e",
'KEY_F': "f",
'KEY_G': "g",
'KEY_H': "h",
'KEY_I': "i",
'KEY_J': "j",
'KEY_K': "k",
'KEY_L': "l",
'KEY_M': "m",
'KEY_N': "n",
'KEY_O': "o",
'KEY_P': "p",
'KEY_Q': "q",
'KEY_R': "r",
'KEY_S': "s",
'KEY_T': "t",
'KEY_U': "u",
'KEY_V': "v",
'KEY_W': "w",
'KEY_X': "x",
'KEY_Y': "z",
'KEY_Z': "y",
'SHIFT_KEY_A': "A",
'SHIFT_KEY_B': "B",
'SHIFT_KEY_C': "C",
'SHIFT_KEY_D': "D",
'SHIFT_KEY_E': "E",
'SHIFT_KEY_F': "F",
'SHIFT_KEY_G': "G",
'SHIFT_KEY_H': "H",
'SHIFT_KEY_I': "I",
'SHIFT_KEY_J': "J",
'SHIFT_KEY_K': "K",
'SHIFT_KEY_L': "L",
'SHIFT_KEY_M': "M",
'SHIFT_KEY_N': "N",
'SHIFT_KEY_O': "O",
'SHIFT_KEY_P': "P",
'SHIFT_KEY_Q': "Q",
'SHIFT_KEY_R': "R",
'SHIFT_KEY_S': "S",
'SHIFT_KEY_T': "T",
'SHIFT_KEY_U': "U",
'SHIFT_KEY_V': "V",
'SHIFT_KEY_W': "W",
'SHIFT_KEY_X': "X",
'SHIFT_KEY_Y': "Z",
'SHIFT_KEY_Z': "Y",
'SHIFT_KEY_GRAVE':'°',
'SHIFT_KEY_1': "!",
'SHIFT_KEY_2': "\"",
'SHIFT_KEY_3': "§",
'SHIFT_KEY_4': "$",
'SHIFT_KEY_5': "%",
'SHIFT_KEY_6': "&",
'SHIFT_KEY_7': "/",
'SHIFT_KEY_8': "(",
'SHIFT_KEY_9': ")",
'SHIFT_KEY_0': "=",
'SHIFT_KEY_EQUAL': "`",
'SHIFT_KEY_RIGHTBRACE':'*',
'SHIFT_KEY_BACKSLASH':'\'',
'SHIFT_KEY_COMMA':';',
'SHIFT_KEY_DOT':':',
'SHIFT_KEY_SLASH':'_',
'ALTGR_KEY_Q': "@",
'ALTGR_KEY_MINUS': "\\",
'ALTGR_KEY_7': "{",
'ALTGR_KEY_0': "}",
'ALTGR_KEY_8': "[",
'ALTGR_KEY_9': "]",
'ALTGR_KEY_RIGHTBRACE':'~',
'KEY_NUMERIC_STAR': "*",
'KEY_GRAVE':'^',
'KEY_LEFTBRACE':'Ü',
'KEY_RIGHTBRACE':'+',
'KEY_SEMICOLON':'Ö',
'KEY_APOSTROPHE':'Ä',
'KEY_BACKSLASH':'#',
'KEY_102ND':'<',
'KEY_COMMA':',',
'KEY_DOT':'.',
'KEY_SLASH':'-',
'KEY_KPSLASH':'/',
'KEY_KPASTERISK':'*',
'KEY_KPMINUS':'-',
'KEY_KPPLUS':'+',
'KEY_KPDOT':'.',
'KEY_SPACE': " ",
'KEY_EQUAL': "´",
'KEY_TAB': "\t",
'KEY_MINUS': "ß",
'KEY_SPACE': " ",
'KEY_NUMERIC_1': "1",
'KEY_NUMERIC_2': "2",
'KEY_NUMERIC_3': "3",
'KEY_NUMERIC_4': "4",
'KEY_NUMERIC_5': "5",
'KEY_NUMERIC_6': "6",
'KEY_NUMERIC_7': "7",
'KEY_NUMERIC_8': "8",
'KEY_NUMERIC_9': "9",
'KEY_NUMERIC_0': "0",
'KEY_KP1': "1",
'KEY_KP2': "2",
'KEY_KP3': "3",
'KEY_KP4': "4",
'KEY_KP5': "5",
'KEY_KP6': "6",
'KEY_KP7': "7",
'KEY_KP8': "8",
'KEY_KP9': "9",
'KEY_KP0': "0",
'KEY_1': "1",
'KEY_2': "2",
'KEY_3': "3",
'KEY_4': "4",
'KEY_5': "5",
'KEY_6': "6",
'KEY_7': "7",
'KEY_8': "8",
'KEY_9': "9",
'KEY_0': "0"
},
'EN': {
'KEY_A': "A",
'KEY_B': "B",
'KEY_C': "C",
'KEY_D': "D",
'KEY_E': "E",
'KEY_F': "F",
'KEY_G': "G",
'KEY_H': "H",
'KEY_I': "I",
'KEY_J': "J",
'KEY_K': "K",
'KEY_L': "L",
'KEY_M': "M",
'KEY_N': "N",
'KEY_O': "O",
'KEY_P': "P",
'KEY_Q': "Q",
'KEY_R': "R",
'KEY_S': "S",
'KEY_T': "T",
'KEY_U': "U",
'KEY_V': "V",
'KEY_W': "W",
'KEY_X': "X",
'KEY_Y': "Y",
'KEY_Z': "Z",
'KEY_GRAVE':'`',
'KEY_MINUS': "-",
'KEY_SPACE': " ",
'KEY_BACKSLASH': "\\",
'KEY_GRAVE': "`",
'KEY_NUMERIC_STAR': "*",
'KEY_LEFTBRACE': "[",
'KEY_RIGHTBRACE': "]",
'KEY_COMMA': ",",
'KEY_EQUAL': "=",
'KEY_SEMICOLON': ";",
'KEY_APOSTROPHE': "'",
'KEY_TAB': "\t",
'KEY_DOT': ".",
'KEY_SLASH': "/",
'KEY_NUMERIC_1': "1",
'KEY_NUMERIC_2': "2",
'KEY_NUMERIC_3': "3",
'KEY_NUMERIC_4': "4",
'KEY_NUMERIC_5': "5",
'KEY_NUMERIC_6': "6",
'KEY_NUMERIC_7': "7",
'KEY_NUMERIC_8': "8",
'KEY_NUMERIC_9': "9",
'KEY_NUMERIC_0': "0",
'KEY_KP1': "1",
'KEY_KP2': "2",
'KEY_KP3': "3",
'KEY_KP4': "4",
'KEY_KP5': "5",
'KEY_KP6': "6",
'KEY_KP7': "7",
'KEY_KP8': "8",
'KEY_KP9': "9",
'KEY_KP0': "0",
'KEY_1': "1",
'KEY_2': "2",
'KEY_3': "3",
'KEY_4': "4",
'KEY_5': "5",
'KEY_6': "6",
'KEY_7': "7",
'KEY_8': "8",
'KEY_9': "9",
'KEY_0': "0"
}
}
def parse_key_to_char(val):
return CODE_MAP_CHAR[LANG][val] if val in CODE_MAP_CHAR[LANG] else ""
def try_kill_script(script):
try:
os.system('pkill -f '+script)
except OSError:
return False
else:
return True
founddev = []
for i in range(10):
trydev = '/dev/input/event'+str(i)
if Path(trydev).exists():
founddev.append(trydev)
devices = map(InputDevice, founddev)
devices = {dev.fd: dev for dev in devices}
for dev in devices.values():
print(dev)
chars = ''
prefix = ''
while 1:
r, w, x = select(devices, [], [])
for fd in r:
try:
for event in devices[fd].read():
if event.type == ecodes.EV_KEY:
e = categorize(event)
if e.keystate == e.key_down:
if e.keycode == 'KEY_LEFTSHIFT' or e.keycode == 'KEY_RIGHTSHIFT':
prefix = 'SHIFT_'
elif e.keycode == 'KEY_LEFTALT':
prefix = 'ALT_'
elif e.keycode == 'KEY_RIGHTALT':
prefix = 'ALTGR_'
print ('prefix='+prefix)
elif e.keycode == 'KEY_ESC':
chars = ''
elif e.keycode == 'KEY_BACKSPACE':
chars = chars[:-1]
elif e.keycode == 'KEY_ENTER' or e.keycode == 'KEY_KPENTER':
sys.stdout.write(chars+'\n')
sys.stdout.flush()
chars = ''
else:
#print (ecodes.EV_LED) # which outputs 17 forever
print (prefix + e.keycode)
chars += parse_key_to_char(prefix + e.keycode)
elif e.keystate == e.key_up:
#sys.stdout.write(parse_key_to_char(e.keycode))
#sys.stdout.flush()
#print ("'"+e.keycode+"':'"+parse_key_to_char(e.keycode)+"',")
if e.keycode == 'KEY_LEFTSHIFT' or e.keycode == 'KEY_RIGHTSHIFT':
prefix = ''
elif e.keycode == 'KEY_LEFTALT':
prefix = ''
elif e.keycode == 'KEY_RIGHTALT':
prefix = ''
except:
try_kill_script('read_hidraw_scanner.py')
Both scripts are full of dents and wrinkles made by a python newbie like me, but I think with a bit of improvement and simplifications it's a really nice toy to tell the RaspPi something like config settings or password inputs or whatever (in my case a barcode scanner with alternative manual input in case of not readable barcode and a RFID reader for waiter/waitress authentification in restaurants)
The mapping covers all german keyboard keys including SHIFT and ALTGR (right ALT), not yet ALT (left ALT). But it doesn't work with accents like è or é or ñ, nor CAPSLOCK. Improvements are welcome.
UPDATE
The output looks like that at the moment.
One after the other, a scanned numeric barcode, an alphanumeric barcode, a keyboard input and the RDFID reader at last.
KEY_0
KEY_4
KEY_0
KEY_1
KEY_3
KEY_6
KEY_0
KEY_0
KEY_0
KEY_0
KEY_4
KEY_8
KEY_6
KEY_0
KEY_9
KEY_5
0401360000486095
KEY_2
SHIFT_KEY_B
KEY_5
KEY_6
KEY_6
KEY_9
SHIFT_KEY_C
SHIFT_KEY_F
KEY_4
SHIFT_KEY_A
KEY_2
KEY_1
SHIFT_KEY_C
KEY_1
SHIFT_KEY_B
KEY_7
2B5669CF4A21C1B7
KEY_M
KEY_Z
KEY_N
KEY_A
KEY_M
KEY_E
ALTGR_KEY_Q
KEY_E
KEY_M
KEY_A
KEY_I
KEY_L
KEY_DOT
KEY_C
KEY_O
KEY_M
[email protected]
KEY_0
KEY_0
KEY_0
KEY_5
KEY_6
KEY_1
KEY_1
KEY_4
KEY_9
KEY_6
0005611496
AZERTY
keyboard, pressingA
would result in scancodeKEY_Q
and keycodea
). The mapping of scancodes to keycodes is done by the virtual console driver. Evdev can only do scancodes. Mapping between the two is not straightforward, but I've been thinking of adding an example to python-evdev, since it's an often requested feature (I'll try this weekend). In the meantime, you can have a look at the keyboard project. – AmazeLANG = locale.getdefaultlocale()[0].split('_')[0].upper()
and the map likeCODE_MAP_CHAR = { 'DE' : { 'KEY_Z': "Y", 'KEY_Y': "Z" }, 'EN' : { 'KEY_Z': "Z", 'KEY_Y': "Y" } }
and the function like thisdef parse_key_to_char(val): return CODE_MAP_CHAR[LANG][val] if val in CODE_MAP_CHAR[LANG] else ""
– Hardwood