text: #!/usr/bin/env python3
  # This alternative Reader.py script was meant to cover not only USB readers but more.
  # It can be used to replace Reader.py if you have readers such as
  # MFRC522, RDM6300 or PN532.
  # Please use the github issue threads to share bugs and improvements
  # or create pull requests.
  
  import os.path
  import sys
  
  import RPi.GPIO as GPIO
  import logging
  
  from evdev import InputDevice, categorize, ecodes, list_devices
  
  logger = logging.getLogger(__name__)
  
  
  def get_devices():
      devices = [InputDevice(fn) for fn in list_devices()]
      devices.append(NonUsbDevice('MFRC522'))
      devices.append(NonUsbDevice('RDM6300'))
      devices.append(NonUsbDevice('PN532'))
      return devices
  
  
  class NonUsbDevice(object):
      name = None
  
      def __init__(self, name):
          self.name = name
  
  
  class UsbReader(object):
      def __init__(self, device):
          self.keys = "X^1234567890XXXXqwertzuiopXXXXasdfghjklXXXXXyxcvbnmXXXXXXXXXXXXXXXXXXXXXXX"
          self.dev = device
  
      def readCard(self):
          from select import select
          stri = ''
          key = ''
          while key != 'KEY_ENTER':
              select([self.dev], [], [])
              for event in self.dev.read():
                  if event.type == 1 and event.value == 1:
                      stri += self.keys[event.code]
                      key = ecodes.KEY[event.code]
          return stri[:-1]
  
  
  class Mfrc522Reader(object):
      def __init__(self):
          import pirc522
          self.device = pirc522.RFID()
  
      def readCard(self):
          # Scan for cards
          self.device.wait_for_tag()
          (error, tag_type) = self.device.request()
  
          if not error:
              logger.info("Card detected.")
              # Perform anti-collision detection to find card uid
              (error, uid) = self.device.anticoll()
              if not error:
                  card_id = ''.join((str(x) for x in uid))
                  logger.info(card_id)
                  return card_id
          logger.debug("No Device ID found.")
          return None
  
      @staticmethod
      def cleanup():
          GPIO.cleanup()
  
  
  class Rdm6300Reader:
      def __init__(self, param=None):
          import serial
          device = '/dev/ttyS0'
          baudrate = 9600
          ser_timeout = 0.1
          self.last_card_id = ''
          try:
              self.rfid_serial = serial.Serial(device, baudrate, timeout=ser_timeout)
              self.serial_SerialException = serial.SerialException
          except serial.SerialException as e:
              logger.error(e)
              exit(1)
  
          self.number_format = ''
          if param is not None:
              nf = param.get("numberformat")
              if nf is not None:
                  self.number_format = nf
  
      def convert_to_weigand26_when_checksum_ok(self, raw_card_id):
          weigand26 = []
          xor = 0
          for i in range(0, len(raw_card_id) >> 1):
              val = int(raw_card_id[i * 2:i * 2 + 2], 16)
              if (i < 5):
                  xor = xor ^ val
                  weigand26.append(val)
              else:
                  chk = val
          if (chk == val):
              return weigand26
          else:
              return None
  
      def readCard(self):
          byte_card_id = bytearray()
  
          try:
              while True:
                  try:
                      wait_for_start_byte = True
                      while True:
                          read_byte = self.rfid_serial.read()
  
                          if (wait_for_start_byte):
                              if read_byte == b'\x02':
                                  wait_for_start_byte = False
                          else:
                              if read_byte != b'\x03':        # could get stuck here, check len? check timeout by len == 0??
                                  byte_card_id.extend(read_byte)
                              else:
                                  break
  
                      raw_card_id = byte_card_id.decode('ascii')
                      byte_card_id.clear()
                      self.rfid_serial.reset_input_buffer()
  
                      if len(raw_card_id) == 12:
                          w26 = self.convert_to_weigand26_when_checksum_ok(raw_card_id)
                          if (w26 is not None):
                              # print ("factory code is ignored" ,w26[0])
  
                              if self.number_format == 'card_id_dec':
                                  # this will return a 10 Digit card ID e.g. 0006762840
                                  card_id = '{0:010d}'.format((w26[1] << 24) + (w26[2] << 16) + (w26[3] << 8) + w26[4])
                              elif self.number_format == 'card_id_float':
                                  # this will return card ID as fraction e.g. 103,12632
                                  card_id = '{0:d},{1:05d}'.format(((w26[1] << 8) + w26[2]), ((w26[3] << 8) + w26[4]))
                              else:
                                  # this will return the raw (original) card ID e.g. 070067315809
                                  card_id = raw_card_id
  
                              if card_id != self.last_card_id:  # does this still makes sense here?
                                  self.last_card_id = card_id  # Means 2nd swipe will not be possible with RDM6300
                                  return self.last_card_id     # intentionaly? Good reason for this?
  
                  except ValueError as ve:
                      logger.error(ve)
  
          except self.serial_SerialException as se:
              logger.error(se)
  
      def cleanup(self):
          self.rfid_serial.close()
  
  
  class Pn532Reader:
      def __init__(self):
          from py532lib.i2c import Pn532_i2c
          from py532lib.mifare import Mifare
          from py532lib.mifare import MIFARE_WAIT_FOR_ENTRY
          pn532 = Pn532_i2c()
          self.device = Mifare()
          self.device.SAMconfigure()
          self.device.set_max_retries(MIFARE_WAIT_FOR_ENTRY)
  
      def readCard(self):
          return str(+int('0x' + self.device.scan_field().hex(), 0))
  
      def cleanup(self):
          # Not sure if something needs to be done here.
          logger.debug("PN532Reader clean up.")
  
  
  class Reader(object):
      def __init__(self):
          path = os.path.dirname(os.path.realpath(__file__))
          if not os.path.isfile(path + '/deviceName.txt'):
              sys.exit('Please run RegisterDevice.py first')
          else:
              with open(path + '/deviceName.txt', 'r') as f:
                  device_name = f.read()
  
              if device_name == 'MFRC522':
                  self.reader = Mfrc522Reader()
              elif device_name == 'RDM6300':
                  # The Rdm6300Reader supports 2 Additional Number Formats which can bee choosen by an optional parameter dictionary:
                  # {'numberformat':'card_id_float'} or {'numberformat':'card_id_dec'}
                  self.reader = Rdm6300Reader()
              elif device_name == 'PN532':
                  self.reader = Pn532Reader()
              else:
                  try:
                      device = [device for device in get_devices() if device.name == device_name][0]
                      self.reader = UsbReader(device)
                  except IndexError:
                      sys.exit('Could not find the device %s.\n Make sure it is connected' % device_name)
  