diff --git a/doc/USB_Linux.md b/doc/USB_Linux.md new file mode 100644 index 0000000..6b19be5 --- /dev/null +++ b/doc/USB_Linux.md @@ -0,0 +1,29 @@ +# USB + +## LibUSB + +libusb - Check Linux/Windows install Guides for this + +- On Ubuntu this is likely already installed. + +## Permissions + +If you are on Linux, you may need to add a UDEV Rule to allow the Printer to be accessed by your User. + +Rule: `/etc/udev/rules.d/99-brother-ptp700.rules` + +```bash +SUBSYSTEMS=="usb", GROUP="plugdev", ACTION=="add", ATTRS{idVendor}==, ATTRS{idProduct}==, MODE="0660" +``` + +This sets the owner of the device node to root:usbusers rather than root:root + +Ensure that the User is in the `plu gdev` group : `sudo usermod -aG plugdev ` + +## Brother P-Touch + +Note: If your printer has a 'PLite' mode, you need to disable it if you want to print via USB. Make sure that the corresponding LED is not lit by holding the button down until it turns off. + +Get VendorID : `lsusb | grep -i Brother | awk '{print $6}' | cut -d':' -f1` + +Get ProductID: `lsusb | grep -i Brother | awk '{print $6}' | cut -d':' -f2` diff --git a/labelprinterkit/backends/usb.py b/labelprinterkit/backends/usb.py index 3eb2cd7..daabddf 100644 --- a/labelprinterkit/backends/usb.py +++ b/labelprinterkit/backends/usb.py @@ -1,4 +1,5 @@ from __future__ import annotations + from time import sleep import usb.core @@ -8,14 +9,22 @@ class PyUSBBackend(BaseBackend): - def __init__(self): - dev = usb.core.find(custom_match=self.is_usb_printer) + """Assumes only a SINGLE USB Printer / Brother Device is Attached""" + + def __init__(self) -> None: + self._dev = PyUSBBackend.get_device() + + @staticmethod + def get_device() -> usb.core.Device: + """Get the PyUSB Device for a USB Printer""" + dev = usb.core.find(custom_match=PyUSBBackend.is_usb_printer) if dev is None: - raise OSError('Device not found') - self._dev = dev + raise OSError("No Printer Found") + return dev @staticmethod def is_usb_printer(dev) -> bool: + """Check if the Device is a USB Printer - by Device Class or Interface Class""" if dev.bDeviceClass == 7: return True for cfg in dev: @@ -23,11 +32,44 @@ def is_usb_printer(dev) -> bool: return True return False - def write(self, data: bytes): + @staticmethod + def is_brother_manufacturer(dev) -> bool: + """Check if the Device is a Brother Printer - by Manufacturer""" + try: + manufacturer = usb.util.get_string(dev, dev.iManufacturer) + except (usb.core.USBError, ValueError): + manufacturer = None + + if manufacturer == "Brother": + return True + return False + + def detach_from_kernel(self) -> None: + """ + Detach the given Device from the Kernel - allowing PyUSB Exclusive Access + Required for: usb.core.USBError: [Errno 16] Resource busy + """ + self._dev.reset() + + interface = self._dev[0].interfaces()[0].bInterfaceNumber + if self._dev.is_kernel_driver_active(interface): + try: + self._dev.detach_kernel_driver(interface) + except usb.core.USBError: + pass + + self._dev.set_configuration() + + self.refresh() + + def refresh(self) -> None: + self._dev = PyUSBBackend.get_device() + + def write(self, data: bytes) -> None: self._dev.write(0x2, data) def read(self, count: int) -> bytes | None: - for i in range(0, 3): + for _ in range(0, 3): data = self._dev.read(0x81, count) if data: return data