From 310de6c2b721ed65555b89d2ba337a602590680a Mon Sep 17 00:00:00 2001 From: wp Date: Sat, 9 Aug 2025 14:48:52 -0500 Subject: [PATCH] add dymo400 lsusb --- 09220019.txt | 59 +++++++++++++++++++++++++++++++++ src/dymo.py | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 09220019.txt create mode 100644 src/dymo.py diff --git a/09220019.txt b/09220019.txt new file mode 100644 index 0000000..b6cf486 --- /dev/null +++ b/09220019.txt @@ -0,0 +1,59 @@ + +Bus 001 Device 013: ID 0922:0019 Dymo-CoStar Corp. LabelWriter 400 +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.10 + bDeviceClass 0 [unknown] + bDeviceSubClass 0 [unknown] + bDeviceProtocol 0 + bMaxPacketSize0 16 + idVendor 0x0922 Dymo-CoStar Corp. + idProduct 0x0019 LabelWriter 400 + bcdDevice 0.4e + iManufacturer 1 DYMO + iProduct 2 DYMO LabelWriter 400 + iSerial 3 05080914235500 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 0x0020 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 2mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 7 Printer + bInterfaceSubClass 1 Printer + bInterfaceProtocol 2 Bidirectional + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 0 +Device Status: 0x0001 + Self Powered diff --git a/src/dymo.py b/src/dymo.py new file mode 100644 index 0000000..ac6e5aa --- /dev/null +++ b/src/dymo.py @@ -0,0 +1,92 @@ +import usb.core +import usb.util +import sys +import re + +dymo_vid = 0x0922 # Dymo vendor ID +dymo_tt_pid = 0x0018 # Dymo Twin Turbo Product ID +dymo_400_pid = 0x0019 +dymo_450_pid = 0x0020 # Dymo 450 Product ID + +dymo_tt = usb.core.find(idVendor=dymo_vid, idProduct=dymo_tt_pid) +dymo_400 = usb.core.find(idVendor=dymo_vid, idProduct=dymo_400_pid) +dymo_450 = usb.core.find(idVendor=dymo_vid, idProduct=dymo_450_pid) + +#dymo_tt.serial_number # Prints device unique ID +#dymo_tt.langids # Returns language IDs (1033,) for english + +# Get descriptor for serial number +# fields: dymo_tt - reference to device to get the data from +# 254 - max buffer for received data (254 max, 30 expected for serial number) +# 0x03 - Data type - 0x03 = string data +# dymo_tt.iSerialNumber - index of serial number data (0x03) +# dymo_tt.langids[0] - First item from supported languages (1033) +#usb.control.get_descriptor(dymo_tt, 254, 0x03, dymo_tt.iSerialNumber, dymo_tt.langids[0]) +#usb.control.get_descriptor(dymo_450, 254, 0x03, dymo_450.iSerialNumber, dymo_450.langids[0]) +dymo_tt_serialdata = usb.control.get_descriptor(dymo_tt, 30, 0x03, 0x03, 1033) +dymo_450_serialdata = usb.control.get_descriptor(dymo_450, 30, 0x03, 0x03, 1033) + +#print(dymo_tt_serialdata.hex()) +#print(dymo_450_serialdata.hex()) + +# returns the string representation of the serial number received from the device +#print(dymo_tt_serialdata[2:len(dymo_tt_serialdata)].decode('utf-16-le')) +#print(dymo_450_serialdata[2:30].decode('utf-16-le')) + +# returns raw bytes representation of encoded data +#print(dymo_tt_serialdata[2:30].decode('utf-8')) +#print(dymo_450_serialdata[2:30].decode('utf-8')) + +def get_serial_raw(dev): + return usb.control.get_descriptor(dev, 254, usb.DT_STRING, dev.iSerialNumber, dev.langids[0]) + +def get_device_id(dev): + # Get the IEEE 1284 Device ID string + # ref: https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/LegacyCollaterals/01233a.pdf + GET_DEVICE_ID = 0xa1 # USBPRINTER protocol bmRequestType printer protocol request + bRequest = 0x00 # USBPRINTER protocol bRequest get_device_id + GET_PORT_STATUS = 0x01 # USBPRINTER protocol bRequest get_port_status (returns 1 byte) + SOFT_RESET = 0x21 # USBPRINTER protocol bmRequestType soft reset + wValue = 0 # USBPRINTER protocol Config Index + wIndex = 0 # USBPRINTER protocol interface index and alt setting + wLength = 1023 # Max buffer length + # ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, msg or buffsize) + return dev.ctrl_transfer(GET_DEVICE_ID, bRequest, wValue, wIndex, wLength) + +def get_serial(dev): + rawserial = get_serial_raw(dev) + # Return just the data portion of the descriptor data + # First byte is length of returned data, second byte is type of returned data (30 and 3 expected) + rawserial = rawserial[2:len(rawserial)].tobytes() + if dev.idVendor == 0x0922 and dev.idProduct == 0x0018: + # Because the Twin Turbo returns invalid serial data, we drop the first 2 bytes + # and decode the remainder as utf-16-be (big-endian) + return rawserial[2:len(rawserial)].decode('utf-16-be') + else: + return rawserial.decode('utf-16-le') + +def fix_serial(uri_line): + if not uri_line.startswith("direct usb://DYMO/LabelWriter%20Twin%20Turbo?serial="): + return uri_line + + # Extract existing serial + match = re.search(r'serial=([^\s]+)', uri_line) + if not match: + return uri_line + + serial = match.group(1) + + # If the serial is all question marks, replace it + if set(serial) == {'?'}: + # Generate fallback serial using USB bus/device number from URI line + # CUPS output looks like: + # direct usb://DYMO/... "DYMO LabelWriter Twin Turbo" usb://... + # We'll add a stable dummy fallback here + #dummy_serial = "DYMOFIX-" + str(abs(hash(uri_line)) % 10000) + new_serial = get_serial() + uri_line = uri_line.replace(f"serial={serial}", f"serial={dummy_serial}") + + return uri_line + +#for line in sys.stdin: +# sys.stdout.write(fix_serial(line))