""" A collection of Printers. It has methods to discover printers, and provides an interface for the methods expected from printers. """ from collections.abc import Set import time import usb.core import usb.util from printer import Printer, EscPosPrinter, BrotherPrinter class Printers: """ Finds and creates a set of Printer that can be used by the Workers to print. """ def __init__(self, app): """ Discover printers connected to the computer and return a Collection of Printer() """ self.app = app self.printers = self._discover_printers() def _discover_printers(self) -> Set[Printer]: """ Gets connected USB printer devices using the pyusb library. We analyse the USB devices, get the ones that match the printer class ( 7 ) and for Brother and EPSON printers, try to create a Printer object that can be used by the Worker class to execute prints. Returns a set of Printer """ self.app.logger.debug("Discovering USB Devices connected to this system") printers = set() # Find all connected USB devices devices = usb.core.find(find_all=True, custom_match=_FindClass(7)) waiting_time_for_printer = 10 while not list(devices): self.app.logger.warning(str(waiting_time_for_printer) + " Found no printers, waiting for a printer to connect to the system...") time.sleep(1) waiting_time_for_printer = waiting_time_for_printer - 1 devices = usb.core.find(find_all=True, custom_match=_FindClass(7)) if waiting_time_for_printer == 0: break if not list(devices): self.app.logger.warning( "No USB devices of class 7 ( printers ) found or pyusb could not access the bus." ) raise RuntimeError( "No USB devices of class 7 ( printers ) found or pyusb could not access the bus." ) for dev in devices: # Attempt to get the manufacturer and product strings try: manufacturer = usb.util.get_string(dev, dev.iManufacturer) except Exception: manufacturer = "Unknown" try: product = usb.util.get_string(dev, dev.iProduct) except Exception: product = "Unknown" self.app.logger.debug( "Looking at %s %s (%s:%s)", manufacturer, product, hex(dev.idVendor), hex(dev.idProduct), ) if manufacturer == "EPSON": try: # We create a new EscPosPrinter() self.app.logger.debug("Trying to create a new EPSON printer") prid = dev.idProduct vendir = dev.idVendor escpos_printer = EscPosPrinter( self.app, vendor_id=vendir, device_id=prid ) except Exception as e: raise e # If the object creation is successfull, we add it to the list of Printers printers.add(escpos_printer) self.app.logger.debug("Found a %s printer", manufacturer) # We already found the type of printer, # we don't need an extra comparaison. continue # or a Brother Printer if manufacturer == "Brother": try: # We create a new BrotherPrinter() self.app.logger.debug("Trying to create a new BROTHER printer") brother_printer = BrotherPrinter( self.app, vendor_id=dev.idVendor, device_id=dev.idProduct ) except Exception as e: self.app.logger.error( "Could not create a %s printer class with %s:%s" , product, dev.idVendor, dev.idProduct, ) raise e # If the object creation is successfull, we add it to the list of Printers printers.add(brother_printer) self.app.logger.debug("Found a %s printer" , manufacturer) self.app.logger.debug("Found %s printers" , len(printers)) if len(printers) < 1: self.app.logger.warning("Not printers found ! Please plug in a Printer and restart the program.") raise RuntimeError("No printers found") return printers def any(self) -> Printer: """ Return a dict key: UUID, value: Printer, with any connected printer. """ if len(self.printers) > 0: for i in self.printers: return i else: raise RuntimeError("No printers available") # def get_printer(self, printer_type): # """ # Return a specific printer # printer_type -- a printer type # """ # return NotImplementedError() class _FindClass: """ Use by usb.core to modify the way USB devices are found Taken from pyUSB documentation on Github """ def __init__(self, class_): self._class = class_ def __call__(self, device): # first, let's check the device if device.bDeviceClass == self._class: return True # ok, transverse all devices to find an # interface that matches our class for cfg in device: # find_descriptor: what's it? intf = usb.util.find_descriptor(cfg, bInterfaceClass=self._class) if intf is not None: return True return False