""" A collection of Printers. It has methods to discover printers, and provides an interface for the methods expected from printers. """ from collections.abc import Mapping, Set import usb.core import usb.util from printer import Printer, EscPosPrinter, BrotherPrinter, PrinterType 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)) if not 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 creat 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 creat a new BROTHER printer") prid = dev.idProduct vendir = dev.idVendor brother_printer = BrotherPrinter(self.app, vendor_id=vendir,device_id=prid) 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)) 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(): 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