-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserial2uinput.py
242 lines (219 loc) · 9.41 KB
/
serial2uinput.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
#!/usr/bin/python
# Alexander Grothe 2011 - 2012
#
# This script requires python-uinput V 0.6.1. or higher. Additional required packages are libudev0 and libudev-dev.
#
### Fetch the code for python-uinput from git: ###
#
# git clone git://github.com/tuomasjjrasanen/python-uinput.git
# cd python-uinput
# git clone git://github.com/tuomasjjrasanen/libsuinput.git
# sudo python setup.py install
#
###
##!/usr/bin/python
# Alexander Grothe 2011 - 2012
#
# This script requires python-uinput V 0.6.1. or higher. Additional required packages are libudev0 and libudev-dev.
#
### Fetch the code for python-uinput from git: ###
#
# git clone git://github.com/tuomasjjrasanen/python-uinput.git
# cd python-uinput
# git clone git://github.com/tuomasjjrasanen/libsuinput.git
# sudo python setup.py install
#
###
#
# This script must be run as superuser or with sufficent rights to create an uinput device and exspects a lircd socket using pid from /var/run/lirc/lircd.pid under /var/run/lirc/lircd.<pid of lircd> if none is given by --lircd-socket /PATH/TO/LIRCD_SOCKET
# lircd must not be startet with --uinput, but may be started with --release="_up" to prevent ghosting events if necessary.
import simplejson
import serial
import syslog
import string
import socket
import sys
import uinput
import datetime
from threading import Timer
from optparse import OptionParser
class Lirc2uinput:
"""Sends keystrokes to a virtual uinput device after applying a repeat-filter"""
def __init__(self, uinput_name="lircd", options=None, debug=None):
self.lastkey = None
self.wait_repeats = options.wait_repeats
self.max_gap = options.max_gap
self.min_gap = options.min_gap
self.acceleration = options.acceleration
self.gap_delta = (self.max_gap - self.min_gap)*self.acceleration
self.current_gap = self.max_gap
self.repeat_num = 0
self.debug = debug
self.timestamp = datetime.datetime.now()
self.timeout_value = 1
self.events = []
self.timeout = Timer(self.timeout_value, uinput.KEY_COFFEE)
# add all defined KEY_.* to supported key events
for element in dir(uinput):
if element.startswith("KEY_"):
self.events.append(eval("uinput.%s"%element))
self.debug.log("uinput.%s"%element)
# create uinput device
self.device = uinput.Device(self.events, uinput_name)
self.specialkeys = [(1, 114),(1, 115)] # KEY_VOLUMEDOWN and KEY_VOLUMEUP - a "real" repeat behaviour is used.
#print "init"
def get_gap(self,repeat_num):
if self.current_gap > self.min_gap:
self.current_gap = self.current_gap - self.gap_delta
else:
self.debug.log("minimum gap reached")
pass
return self.current_gap
def getKeyname(self, key):
try:
if key[0].islower():
keycmd = eval('uinput.%s'%(key.upper()))
k_upper = False
else:
keycmd = eval('uinput.%s'%(key.replace('_up','')))
k_upper = True
# '_up' ist a suffix added by lircd to keynames optionally to signal key release
except:
keycmd = uinput.KEY_COFFEE
k_upper = True
self.debug.log("Key %s is not supported by your input.h, get a coffee ;)"%key)
#print "%s mapped to %s"%(key,keycmd)
return keycmd, k_upper
def send_key(self,key):
keycmd, k_upper = self.getKeyname(key)
self.debug.log(keycmd)
now = datetime.datetime.now()
# repeated keypress
if self.lastkey == keycmd and (now - self.timestamp).microseconds < self.current_gap:
self.debug.log("Passing keypress %s... too early"%(unicode(keycmd)))
pass
elif self.lastkey == keycmd:
self.debug.log("Repeated keypress %s" %(unicode(keycmd)))
if self.repeat_num >= self.wait_repeats:
self.current_gap = self.get_gap(self.repeat_num)
else:
pass
if self.repeat_num > 0:
self.keypress(keycmd, 2)
self.timestamp = datetime.datetime.now()
self.repeat_num += 1
else:
self.keypress(keycmd, 1)
self.repeat_num += 1
self.lastkey=keycmd
return keycmd
def keypress(self, key, value):
if key:
self.device.emit(key, value)
#print "sending", key, value
class main:
"""Listens to Arduino's serial device and calls a method each time an
IR command is received."""
def __init__(self):
parser = Options()
self.options = parser.get_opts()
self.Dbg = Debug(self.options.debug)
self.syslog_init()
self.currenkey = None
self.lastkey = None
# use /dev/ttyUSB0 or given path as serial
with open(self.options.keymap,'r') as keymapfile:
self.keymap = simplejson.JSONDecoder().decode(keymapfile.read())
syslog.syslog("successfully loaded keymap")
self.Dbg.log(self.keymap)
self.uinputdev = Lirc2uinput(options=self.options, debug=self.Dbg)
self.listen2socket()
def listen2socket(self):
#self.sock.connect(self.socket_path)
ser = serial.Serial(self.options.serial_device, 115200)
#print "connected to ttyUSB0"
#try:
while 1:
line = ser.readline()
p = 0
a = 0
c = 0
n = 0
#print line
if line:
if "Arduino" in line:
#print "received greetings"
pass
elif "switched" in line:
#print "changed status"
pass
elif "is" in line:
#print "received startup value:", line[-3:]
pass
else:
try:
p,a,c,n = line.split()
#print "%s_%s_%s"%(p,a[2:],int(c[2:], 16))
try:
cmd = self.keymap["%s_%s_%s"%(p,a[2:],(int(c[2:], 16)))]
#print cmd
self.currentkey = self.uinputdev.send_key(cmd)
if self.lastkey and self.lastkey == self.currentkey:
timeout.cancel()
timeout = Timer(0.15, self.release)
timeout.start()
self.lastkey = self.currentkey
except KeyError:
#print "unsupported key"
pass
except:
#print "Unexpected error:", sys.exc_info()[0]
pass
except ValueError:
#print line[:-1], p,a,c,n
pass
except:
#print "Unexpected error:", sys.exc_info()[0]
pass
def release(self):
self.uinputdev.keypress(self.lastkey,0)
#print "released key", self.lastkey
self.currentkey = None
self.lastkey = None
self.uinputdev.lastkey = None
self.uinputdev.repeat_num = 0
def syslog_init(self):
self.Dbg.log('Started lircd2uinput with these options:')
self.Dbg.log('wait_repeats = %s'%(self.options.wait_repeats))
self.Dbg.log('max_gap = %s'%(self.options.max_gap))
self.Dbg.log('min_gap = %s'%(self.options.min_gap))
self.Dbg.log('acceleration = %s'%(self.options.acceleration))
class Options:
def __init__(self):
self.parser = OptionParser()
self.parser.add_option("-l", "--min-gap", dest="min_gap", default=100000, type="int",
help="set minimum gap between repeated keystrokes (default 100000)", metavar="MIN_GAP")
self.parser.add_option("-u", "--max-gap", dest="max_gap", default=300000, type="int",
help="set maximum gap between repeated keystrokes (default 300000)", metavar="MAX_GAP")
self.parser.add_option("-r", "--min-repeats", dest="wait_repeats", default=2, type="int",
help="number of repeats before using accelerated keypresses (default = 2)", metavar="WAIT_REPEATS")
self.parser.add_option("-a", "--acceleration", dest="acceleration", default=0.25, type="float",
help="acceleration to get from MAX_GAP to MIN_GAP. default value of 0.25 equals 4 repeated keystrokes to reach maximum speed",
metavar="ACCELERATION")
self.parser.add_option("-s", "--serial-device", dest="serial_device", default="/dev/ttyUSB0",
help="choose lircd socket to listen on", metavar="LIRCD_SOCKET")
self.parser.add_option("-d", "--debug", dest="debug", action="store_true",
help='enable debug mode')
self.parser.add_option("-k", "--keymap", dest="keymap", default="keymap.json",
help="choose keymap to use", metavar="KEYMAP")
def get_opts(self):
(options, args) = self.parser.parse_args()
return options
class Debug():
def __init__(self,isactive=False):
self.active = isactive
def log(self, message):
if self.active:
syslog.syslog(message)
if __name__ == "__main__":
vlirc = main()