#!/usr/bin/env python3 # # PC remote control - Host driver # # Copyright (c) 2013 Michael Buesch # Licensed under the terms of the GNU General Public License version 2. # import sys import struct import getopt import time import select from comm import * pyatspi = None virtkey = None try: import pyatspi except ImportError: try: import virtkey except ImportError: print("Neither module 'pyatspi' nor module 'virtkey' found.") print("If you are running Debian Linux, " "please install the package 'python3-pyatspi'.") sys.exit(1) try: sys.path.extend(["/usr/local/bin", "/usr/bin"]) from mysmartusb import MySmartUsb except ImportError: print("Module 'mysmartusb' not found.") print("Will not switch the MySmartUSB device into data mode.") print("Warning: The program might not work properly.") MySmartUsb = None class KeyEmulator(object): def __init__(self): if pyatspi is not None: print("Using 'pyatspi' for key emulation") self.reg = pyatspi.registry.Registry() elif virtkey is not None: print("Using 'virtkey' for key emulation") self.vk = virtkey.virtkey() self.vk.reload() else: assert(0) def generateKeyboardEvent(self, keysym): print("Emulating keysym: 0x%04X" % keysym) if pyatspi is not None: self.reg.generateKeyboardEvent(keysym, None, pyatspi.KEY_SYM) elif virtkey is not None: self.vk.press_keysym(keysym) self.vk.release_keysym(keysym) else: assert(0) class EventMapper(object): cmd2keysym_0 = { 29 : 0xFF0D, # Center button -> Return 40 : 0xFF0D, # 5 -> Return 34 : 0xFF51, # Arrow left -> Arrow left 8 : 0xFF51, # 4 -> Arrow left 1 : 0xFF52, # Arrow up -> Arrow up 16 : 0xFF52, # 2 -> Arrow up 2 : 0xFF53, # Arrow right -> Arrow right 24 : 0xFF53, # 6 -> Arrow right 33 : 0xFF54, # Arrow down -> Arrow down 4 : 0xFF54, # 8 -> Arrow down 60 : 0xFFC2, # "TV" -> F5 } cmd2keysym_27526 = { 5 : 0xFF0D, # 5 -> Return 10 : 0xFF0D, # Recall -> Return 4 : 0xFF51, # 4 -> Arrow left 2 : 0xFF52, # 2 -> Arrow up 6 : 0xFF53, # 6 -> Arrow right 8 : 0xFF54, # 8 -> Arrow down 19 : 0xFFC2, # Mute -> F5 } cmd2keysym_53040 = { 4 : 0xFF0D, # Balance -> Return 12 : 0xFF51, # L -> Arrow left 2 : 0xFF52, # + -> Arrow up 5 : 0xFF53, # R -> Arrow right 1 : 0xFF54, # - -> Arrow down 18 : 0xFFC2, # Standby -> F5 } addr2cmdtab = { 0 : cmd2keysym_0, 27526 : cmd2keysym_27526, 53040 : cmd2keysym_53040, } def __init__(self): self.lastCmd = (None, None, None) self.emu = KeyEmulator() def handle(self, irEncoding, irFieldBit, irToggleBit, irRepeat, irAddr, irCmd): if irEncoding == PCRemote.ENC_RC5: if irFieldBit == 0: irAddr += 64 if self.lastCmd == (irToggleBit, irAddr, irCmd): return self.lastCmd = (irToggleBit, irAddr, irCmd) elif irEncoding == PCRemote.ENC_NEC: if irRepeat: return if (irCmd & 0xFF) != ((irCmd >> 8) ^ 0xFF): print("Received IR command invalid!") return irCmd &= 0xFF print("\nReceived IR code: Address=%d, Command=%d" %\ (irAddr, irCmd)) try: cmdtab = self.addr2cmdtab[irAddr] sym = cmdtab[irCmd] except KeyError: return self.emu.generateKeyboardEvent(sym) class MessageIR(SerialMessage): def __init__(self, encoding, fieldBit, toggleBit, repeatBit, addr, cmd): pass#TODO class MessageConfig(SerialMessage): def __init__(self, encoding, notifyEn): pl = [ PCRemote.MSGID_CONFIG, encoding, notifyEn ] SerialMessage.__init__(self, fc=0,#TODO rack payload=bytes(pl)) class PCRemote(SerialComm): MSGID_IR = 0 MSGID_CONFIG = 1 ENC_RC5 = 0 ENC_NEC = 1 def __init__(self, device, encoding=ENC_RC5, notifyEn=True): SerialComm.__init__(self, device, 9600, 8, serial.PARITY_NONE, 1) self.setSendDelay(0.01) self.encoding = encoding self.send(MessageConfig(self.encoding, notifyEn)) self.mapper = EventMapper() def __handleMsg_IR(self, msg): self.mapper.handle(irEncoding = msg.payload[1], irFieldBit = msg.payload[2] & 1, irToggleBit = (msg.payload[2] & 2) >> 1, irRepeat = (msg.payload[2] & 4) >> 2, irAddr = msg.payload[3] | \ (msg.payload[4] << 8), irCmd = msg.payload[5] | \ (msg.payload[6] << 8)) def work(self): msg = self.receive() id = msg.payload[0] if id == PCRemote.MSGID_IR: self.__handleMsg_IR(msg) else: print("Received unknown message: %d" % id) def usage(): print("Usage: pcremote.py [OPTIONS] [SERIAL_DEV]") print() print("Options:") print(" -e|--encoding ENC Set the IR encoding. Valid values are:") print(" RC-5 (default), NEC") print(" -n|--notify-disable Disable the notification LED") print() print("If SERIAL_DEV is not specified, /dev/ttyUSB0 is used.") def main(): opt_encoding = PCRemote.ENC_RC5 opt_notifyEn = True try: (opts, args) = getopt.getopt(sys.argv[1:], "he:n", [ "help", "encoding=", "notify-disable", ]) except getopt.GetoptError: usage() return 1 for (o, v) in opts: if o in ("-h", "--help"): usage() return 0 if o in ("-e", "--encoding"): if v.upper() in ("RC-5", "RC5"): opt_encoding = PCRemote.ENC_RC5 elif v.upper() == "NEC": opt_encoding = PCRemote.ENC_NEC else: print("Unknown encoding:", v) return 1 if o in ("-n", "--notify-disable"): opt_notifyEn = False if len(args) == 0: args.append("/dev/ttyUSB0") elif len(args) != 1: usage() return 1 dev = args[0] if MySmartUsb: msu = MySmartUsb(dev) msu.setMode(MySmartUsb.MODE_DATA) msu.resetBoard() msu.close() time.sleep(0.5) r = PCRemote(dev, opt_encoding, opt_notifyEn) while 1: try: r.work() except SerialError as e: print("ERROR: " + str(e)) except (KeyboardInterrupt, select.error) as e: r.close() break return 0 if __name__ == "__main__": sys.exit(main())