#!/usr/bin/python3 ''' lirc library for python (py2 or py3) (c) 2016 Jan ONDREJ (SAL) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. ''' from __future__ import absolute_import from __future__ import print_function from ctypes import * from ctypes.util import find_library import os, fcntl LIRC_RET_SUCCESS = 0 LIRC_RET_ERROR = -1 class LIRCError(Exception): pass class lirc_config(Structure): _fields_ = [ ("lirc_class", c_char_p), ("current_mode", c_char_p), ("next", POINTER(c_void_p)), ("first", POINTER(c_void_p)), ("sockfd", c_int) ] def __str__(self): return '' % ( self.lirc_class, self.current_mode ) lirc_config = POINTER(lirc_config) class lirc(): config = None def __init__(self, client_name="python_lirc", configuration=None, blocking=False, verbose=0): solib = find_library("lirc_client") if not solib: raise LIRCError("Unable to import liblirc.so!") self.so = cdll.LoadLibrary(solib) self.so.lirc_init.argtypes = [c_char_p, c_int] self.so.lirc_readconfig.argtypes = \ [c_char_p, POINTER(lirc_config), c_void_p] self.so.lirc_freeconfig.argtypes = [lirc_config] self.so.lirc_nextcode.argtypes = [POINTER(c_char_p)] self.so.lirc_code2char.argtypes = \ [lirc_config, c_char_p, POINTER(c_char_p)] self.so.lirc_getmode.argtypes = [lirc_config] self.so.lirc_getmode.restype = c_char_p self.so.lirc_setmode.argtypes = [lirc_config, c_char_p] self.so.lirc_setmode.restype = c_char_p self.so.lirc_send_one.argtypes = [c_int, c_char_p, c_char_p] self.so.lirc_simulate.argtypes = [c_int, c_char_p, c_char_p] self.so.free.restype = None # libc free() # init self.handle = self.so.lirc_init(client_name.encode(), verbose) if self.handle==LIRC_RET_ERROR: raise LIRCError("Initialization error %d!" % get_errno()) self.readconfig(configuration) self.blocking(blocking) def __del__(self): if self.config: self.so.lirc_freeconfig(self.config) if self.so: self.so.lirc_deinit() def errcheck(self, result, func, args): if result==LIRC_RET_ERROR: raise LIRCError("LIRC error in %s" % func) def blocking(self, value=False): fcntl.fcntl(self.handle, fcntl.F_SETOWN, os.getpid()) flags = fcntl.fcntl(self.handle, fcntl.F_GETFL, 0) if flags!=-1: flags = flags & ~os.O_NONBLOCK if not value: flags = flags | os.O_NONBLOCK fcntl.fcntl(self.handle, fcntl.F_SETFL, flags) def readconfig(self, path=None): self.config = lirc_config() cb = None if self.so.lirc_readconfig(path, byref(self.config), cb)!=LIRC_RET_SUCCESS: raise LIRCError("Unable to read configuration file!") return self.config def nextcode(self): code = c_char_p() if self.so.lirc_nextcode(byref(code))!=LIRC_RET_SUCCESS: raise LIRCError("Error getting next code!") value = code.value self.so.free(code) # free memory return value def code2char(self, code): ret = c_char_p() while True: err = self.so.lirc_code2char(self.config, code, byref(ret)) if err==LIRC_RET_SUCCESS and ret.value is not None: #print(ret, ret.value, code, err) yield ret.value.decode() else: break def __call__(self): code = self.nextcode() if code is None: return [] return list(self.code2char(code)) def send(self, remote, keysym): if self.so.lirc_send_one(self.handle, remote, keysym)==LIRC_RET_ERROR: raise LIRCError("Error sending keysym!") def simulate(self, remote, keysym): if self.so.lirc_simulate(self.handle, remote, keysym)==LIRC_RET_ERROR: raise LIRCError("Error simulating keysym!") # pylirc compatibility class pylirc_class(): def __init__(self): self.lirc = None def init(self, name, configuration=None, blocking=0): try: self.lirc = lirc(name, configuration, blocking!=0) except LIRCError: raise RuntimeError("Unable to initialize lirc!") def nextcode(self): if self.lirc: return self.lirc() def blocking(self, value): if self.lirc: self.lirc.blocking(value!=0) return 0 pylirc = pylirc_class() if __name__ == "__main__": import sys l = lirc(sys.argv[1], blocking=True) while True: r = l() print(r)