LIRC libraries
LinuxInfraredRemoteControl
 All Classes Files Functions Variables Typedefs Enumerations Macros Modules Pages
client.py
Go to the documentation of this file.
1 ''' Top-level python bindings for the lircd socket interface. '''
2 ##
3 # @file client.py
4 # @author Alec Leamas
5 # @brief Python bindings for a subset of the lirc_client.h interface.
6 # @ingroup python_bindings
7 
8 ##
9 # @defgroup python_bindings Python client bindings
10 #
11 # Unstable python interfaces to read and send lirc data.
12 #
13 # Sending is a pure python implementation described in @ref sending.
14 #
15 # Reading data uses a C extension module, see @ref receiving. This also
16 # includes AsyncConnection with a small asynchronous interface to read data.
17 #
18 # The otherwise undocumented file config.py, which can be imported using
19 # <i>import lirc.config</i>, provides access to the paths defined when
20 # running configure such as VARRUNDIR (often /var/run) and SYSCONFDIR
21 # (typically /etc).
22 
23 # pylint: disable=W0613
24 
25 ## @addtogroup python_bindings
26 # @{
27 
28 from abc import ABCMeta, abstractmethod
29 from enum import Enum
30 import configparser
31 import os
32 import os.path
33 import selectors
34 import socket
35 import time
36 
37 import lirc.config
38 import _client
39 
40 _DEFAULT_PROG = 'lircd-client'
41 
42 
43 def get_default_socket_path() -> str:
44  ''' Get default value for the lircd socket path, using (falling priority):
45 
46  - The environment variable LIRC_SOCKET_PATH.
47  - The 'output' value in the lirc_options.conf file if value and the
48  corresponding file exists.
49  - A hardcoded default lirc.config.VARRUNDIR/lirc/lircd, possibly
50  non-existing.
51  '''
52 
53  if 'LIRC_SOCKET_PATH' in os.environ:
54  return os.environ['LIRC_SOCKET_PATH']
55  path = lirc.config.SYSCONFDIR + '/lirc/lirc_options.conf'
56  parser = configparser.SafeConfigParser()
57  try:
58  parser.read(path)
59  except configparser.Error:
60  pass
61  else:
62  if parser.has_section('lircd'):
63  try:
64  path = str(parser.get('lircd', 'output'))
65  if os.path.exists(path):
66  return path
67  except configparser.NoOptionError:
68  pass
69  return lirc.config.VARRUNDIR + '/lirc/lircd'
70 
71 
72 def get_default_lircrc_path() -> str:
73  ''' Get default path to the lircrc file according to (falling priority):
74 
75  - $XDG_CONFIG_HOME/lircrc if environment variable and file exists.
76  - ~/.config/lircrc if it exists.
77  - ~/.lircrc if it exists
78  - A hardcoded default lirc.config.SYSCONFDIR/lirc/lircrc, whether
79  it exists or not.
80  '''
81  if 'XDG_CONFIG_HOME' in os.environ:
82  path = os.path.join(os.environ['XDG_CONFIG_HOME'], 'lircrc')
83  if os.path.exists(path):
84  return path
85  path = os.path.join(os.path.expanduser('~'), '.config' 'lircrc')
86  if os.path.exists(path):
87  return path
88  path = os.path.join(os.path.expanduser('~'), '.lircrc')
89  if os.path.exists(path):
90  return path
91  return os.path.join(lirc.config.SYSCONFDIR, 'lirc', 'lircrc')
92 
93 
94 class BadPacketException(Exception):
95  ''' Malformed or otherwise unparsable packet received. '''
96  pass
97 
98 
99 class TimeoutException(Exception):
100  ''' Timeout receiving data from remote host.'''
101  pass
102 
104 ##
105 # @defgroup receiving Classes to receive keypresses
106 #
107 # Interface to read raw code strings like irw(1) or
108 # translated application strings like ircat(1).
109 #
110 # Reading raw data
111 # ----------------
112 #
113 # Reading raw data direct from the lircd socket can be done with the
114 # RawConnection object using something like
115 #
116 # from lirc import RawConnnection
117 # with RawConnection(socket_path) as conn:
118 # while True:
119 # keypress = conn.readline()
120 # ... do something with keypress
121 #
122 # The optional socket_path argument is the path to the lircd socket.
123 # Refer to get_default_socket_path() for defaults if omitted.
124 #
125 # See also the @ref irw.py example.
126 #
127 #
128 # Reading lircrc-translated data.
129 # -------------------------------
130 #
131 # Reading application strings translated with lircrc can be achieved using
132 #
133 # from lirc import LircdConnection
134 # with LircdConnection(program, lircrc_path, socket_path) as conn:
135 # while True:
136 # string = conn.readline()
137 # ... do domething with string
138 #
139 # The arguments:
140 # - program: Program identifier as described in ircat(1).
141 # - lircrc_path: Path to lircrc file. See
142 # get_default_lircrc_path() for defaults if omitted.
143 # - socket_path: See RawConnection above.
144 #
145 # See also the @ref ircat.py example
146 #
147 # @example irw.py
148 # @example ircat.py
149 #
150 # @addtogroup receiving
151 # @{
152 
153 
154 class AbstractConnection(metaclass=ABCMeta):
155  ''' Abstract interface for all connections. '''
156 
157  def __enter__(self):
158  return self
159 
160  def __exit__(self, exc_type, exc, traceback):
161  self.close()
162 
163  @abstractmethod
164  def readline(self, timeout: float = None) -> str:
165  ''' Read a buffered line
166 
167  Parameters:
168  - timeout: seconds.
169  - If set to 0 immediately return either a line or None.
170  - If set to None (default mode) use blocking read.
171 
172  Returns: code string as described in lircd(8) without trailing
173  newline or None.
174 
175  Raises: TimeoutException if timeout > 0 expires.
176  '''
177  pass
178 
179  @abstractmethod
180  def fileno(self) -> int:
181  ''' Return the file nr used for IO, suitable for select() etc. '''
182  pass
183 
184  @abstractmethod
185  def has_data(self) -> bool:
186  ''' Return true if next readline(None) won't block . '''
187  pass
188 
189  @abstractmethod
190  def close(self):
191  ''' Close/release all resources '''
192  pass
193 
195 class RawConnection(AbstractConnection):
196  ''' Interface to receive code strings as described in lircd(8).
197 
198  Parameters:
199  - socket_path: lircd output socket path, see get_default_socket_path()
200  for defaults.
201  - prog: Program name used in lircrc decoding, see ircat(1). Could be
202  omitted if only raw keypresses should be read.
203 
204  '''
205  # pylint: disable=no-member
206 
207  def __init__(self, socket_path: str = None, prog: str = _DEFAULT_PROG):
208  if socket_path:
209  os.environ['LIRC_SOCKET_PATH'] = socket_path
210  else:
211  os.environ['LIRC_SOCKET_PATH'] = get_default_socket_path()
212  _client.lirc_deinit()
213  fd = _client.lirc_init(prog)
214  self._socket = socket.fromfd(fd, socket.AF_UNIX, socket.SOCK_STREAM)
215  self._select = selectors.DefaultSelector()
216  self._select.register(self._socket, selectors.EVENT_READ)
217  self._buffer = bytearray(0)
218 
219  def readline(self, timeout: float = None) -> str:
220  ''' Implements AbstractConnection.readline(). '''
221  if timeout:
222  start = time.clock()
223  while b'\n' not in self._buffer:
224  ready = self._select.select(
225  start + timeout - time.clock() if timeout else timeout)
226  if ready == []:
227  if timeout:
228  raise TimeoutException(
229  "readline: no data within %f seconds" % timeout)
230  else:
231  return None
232  self._buffer += self._socket.recv(4096)
233  line, self._buffer = self._buffer.split(b'\n', 1)
234  return line.decode('ascii', 'ignore')
235 
236  def fileno(self) -> int:
237  ''' Implements AbstractConnection.fileno(). '''
238  return self._socket.fileno()
239 
240  def has_data(self) -> bool:
241  ''' Implements AbstractConnection.has_data() '''
242  return b'\n' in self._buffer
243 
244  def close(self):
245  ''' Implements AbstractConnection.close() '''
246  self._socket.close()
247  _client.lirc_deinit()
249 
250 AbstractConnection.register(RawConnection) # pylint:disable=no-member
251 
252 
254  ''' Interface to receive lircrc-translated keypresses. This is basically
255  built on top of lirc_code2char() and as such supporting centralized
256  translations using lircrc_class. See lircrcd(8).
257 
258  Parameters:
259  - program: string, used to identify client. See ircat(1)
260  - lircrc: lircrc file path. See get_default_lircrc_path() for defaults.
261  - socket_path: lircd output socket path, see get_default_socket_path()
262  for defaults.
263  '''
264  # pylint: disable=no-member
265 
266  def __init__(self, program: str,
267  lircrc_path: str = None,
268  socket_path: str = None):
269  if not lircrc_path:
270  lircrc_path = get_default_lircrc_path()
271  if not lircrc_path:
272  raise FileNotFoundError('Cannot find lircrc config file.')
273  self._connection = RawConnection(socket_path, program)
274  self._lircrc = _client.lirc_readconfig(lircrc_path)
275  self._program = program
276  self._buffer = []
277 
278  def readline(self, timeout: float = None):
279  ''' Implements AbstractConnection.readline(). '''
280  while len(self._buffer) <= 0:
281  code = self._connection.readline(timeout)
282  if code is None:
283  return None
284  strings = \
285  _client.lirc_code2char(self._lircrc, self._program, code)
286  if not strings or len(strings) == 0:
287  if timeout == 0:
288  return None
289  continue
290  self._buffer.extend(strings)
291  return self._buffer.pop(0)
292 
293  def has_data(self) -> bool:
294  ''' Implements AbstractConnection.has_data() '''
295  return len(self._buffer) > 0
296 
297  def fileno(self) -> int:
298  ''' Implements AbstractConnection.fileno(). '''
299  return self._connection.fileno()
300 
301  def close(self):
302  ''' Implements AbstractConnection.close() '''
303  self._connection.close()
304  _client.lirc_freeconfig(self._lircrc)
306 
307 AbstractConnection.register(LircdConnection) # pylint: disable=no-member
308 
309 ## @}
310 
311 
312 ##
313 # @defgroup sending Classes to send commands
314 #
315 # Classes to send a Command to lircd and parse the reply.
316 #
317 # Sending data
318 # ------------
319 #
320 # Sending commands is about creating a command and connection. In the
321 # simplest form it looks like
322 #
323 # import lirc
324 # with lirc.CommandConnection(socket_path=...) as conn:
325 # reply = lirc.StopRepeatCommand(conn, 'mceusb', 'KEY_1').run()
326 # if not reply.success:
327 # print(parser.data[0])
328 #
329 # See also the @ref list-remotes.py, @ref list-keys.py and @ref simulate.py
330 # examples.
331 #
332 # The parameters depends on the actual command; there is a Command
333 # defined for all known lircd commands. socket_path can often be omitted,
334 # see get_default_socket_path() for default locations used.
335 #
336 # The returned object is a Reply with various info on the processed
337 # command.
338 #
339 # To get more control lower-level primitives could be used instead of
340 # run() as in this example:
341 #
342 # while not command.parser.is_completed():
343 # line = conn.readline(0.1)
344 # if line:
345 # command.parser.feed(line)
346 # else:
347 # ... handle timeout
348 # if not command.parser.result == lirc.client.Result.OK:
349 # print('Cannot get version string')
350 # else:
351 # print(command.parser.data[0])
352 # ...
353 # conn.close()
354 #
355 # @example simulate.py
356 # @example list-keys.py
357 # @example list-remotes.py
358 #
359 # @addtogroup sending
360 # @{
361 
362 
364  ''' Extends the parent with a send() method. '''
365 
366  def __init__(self, socket_path: str = None):
367  RawConnection.__init__(self, socket_path)
368 
369  def send(self, command: (bytearray, str)):
370  ''' Send single line over socket '''
371  if not isinstance(command, bytearray):
372  command = command.encode('ascii')
373  while len(command) > 0:
374  sent = self._socket.send(command)
375  command = command[sent:]
376 
377 
378 class Result(Enum):
379  ''' Public reply parser result, available when completed. '''
380  OK = 1
381  FAIL = 2
382  INCOMPLETE = 3
383 
384 
385 class Command(object):
386  ''' Command, parser and connection container with a run() method. '''
387 
388  def __init__(self, cmd: str,
389  connection: AbstractConnection,
390  timeout: float = 0.4):
391  self._conn = connection
392  self._cmd_string = cmd
393  self._parser = ReplyParser()
394 
395  def run(self, timeout: float = None):
396  ''' Run the command and return a Reply. Timeout as of
397  AbstractConnection.readline()
398  '''
399  self._conn.send(self._cmd_string)
400  while not self._parser.is_completed():
401  line = self._conn.readline(timeout)
402  if not line:
403  raise TimeoutException('No data from lircd host.')
404  self._parser.feed(line)
405  return self._parser
406 
407 
408 class Reply(object):
409  ''' The status/result from parsing a command reply.
410 
411  Attributes:
412  result: Enum Result, reflects parser state.
413  success: bool, reflects SUCCESS/ERROR.
414  data: List of lines, the command DATA payload.
415  sighup: bool, reflects if a SIGHUP package has been received
416  (these are otherwise ignored).
417  last_line: str, last input line (for error messages).
418  '''
419  def __init__(self):
420  self.result = Result.INCOMPLETE
421  self.success = None
422  self.data = []
423  self.sighup = False
424  self.last_line = ''
425 
426 
427 class ReplyParser(Reply):
428  ''' Handles the actual parsing of a command reply. '''
429 
430  def __init__(self):
431  Reply.__init__(self)
432  self._state = self._State.BEGIN
433  self._lines_expected = None
434  self._buffer = bytearray(0)
436  def is_completed(self) -> bool:
437  ''' Returns true if no more reply input is required. '''
438  return self.result != Result.INCOMPLETE
439 
440  def feed(self, line: str):
441  ''' Enter a line of data into parsing FSM, update state. '''
443  fsm = {
444  self._State.BEGIN: self._begin,
445  self._State.COMMAND: self._command,
446  self._State.RESULT: self._result,
447  self._State.DATA: self._data,
448  self._State.LINE_COUNT: self._line_count,
449  self._State.LINES: self._lines,
450  self._State.END: self._end,
451  self._State.SIGHUP_END: self._sighup_end
452  }
453  line = line.strip()
454  if not line:
455  return
456  self.last_line = line
457  fsm[self._state](line)
458  if self._state == self._State.DONE:
459  self.result = Result.OK
460 
461 ##
462 # @defgroup FSM Internal parser FSM
463 # @{
464 # Internal parser FSM.
465 # pylint: disable=missing-docstring,redefined-variable-type
466 
467  class _State(Enum):
468  ''' Internal FSM state. '''
469  BEGIN = 1
470  COMMAND = 2
471  RESULT = 3
472  DATA = 4
473  LINE_COUNT = 5
474  LINES = 6
475  END = 7
476  DONE = 8
477  NO_DATA = 9
478  SIGHUP_END = 10
479 
480  def _bad_packet_exception(self, line):
481  self.result = Result.FAIL
482  raise BadPacketException(
483  'Cannot parse: %s\nat state: %s\n' % (line, self._state))
485  def _begin(self, line):
486  if line == 'BEGIN':
487  self._state = self._State.COMMAND
488 
489  def _command(self, line):
490  if not line:
491  self._bad_packet_exception(line)
492  elif line == 'SIGHUP':
493  self._state = self._State.SIGHUP_END
494  self.sighup = True
495  else:
496  self._state = self._State.RESULT
497 
498  def _result(self, line):
499  if line in ['SUCCESS', 'ERROR']:
500  self.success = line == 'SUCCESS'
501  self._state = self._State.DATA
502  else:
503  self._bad_packet_exception(line)
504 
505  def _data(self, line):
506  if line == 'END':
507  self._state = self._State.DONE
508  elif line == 'DATA':
509  self._state = self._State.LINE_COUNT
510  else:
511  self._bad_packet_exception(line)
512 
513  def _line_count(self, line):
514  try:
515  self._lines_expected = int(line)
516  except ValueError:
517  self._bad_packet_exception(line)
518  if self._lines_expected == 0:
519  self._state = self._State.END
520  else:
521  self._state = self._State.LINES
522 
523  def _lines(self, line):
524  self.data.append(line)
525  if len(self.data) >= self._lines_expected:
526  self._state = self._State.END
527 
528  def _end(self, line):
529  if line != 'END':
530  self._bad_packet_exception(line)
531  self._state = self._State.DONE
532 
533  def _sighup_end(self, line):
534  if line == 'END':
535  ReplyParser.__init__(self)
536  self.sighup = True
537  else:
538  self._bad_packet_exception(line)
539 
540 ## @}
541 # FSM
542 # pylint: enable=missing-docstring,redefined-variable-type
543 
544 ## @}
545 
546 
547 ## @defgroup commands Commands to control lircd
548 # Canned classes, one for each command in the lircd(8) socket
549 # interface.
550 #
551 # @addtogroup commands
552 # @{
553 
554 
555 class SimulateCommand(Command):
556  ''' Simulate a button press, see SIMULATE in lircd(8) manpage. '''
557  # pylint: disable=too-many-arguments
558 
559  def __init__(self, connection: AbstractConnection,
560  remote: str, key: str, repeat: int = 1, keycode: int = 0):
561  cmd = 'SIMULATE %016d %02d %s %s\n' % \
562  (int(keycode), int(repeat), key, remote)
563  Command.__init__(self, cmd, connection)
564 
565 
567  ''' List available remotes, see LIST in lircd(8) manpage. '''
568 
569  def __init__(self, connection: AbstractConnection):
570  Command.__init__(self, 'LIST\n', connection)
571 
573 class ListKeysCommand(Command):
574  ''' List available keys in given remote, see LIST in lircd(8) manpage. '''
575 
576  def __init__(self, connection: AbstractConnection, remote: str):
577  Command.__init__(self, 'LIST %s\n' % remote, connection)
578 
579 
581  ''' Start repeating given key, see SEND_START in lircd(8) manpage. '''
582 
583  def __init__(self, connection: AbstractConnection,
584  remote: str, key: str):
585  cmd = 'SEND_START %s %s\n' % (remote, key)
586  Command.__init__(self, cmd, connection)
587 
588 
590  ''' Stop repeating given key, see SEND_STOP in lircd(8) manpage. '''
591 
592  def __init__(self, connection: AbstractConnection,
593  remote: str, key: str):
594  cmd = 'SEND_STOP %s %s\n' % (remote, key)
595  Command.__init__(self, cmd, connection)
596 
598 class SendCommand(Command):
599  ''' Send given key, see SEND_ONCE in lircd(8) manpage. '''
600 
601  def __init__(self, connection: AbstractConnection,
602  remote: str, keys: str):
603  if not len(keys):
604  raise ValueError('No keys to send given')
605  cmd = 'SEND_ONCE %s %s\n' % (remote, ' '.join(keys))
606  Command.__init__(self, cmd, connection)
607 
608 
610  ''' Set transmitters to use, see SET_TRANSMITTERS in lircd(8) manpage.
611 
612  Arguments:
613  transmitter: Either a bitmask or a list of int describing active
614  transmitter numbers.
615  '''
616 
617  def __init__(self, connection: AbstractConnection,
618  transmitters: (int, list)):
619  if isinstance(transmitters, list):
620  mask = 0
621  for transmitter in transmitters:
622  mask |= (1 << (int(transmitter) - 1))
623  else:
624  mask = transmitters
625  cmd = 'SET_TRANSMITTERS %d\n' % mask
626  Command.__init__(self, cmd, connection)
627 
628 
629 class VersionCommand(Command):
630  ''' Get lircd version, see VERSION in lircd(8) manpage. '''
632  def __init__(self, connection: AbstractConnection):
633  Command.__init__(self, 'VERSION\n', connection)
634 
635 
637  ''' Set a driver option value, see DRV_OPTION in lircd(8) manpage. '''
638 
639  def __init__(self, connection: AbstractConnection,
640  option: str, value: str):
641  cmd = 'DRV_OPTION %s %s\n' % (option, value)
642  Command.__init__(self, cmd, connection)
643 
644 
645 class SetLogCommand(Command):
646  ''' Start/stop logging lircd output , see SET_INPUTLOG in lircd(8)
647  manpage.
648  '''
649 
650  def __init__(self, connection: AbstractConnection,
651  logfile: str = None):
652  cmd = 'SET_INPUTLOG' + (' ' + logfile if logfile else '') + '\n'
653  Command.__init__(self, cmd, connection)
654 
655 ## @}
656 
657 
658 ## @defgroup lircrcd Commands to control lircrcd
659 # Canned classes, one for each command in the lircrcd(l8) socket
660 # interface.
661 #
662 # @addtogroup lircrcd
663 # @{
665 
666 class IdentCommand(Command):
667  ''' Identify client using the prog token, see IDENT in lircrcd(8) '''
668 
669  def __init__(self, connection: AbstractConnection,
670  prog: str = None):
671  if not prog:
672  raise ValueError('The prog argument cannot be None')
673  cmd = 'IDENT {}\n'.format(prog)
674  Command.__init__(self, cmd, connection)
675 
676 
677 class CodeCommand(Command):
678  '''Translate a keypress to application string, see CODE in lircrcd(8) '''
679 
680  def __init__(self, connection: AbstractConnection,
681  code: str = None):
682  if not code:
683  raise ValueError('The prog argument cannot be None')
684  Command.__init__(self, 'CODE {}\n'.format(code), connection)
685 
686 
687 class GetModeCommand(Command):
688  '''Get current translation mode, see GETMODE in lircrcd(8) '''
689 
690  def __init__(self, connection: AbstractConnection):
691  Command.__init__(self, "GETMODE\n", connection)
692 
693 
695  '''Set current translation mode, see SETMODE in lircrcd(8) '''
696 
697  def __init__(self, connection: AbstractConnection,
698  mode: str = None):
699  if not mode:
700  raise ValueError('The mode argument cannot be None')
701  Command.__init__(self, 'SETMODE {}\n'.format(mode), connection)
702 
703 ## @}
705 
706 ## @}
707 # python-bindings
Get lircd version, see VERSION in lircd(8) manpage.
Definition: client.py:646
Send given key, see SEND_ONCE in lircd(8) manpage.
Definition: client.py:615
List available keys in given remote, see LIST in lircd(8) manpage.
Definition: client.py:590
Simulate a button press, see SIMULATE in lircd(8) manpage.
Definition: client.py:572
result
Enum Result, reflects parser state.
Definition: client.py:434
def fileno
Return the file nr used for IO, suitable for select() etc.
Definition: client.py:184
success
bool, reflects SUCCESS/ERROR.
Definition: client.py:435
def fileno
Implements AbstractConnection.fileno().
Definition: client.py:240
sighup
bool, reflects if a SIGHUP package has been received
Definition: client.py:437
def send
Send single line over socket.
Definition: client.py:373
Public reply parser result, available when completed.
Definition: client.py:382
def readline
Implements AbstractConnection.readline().
Definition: client.py:282
def has_data
Implements AbstractConnection.has_data()
Definition: client.py:297
Identify client using the prog token, see IDENT in lircrcd(8)
Definition: client.py:683
def get_default_socket_path
Get default value for the lircd socket path, using (falling priority):
Definition: client.py:54
Command, parser and connection container with a run() method.
Definition: client.py:389
def run
Run the command and return a Reply.
Definition: client.py:401
last_line
str, last input line (for error messages).
Definition: client.py:438
def get_default_lircrc_path
Get default path to the lircrc file according to (falling priority):
Definition: client.py:83
Interface to receive code strings as described in lircd(8).
Definition: client.py:207
Interface to receive lircrc-translated keypresses.
Definition: client.py:266
def fileno
Implements AbstractConnection.fileno().
Definition: client.py:301
Extends the parent with a send() method.
Definition: client.py:367
The status/result from parsing a command reply.
Definition: client.py:415
Abstract interface for all connections.
Definition: client.py:158
def close
Implements AbstractConnection.close()
Definition: client.py:305
def readline
Implements AbstractConnection.readline().
Definition: client.py:223
data
List of lines, the command DATA payload.
Definition: client.py:436
Set transmitters to use, see SET_TRANSMITTERS in lircd(8) manpage.
Definition: client.py:631
def close
Implements AbstractConnection.close()
Definition: client.py:248
Timeout receiving data from remote host.
Definition: client.py:103
Malformed or otherwise unparsable packet received.
Definition: client.py:98
def has_data
Implements AbstractConnection.has_data()
Definition: client.py:244
def close
Close/release all resources.
Definition: client.py:194
Handles the actual parsing of a command reply.
Definition: client.py:442
Start/stop logging lircd output , see SET_INPUTLOG in lircd(8) manpage.
Definition: client.py:664
Start repeating given key, see SEND_START in lircd(8) manpage.
Definition: client.py:597
Get current translation mode, see GETMODE in lircrcd(8)
Definition: client.py:704
Stop repeating given key, see SEND_STOP in lircd(8) manpage.
Definition: client.py:606
Set a driver option value, see DRV_OPTION in lircd(8) manpage.
Definition: client.py:653
Translate a keypress to application string, see CODE in lircrcd(8)
Definition: client.py:694
List available remotes, see LIST in lircd(8) manpage.
Definition: client.py:583
def readline
Read a buffered line.
Definition: client.py:179
def has_data
Return true if next readline(None) won't block .
Definition: client.py:189
Internal FSM state.
Definition: client.py:484
Set current translation mode, see SETMODE in lircrcd(8)
Definition: client.py:711