Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for LabelManager 280 #40

Closed
pschonmann opened this issue Nov 11, 2021 · 42 comments
Closed

Support for LabelManager 280 #40

pschonmann opened this issue Nov 11, 2021 · 42 comments

Comments

@pschonmann
Copy link

Hi i have Linux mint and when run program i got this error message
The device 'Dymo LabelManager PnP' could not be found on this system.

When i print test page in printer dialog, printer works.
Im using Label Manager 280

@maresb
Copy link
Collaborator

maresb commented Nov 11, 2021

Hi @pschonmann, that's because the Label Manager 280 is different from the LabelManager PnP.

It would be nice to support other models like this one, but it would probably take some work. Would you be interested in figuring out and implementing an interface for this model?

@pschonmann
Copy link
Author

Hi, thanks for fast reply.
Im interested, but have no python programming skills.

@maresb
Copy link
Collaborator

maresb commented Nov 11, 2021

Are you able to make sense of this? I think that figuring out the device IDs is probably the place to start.

Although this was almost 10 years ago, and I suspect there's an easier way now. I'd actually like to rewrite this whole project.

@Scorcerer
Copy link

Scorcerer commented Jan 24, 2022

Hi!
I just got the LabelManager 280 as well, and noticed it's not working with dymoprint :(
In dmesg it looks like this:

usb 1-1.1: new full-speed USB device number 10 using ehci-pci
usb 1-1.1: New USB device found, idVendor=0922, idProduct=1005, bcdDevice= 1.00
usb 1-1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 1-1.1: Product: DYMO LabelManager 280
usb 1-1.1: Manufacturer: DYMO
usb 1-1.1: SerialNumber: 14424821032015

And in lsusb i have:
Bus 001 Device 010: ID 0922:1005 Dymo-CoStar Corp. DYMO LabelManager 280
In verbose it's:

Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Couldn't open device, some information will be missing
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            9 Hub
  bDeviceSubClass         0 
  bDeviceProtocol         0 Full speed (or root) hub
  bMaxPacketSize0        64
  idVendor           0x1d6b Linux Foundation
  idProduct          0x0002 2.0 root hub
  bcdDevice            5.15
  iManufacturer           3 Linux 5.15.13-gentoo-x86_64 ehci_hcd
  iProduct                2 EHCI Host Controller
  iSerial                 1 0000:00:1a.0
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength       0x0019
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0xe0
      Self Powered
      Remote Wakeup
    MaxPower                0mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         9 Hub
      bInterfaceSubClass      0 
      bInterfaceProtocol      0 Full speed (or root) hub
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0004  1x 4 bytes
        bInterval              12

If you have any ideas how to make it work I'd be glad to test them, unfortuantely my skills aren't up to the task of reverse-engineering it from scratch....
Ah, and i tried changing the ProoductIDs in udev and usb_modeswitch, no dice :(

@maresb
Copy link
Collaborator

maresb commented Jan 24, 2022

Hi @Scorcerer, thanks for the detailed logs, that's very helpful. For how to proceed, please see my comments here: #30 (comment) In particular, you should focus on getting usb_modeswitch to work. (The usb_modeswitch tool sends a magical 3-byte packet 1B 5A 01 which actually enables the printer device as opposed to the storage device with the Windows drivers.)

@Scorcerer
Copy link

So, i started with:
Bus 001 Device 013: ID 0922:1005 Dymo-CoStar Corp. DYMO LabelManager 280
Then i ran:

$ usb_modeswitch --default-vendor 0922 --default-product 1005 --message-content 1B5A01
Look for default devices ...
 Found devices in default mode (1)
Access device 013 on bus 001
Get the current device configuration ...
Current configuration number is 1
Use interface number 0
 with class 7
Use endpoints 0x02 (out) and 0x81 (in)
Looking for active drivers ...
Set up interface 0
Use endpoint 0x02 for message sending ...
Trying to send message 1 to endpoint 0x02 ...
 OK, message successfully sent

Reset response endpoint 0x81
Reset message endpoint 0x02
-> Run lsusb to note any changes. Bye!

Afterwards it's still:
Bus 001 Device 013: ID 0922:1005 Dymo-CoStar Corp. DYMO LabelManager 280

I also tried:

$ usb_modeswitch --default-vendor 0922 --default-product 1005 --message-endpoint 01 --message-content 1B5A01
Look for default devices ...
 Found devices in default mode (1)
Access device 013 on bus 001
Get the current device configuration ...
Current configuration number is 1
Use interface number 0
 with class 7
Use endpoints 0x01 (out) and 0x81 (in)
Looking for active drivers ...
Set up interface 0
Use endpoint 0x01 for message sending ...
Trying to send message 1 to endpoint 0x01 ...
 Sending the message returned error -1. Try to continue

Reset response endpoint 0x81
Reset message endpoint 0x01
 Could not reset endpoint (probably harmless): -5
-> Run lsusb to note any changes. Bye!

But same thing, no change. Can it be that a different magic string is needed to make it work?

@maresb
Copy link
Collaborator

maresb commented Jan 25, 2022

@Scorcerer, excellent work! I tried your commands on my LabelManager PnP with the following results:

$ lsusb | grep 0922
Bus 001 Device 014: ID 0922:1001 Dymo-CoStar Corp. LabelManager PnP

$ usb_modeswitch --default-vendor 0922 --default-product 1001 --message-content 1B5A01
Look for default devices ...
 Found devices in default mode (1)
Access device 014 on bus 001
Error opening the device. Abort

$ sudo usb_modeswitch --default-vendor 0922 --default-product 1001 --message-content 1B5A01
Look for default devices ...
 Found devices in default mode (1)
Access device 014 on bus 001
Get the current device configuration ...
Current configuration number is 1
Use interface number 0
 with class 3
Error: message endpoint not given or found. Abort

$ sudo usb_modeswitch --default-vendor 0922 --default-product 1001 --message-endpoint 01 --message-content 1B5A01
Look for default devices ...
 Found devices in default mode (1)
Access device 014 on bus 001
Get the current device configuration ...
Current configuration number is 1
Use interface number 0
 with class 3
Error: response endpoint not given or found. Abort

$ sudo usb_modeswitch --default-vendor 0922 --default-product 1001 --message-endpoint 01 --response-endpoint 01 --message-content 1B5A01
Look for default devices ...
 Found devices in default mode (1)
Access device 014 on bus 001
Get the current device configuration ...
Current configuration number is 1
Use interface number 0
 with class 3
Use endpoints 0x01 (out) and 0x01 (in)
Looking for active drivers ...
 OK, driver detached
Set up interface 0
Use endpoint 0x01 for message sending ...
Trying to send message 1 to endpoint 0x01 ...
 OK, message successfully sent

Reset response endpoint 0x01
 Could not reset endpoint (probably harmless): -99
Reset message endpoint 0x01
 Could not reset endpoint (probably harmless): -99
-> Run lsusb to note any changes. Bye!

$ lsusb | grep 0922
Bus 001 Device 015: ID 0922:1002 Dymo-CoStar Corp. 

There are a few minor differences which may be worth looking into (like setting the response endpoint). It could certainly be that the magic string is different. I'm honestly no expert on this stuff, and have no idea.

If you have access to a Windows machine, maybe you could try to see if you could sniff the message. But then again, if the mode switching string is different, probably all the other control strings are different, so it's probably not worthwhile.

@Scorcerer
Copy link

Scorcerer commented Jan 25, 2022

I just now saw that i posted in wrong lsusb -v codeblock. This is the right one:

Bus 001 Device 015: ID 0922:1005 Dymo-CoStar Corp. DYMO LabelManager 280
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        16
  idVendor           0x0922 Dymo-CoStar Corp.
  idProduct          0x1005 
  bcdDevice            1.00
  iManufacturer           1 DYMO
  iProduct                2 DYMO LabelManager 280
  iSerial                 3 14424821032015
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength       0x0020
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0xc0
      Self Powered
    MaxPower               50mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         7 Printer
      bInterfaceSubClass      1 Printer
      bInterfaceProtocol      2 Bidirectional
      iInterface              4 Printer-Class
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 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

I'll try to install it via CUPS with the official driver and let you know about the results (seeing it's reported as printer, maybe it'll "just work"
Can you post yours for comparison, before and after modeswitch?

@maresb
Copy link
Collaborator

maresb commented Jan 25, 2022

$ lsusb | grep 0922
Bus 001 Device 016: ID 0922:1001 Dymo-CoStar Corp. LabelManager PnP
$ sudo lsusb -v -s 001:016

Bus 001 Device 016: ID 0922:1001 Dymo-CoStar Corp. LabelManager PnP
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x0922 Dymo-CoStar Corp.
  idProduct          0x1001 LabelManager PnP
  bcdDevice            1.00
  iManufacturer           1 Dymo
  iProduct                2 DYMO LabelManager PnP
  iSerial                 3 02281002072010
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength       0x0040
    bNumInterfaces          2
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              500mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 
      bInterfaceProtocol      0 
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      34
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval              10
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval              10
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         8 Mass Storage
      bInterfaceSubClass      6 SCSI
      bInterfaceProtocol     80 Bulk-Only
      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
can't get device qualifier: Resource temporarily unavailable
can't get debug descriptor: Resource temporarily unavailable
Device Status:     0x0000
  (Bus Powered)
$ sudo usb_modeswitch --default-vendor 0922 --default-product 1001 --message-endpoint 01 --response-endpoint 01 --message-content 1B5A01
Look for default devices ...
 Found devices in default mode (1)
Access device 016 on bus 001
Get the current device configuration ...
Current configuration number is 1
Use interface number 0
 with class 3
Use endpoints 0x01 (out) and 0x01 (in)
Looking for active drivers ...
 OK, driver detached
Set up interface 0
Use endpoint 0x01 for message sending ...
Trying to send message 1 to endpoint 0x01 ...
 OK, message successfully sent

Reset response endpoint 0x01
 Could not reset endpoint (probably harmless): -99
Reset message endpoint 0x01
 Could not reset endpoint (probably harmless): -99
-> Run lsusb to note any changes. Bye!
$ lsusb | grep 0922
Bus 001 Device 017: ID 0922:1002 Dymo-CoStar Corp. 
$ sudo lsusb -v -s 001:017

Bus 001 Device 017: ID 0922:1002 Dymo-CoStar Corp. 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x0922 Dymo-CoStar Corp.
  idProduct          0x1002 
  bcdDevice            1.00
  iManufacturer           1 Dymo
  iProduct                2 DYMO LabelManager PnP
  iSerial                 3 02281002072010
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength       0x0057
    bNumInterfaces          3
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              500mA
    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     0x85  EP 5 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     0x05  EP 5 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         8 Mass Storage
      bInterfaceSubClass      6 SCSI
      bInterfaceProtocol     80 Bulk-Only
      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
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        2
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 
      bInterfaceProtocol      0 
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      34
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval              10
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval              10
can't get device qualifier: Resource temporarily unavailable
can't get debug descriptor: Resource temporarily unavailable
Device Status:     0x0000
  (Bus Powered)

@maresb
Copy link
Collaborator

maresb commented Jan 25, 2022

@Scorcerer, I just took a look at your lsusb codeblock, and it does indeed appear that the device is already exposing a printer without using usb_modeswitch.

@Scorcerer
Copy link

So side-by side it looks like there are minor differences in endpoint addresses and stuff, but both devices announce themselves as printers (mine from the beginning, hours after usb_modeswitch)
Would it be possible for dymoprint to accept different addressing or stuff? Idk how it talks to the printer, but from what i see here, this should be working....

@maresb
Copy link
Collaborator

maresb commented Jan 25, 2022

@Scorcerer, how are your Python skills? Can you manage an editable install? (i.e. git clone, and then pip install -e .)

@maresb
Copy link
Collaborator

maresb commented Jan 25, 2022

You may be able to get by with simply editing this file.

@Scorcerer
Copy link

Changed like this:
https://github.com/Scorcerer/dymoprint/blob/LM280_testing/src/dymoprint/constants.py
but still:```

(.venv) scor@Hojo ~/Projects/dymoprint $ pip install -e .
Obtaining file:///home/scor/Projects/dymoprint
Installing build dependencies ... done
Checking if build backend supports build_editable ... done
Getting requirements to build wheel ... done
Preparing metadata (pyproject.toml) ... done
Requirement already satisfied: appdirs in ./.venv/lib/python3.9/site-packages (from dymoprint==1.0.1.post1.dev59+gfb42b08) (1.4.4)
Requirement already satisfied: python-barcode>=0.13.1<1 in ./.venv/lib/python3.9/site-packages (from dymoprint==1.0.1.post1.dev59+gfb42b08) (0.13.1)
Requirement already satisfied: Pillow<9,>=8.1.2 in ./.venv/lib/python3.9/site-packages (from dymoprint==1.0.1.post1.dev59+gfb42b08) (8.4.0)
Requirement already satisfied: PyQRCode<2,>=1.2.1 in ./.venv/lib/python3.9/site-packages (from dymoprint==1.0.1.post1.dev59+gfb42b08) (1.2.1)
Installing collected packages: dymoprint
Attempting uninstall: dymoprint
Found existing installation: dymoprint 1.0.1.post1.dev58+g3e26dd3.d20220125
Uninstalling dymoprint-1.0.1.post1.dev58+g3e26dd3.d20220125:
Successfully uninstalled dymoprint-1.0.1.post1.dev58+g3e26dd3.d20220125
Running setup.py develop for dymoprint
Successfully installed dymoprint-1.0.1.post1.dev59+gfb42b08
(.venv) scor@Hojo ~/Projects/dymoprint $ whereis dymoprint
dymoprint: /home/scor/Projects/dymoprint/.venv/bin/dymoprint
(.venv) scor@Hojo ~/Projects/dymoprint $ dymoprint Machine_test
The device 'Dymo LabelManager 280' could not be found on this system.

@maresb
Copy link
Collaborator

maresb commented Jan 25, 2022

Looks fundamentally good. With the editable install, you should be able to modify the source code without the need to reinstall.

This is the part of code which we need to diagnose.

You should be able to run python, or more conveniently ipython, and then run

from dymoprint.constants import (
    DEV_CLASS,
    DEV_NAME,
    DEV_NODE,
    DEV_PRODUCT,
    DEV_VENDOR,
    FONT_SIZERATIO,
    USE_QR,
    QRCode,
    e_qrcode,
)
from dymoprint.utils import getDeviceFile
dev = getDeviceFile(DEV_CLASS, DEV_VENDOR, DEV_PRODUCT)
dev

For me, the value of dev is the string /dev/hidraw8.

@maresb
Copy link
Collaborator

maresb commented Jan 25, 2022

Haha, actually it's much simpler...

First try

$ ls -al /sys/bus/hid/devices/*0922*
lrwxrwxrwx 1 root root 0 Jan 25 22:47 /sys/bus/hid/devices/0003:0922:1002.001A -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-5/1-5.3/1-5.3:1.2/0003:0922:1002.001A

That's essentially what getDeviceFile does.

@Scorcerer
Copy link

Hmm, no dice:

(.venv) scor@Hojo ~/Projects/dymoprint $ ls -al /sys/bus/hid/devices/*0922*
ls: cannot access '/sys/bus/hid/devices/*0092*': No such file or directory

Although there are some things in there, looks like printer is not HID-recognized :(

@maresb
Copy link
Collaborator

maresb commented Jan 25, 2022

There's one last thing you could try. If you go back to the original dymoprint they use Python's USB library instead of the device file.

@Scorcerer
Copy link

Hah! reading through https://sbronner.com/dymoprint.html I opted for installing it via CUPS. It freaking works, as in "it pushed out tape when i clicked 'print'"
Now the question is how to print proper labels and stuff out of it, preferably simply from commandline...

@maresb
Copy link
Collaborator

maresb commented Jan 26, 2022

Wow, it's been a long time since I've read that text, but after our experience, I finally understand what he is saying! 😂

That's awesome that it responds to CUPS!!! 🎉 No idea about proper labels, i suppose we should learn some CUPS.

Also, based on our work above, i think we could put together a very useful document about probing DYMO printers so that others can work on supporting their own devices. What do you think?

@Scorcerer
Copy link

Yup, sounds like a plan :) That means i'm gonna have to figure out how to pritn with LM280 quickly, as i was able to get Rhino 6000 cheaply (it seems it also works with D1 tapes) and it's gonna be the next project :)
I'll try to figure out how to print with CUPS, as for example for me it'd make sense to use commandline in a quick'n'dirty way to print a label, without playing extensively in some kind of label editing software.

@maresb
Copy link
Collaborator

maresb commented Jan 26, 2022

Ping @varac, since they also were interested in the 280. (See #30 (comment))

@maresb
Copy link
Collaborator

maresb commented Jan 26, 2022

Also, I don't know if it's relevant to CUPS, but this technical manual is a good guide to the commands:
https://download.dymo.com/dymo/technical-data-sheets/LW%20450%20Series%20Technical%20Reference.pdf

BTW, the modeswitch command is <esc> Z <soh>, which sort of fits in with the protocol.

@maresb maresb changed the title The device 'Dymo LabelManager PnP' could not be found on this system. LabelManager 280 Jan 27, 2022
@aleksasp
Copy link

aleksasp commented Nov 16, 2022

@Scorcerer

Hi, trying to use LabelManager 280 via CUPS. I've managed to compile and install the CUPS drivers (Ubuntu detects the LM280 printer when it is connected and powered on)
Got the updated driver source from THIS REPO
I've tried installing dymoprint from YOUR REPO but
running dymoprint hello results in The device 'Dymo LabelManager 280' could not be found on this system.

Any suggestions I've might be doing wrong?

regarding print command oneliner

Also i've set it as default printer

lpstat -p -d
lpoptions -d LabelManager-280

tryied to print text using command as shown HERE with no success. Ubuntu shows that printing in progress, the finished but LM280 did nothing.

@maresb
Copy link
Collaborator

maresb commented Nov 17, 2022

@aleksasp, I think the problem is that the printer is not showing up as a HID device as the current repo expects. If you run

ls -al /sys/bus/hid/devices/*0922*

then I think you will get "No such file or directory". Could you confirm this?

In this case, you may instead have success with the original dymoprint script by Sebastian Bronner. That uses USB directly, instead of the HID virtual file. So I think you just need to add 0x1005 to _USB_PRODUCT and make sure all the required packages are installed.

@aleksasp
Copy link

aleksasp commented Nov 18, 2022

@maresb you were right, just had also to change the device class to 0x7 instead of 0x3 and change path to an existing ttf font.

Below is the output of

print(dev.get_active_configuration())

that suggested changing device class to 0x7.

CONFIGURATION 1: 50 mA ===================================
   bLength              :    0x9 (9 bytes)
   bDescriptorType      :    0x2 Configuration
   wTotalLength         :   0x20 (32 bytes)
   bNumInterfaces       :    0x1
   bConfigurationValue  :    0x1
   iConfiguration       :    0x0 
   bmAttributes         :   0xc0 Self Powered
   bMaxPower            :   0x19 (50 mA)
    INTERFACE 0: Printer ===================================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x0
     bAlternateSetting  :    0x0
     bNumEndpoints      :    0x2
     bInterfaceClass    :    0x7 Printer
     bInterfaceSubClass :    0x1
     bInterfaceProtocol :    0x2
     iInterface         :    0x4 Printer-Class
      ENDPOINT 0x81: Bulk IN ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x0
      ENDPOINT 0x2: Bulk OUT ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x2 OUT
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x0

Added public GIST form my dymoprint version.

@maresb
Copy link
Collaborator

maresb commented Nov 18, 2022

@aleksasp, does it print? 👀

@aleksasp
Copy link

aleksasp commented Nov 18, 2022

@maresb yes it does print. Sorry for not making it clear in the reply.

@maresb
Copy link
Collaborator

maresb commented Nov 18, 2022

@aleksasp, that's excellent!!!

Do you need to run with sudo in order to print?

Also, do you know to what degree CUPS is necessary? My understanding is that dymoprint replaces CUPS in all cases.

I see what happened now with why this repo uses the HID virtual file while the original dymoprint uses PyUSB. The latest version of Sebastian Bronner's dymoprint was from 2016. However, the version on which this repo was based is from 2013. So unfortunately these improvements never got merged in our fork.

@aleksas
Copy link
Contributor

aleksas commented Nov 19, 2022

Printing without sudo. It is possibly likely due to appropriate rules file. Will asd that to the gist. Will try to remove CUPS or run the script on different host next week.

@maresb
Copy link
Collaborator

maresb commented Nov 19, 2022

We should consider merging some of these changes, especially the use of PyUSB.

Changes added to the newer version of dymoprint
diff --git a/a b/b
index 1491c03..35f5c02 100644
--- a/a
+++ b/b
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # === LICENSE STATEMENT ===
 # Copyright (c) 2011 Sebastian J. Bronner <waschtl@sbronner.com>
@@ -8,28 +8,14 @@
 # this notice are preserved.
 # === END LICENSE STATEMENT ===
 
-# On systems with access to sysfs under /sys, this script will use the three
-# variables DEV_CLASS, DEV_VENDOR, and DEV_PRODUCT to find the device file
-# under /dev automatically. This behavior can be overridden by setting the
-# variable DEV_NODE to the device file path. This is intended for cases, where
-# either sysfs is unavailable or unusable by this script for some reason.
-# Please beware that DEV_NODE must be set to None when not used, else you will
-# be bitten by the NameError exception.
-
-
-DEV_CLASS      = 3
-DEV_VENDOR     = 0x0922
-DEV_PRODUCT    = 0x1002
-DEV_NODE       = None
-DEV_NAME       = 'Dymo LabelManager PnP'
-FONT_FILENAME  = '/usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf'
-FONT_SIZERATIO = 7./8
-VERSION        = "0.1.0 (2013-07-09)"
+# The following module libraries are not included with python and need to be
+# installed separately:
+# * Pillow (python-pillow or python3-pillow)
+# * PyUSB  (python-pyusb, python3-pyusb, python-usb, or python3-usb)
 
 
-import Image
-import ImageDraw
-import ImageFont
+from PIL import Image, ImageDraw, ImageFont
+import argparse
 import array
 import fcntl
 import os
@@ -39,6 +25,22 @@ import subprocess
 import sys
 import termios
 import textwrap
+import usb
+
+
+#FONT_FILENAME = '/usr/share/fonts/TTF/DejaVuSans.ttf'
+#FONT_FILENAME = '/usr/share/fonts/TTF/DejaVuSans-Bold.ttf'
+#FONT_FILENAME = '/usr/share/fonts/TTF/DejaVuSans-Oblique.ttf'
+#FONT_FILENAME = '/usr/share/fonts/TTF/DejaVuSans-BoldOblique.ttf'
+#FONT_FILENAME = '/usr/share/fonts/TTF/DejaVuSans-ExtraLight.ttf'
+FONT_FILENAME = '/usr/share/fonts/TTF/DejaVuSansCondensed.ttf'
+#FONT_FILENAME = '/usr/share/fonts/TTF/DejaVuSansCondensed-Bold.ttf'
+#FONT_FILENAME = '/usr/share/fonts/TTF/DejaVuSansCondensed-Oblique.ttf'
+#FONT_FILENAME = '/usr/share/fonts/TTF/DejaVuSansCondensed-BoldOblique.ttf'
+#FONT_FILENAME = '/usr/share/fonts/TTF/ezra/SILEOT.ttf'
+#FONT_FILENAME = '/usr/share/fonts/TTF/gentium/Gentium-R.ttf'  # IPA
+FONT_SIZERATIO = 7./8
+VERSION = "0.2.0 (2016-11-07)"
 
 
 class DymoLabeler:
@@ -51,242 +53,247 @@ class DymoLabeler:
     high-level functions for help. Each function is marked in its docstring
     with 'HLF' or 'MLF' in parentheses.
     """
-
     _ESC = 0x1b
     _SYN = 0x16
     _MAX_BYTES_PER_LINE = 8  # 64 pixels on a 12mm tape
-
-    def __init__(self, dev):
+    _USB_VENDOR = 0x0922
+    _USB_PRODUCT = (0x1001, 0x1002)
+    # Number of commands to send before waiting for a response. This helps
+    # to avoid timeouts due to differences between data transfer and
+    # printer speeds. I added this because I kept getting "IOError: [Errno
+    # 110] Connection timed out" with long labels. Using dev.default_timeout
+    # (1000) and the transfer speeds available in the descriptors somewhere, a
+    # sensible timeout can also be calculated dynamically.
+    _SYNWAIT = 64
+
+    def __init__(self):
         """Initialize the LabelManager object. (HLF)"""
-
         self.cmd = []
         self.response = False
-        self.bytesPerLine_ = None
-        self.dotTab_ = 0
-        self.dev = open(dev, 'r+')
-
-    def sendCommand(self):
+        self.bytes_per_line_ = None
+        self.dot_tab_ = 0
+
+        # Find and prepare device communication endpoints.
+        dev = usb.core.find(custom_match = lambda d: (d.idVendor ==
+            self._USB_VENDOR and d.idProduct in self._USB_PRODUCT))
+        if dev is None:
+            raise RuntimeError("No USB device matching the following criteria "
+                "was found:\n  idVendor: 0x%04x\n  idProduct: %s" %
+                (self._USB_VENDOR, ', '.join('0x%04x' % id_ for id_ in
+                self._USB_PRODUCT)))
+        try:
+            dev.set_configuration()
+        except usb.core.USBError as e:
+            if e.errno == 13:
+                # Handle error number 13 (Access denied) by printing
+                # instructions for gaining access.
+                lines = []
+                lines.append("You do not have sufficient access to the "
+                    "device. You probably want to add some udev rules in "
+                    "/etc/udev/rules.d/dymoprint.rules along the following "
+                    "lines:")
+                lines.append("")
+                for id_ in self._USB_PRODUCT:
+                    lines.append('ACTION=="add", SUBSYSTEMS=="usb", '
+                        'ATTRS{idVendor}=="%04X", ATTRS{idProduct}=="%04X", '
+                        'MODE="0660", GROUP="users"' % (self._USB_VENDOR, id_))
+                lines.append("")
+                lines.append("Following that, turn your device off and back "
+                    "on again to activate the new permissions.")
+                raise RuntimeError('\n'.join(lines))
+            if e.errno == 16:
+                # Handle error number 16 (Resource busy) by ignoring it. It
+                # just means that the configuration has already been set.
+                pass
+            else:
+                # On all other errors, the original exception is simple
+                # re-raised.
+                raise
+        intf = usb.util.find_descriptor(dev.get_active_configuration(),
+            bInterfaceClass=0x3)
+        if dev.is_kernel_driver_active(intf.bInterfaceNumber):
+            dev.detach_kernel_driver(intf.bInterfaceNumber)
+        self.data = usb.util.find_descriptor(intf, custom_match = (lambda e:
+            usb.util.endpoint_direction(e.bEndpointAddress) ==
+            usb.util.ENDPOINT_OUT))
+        self.status = usb.util.find_descriptor(intf, custom_match = (lambda e:
+            usb.util.endpoint_direction(e.bEndpointAddress) ==
+            usb.util.ENDPOINT_IN))
+
+    def send_command(self):
         """Send the already built command to the LabelManager. (MLF)"""
-
         if len(self.cmd) == 0:
             return
-        cmdBin = array.array('B', self.cmd)
-        cmdBin.tofile(self.dev)
+        while len(self.cmd) > 0:
+            synCount = 0
+            pos = -1
+            while synCount < self._SYNWAIT:
+                try:
+                    pos += self.cmd[pos+1:].index(self._SYN) + 1
+                except ValueError:
+                    pos = len(self.cmd)
+                    break
+                synCount += 1
+            cmdBin = array.array('B', [self._ESC, ord('A')])
+            cmdBin.tofile(self.data)
+            rspBin = self.status.read(8)
+            rsp = array.array('B', rspBin).tolist()
+            print(rsp, pos, len(self.cmd))
+            cmdBin = array.array('B', self.cmd[:pos])
+            cmdBin.tofile(self.data)
+            self.cmd = self.cmd[pos:]
         self.cmd = []
         if not self.response:
             return
         self.response = False
-        responseBin = self.dev.read(8)
+        responseBin = self.status.read(8)
         response = array.array('B', responseBin).tolist()
         return response
 
-    def resetCommand(self):
+    def reset_command(self):
         """Remove a partially built command. (MLF)"""
-
         self.cmd = []
         self.response = False
 
-    def buildCommand(self, cmd):
+    def build_command(self, cmd):
         """Add the next instruction to the command. (MLF)"""
-
         self.cmd += cmd
 
-    def statusRequest(self):
+    def status_request(self):
         """Set instruction to get the device's status. (MLF)"""
-
         cmd = [self._ESC, ord('A')]
-        self.buildCommand(cmd)
+        self.build_command(cmd)
         self.response = True
 
-    def dotTab(self, value):
+    def dot_tab(self, value):
         """Set the bias text height, in bytes. (MLF)"""
-
         if value < 0 or value > self._MAX_BYTES_PER_LINE:
             raise ValueError
         cmd = [self._ESC, ord('B'), value]
-        self.buildCommand(cmd)
-        self.dotTab_ = value
-        self.bytesPerLine_ = None
+        self.build_command(cmd)
+        self.dot_tab_ = value
+        self.bytes_per_line_ = None
 
-    def tapeColor(self, value):
+    def tape_color(self, value):
         """Set the tape color. (MLF)"""
-
         if value < 0: raise ValueError
         cmd = [self._ESC, ord('C'), value]
-        self.buildCommand(cmd)
+        self.build_command(cmd)
 
-    def bytesPerLine(self, value):
+    def bytes_per_line(self, value):
         """Set the number of bytes sent in the following lines. (MLF)"""
-
-        if value < 0 or value + self.dotTab_ > self._MAX_BYTES_PER_LINE:
+        if value < 0 or value + self.dot_tab_ > self._MAX_BYTES_PER_LINE:
             raise ValueError
-        if value == self.bytesPerLine_:
+        if value == self.bytes_per_line_:
             return
         cmd = [self._ESC, ord('D'), value]
-        self.buildCommand(cmd)
-        self.bytesPerLine_ = value
+        self.build_command(cmd)
+        self.bytes_per_line_ = value
 
     def cut(self):
         """Set instruction to trigger cutting of the tape. (MLF)"""
-
         cmd = [self._ESC, ord('E')]
-        self.buildCommand(cmd)
+        self.build_command(cmd)
 
     def line(self, value):
         """Set next printed line. (MLF)"""
-
-        self.bytesPerLine(len(value))
+        self.bytes_per_line(len(value))
         cmd = [self._SYN] + value
-        self.buildCommand(cmd)
+        self.build_command(cmd)
 
-    def chainMark(self):
+    def chain_mark(self):
         """Set Chain Mark. (MLF)"""
-
-        self.dotTab(0)
-        self.bytesPerLine(self._MAX_BYTES_PER_LINE)
+        self.dot_tab(0)
+        self.bytes_per_line(self._MAX_BYTES_PER_LINE)
         self.line([0x99] * self._MAX_BYTES_PER_LINE)
 
-    def skipLines(self, value):
+    def skip_lines(self, value):
         """Set number of lines of white to print. (MLF)"""
-
         if value <= 0:
             raise ValueError
-        self.bytesPerLine(0)
+        self.bytes_per_line(0)
         cmd = [self._SYN] * value
-        self.buildCommand(cmd)
+        self.build_command(cmd)
 
-    def initLabel(self):
+    def init_label(self):
         """Set the label initialization sequence. (MLF)"""
-
         cmd = [0x00] * 8
-        self.buildCommand(cmd)
+        self.build_command(cmd)
 
-    def getStatus(self):
+    def get_status(self):
         """Ask for and return the device's status. (HLF)"""
+        self.status_request()
+        response = self.send_command()
+        print(response)
 
-        self.statusRequest()
-        response = self.sendCommand()
-        print response
-
-    def printLabel(self, lines, dotTab):
+    def print_label(self, lines, dot_tab):
         """Print the label described by lines. (HLF)"""
-
-        self.initLabel
-        self.tapeColor(0)
-        self.dotTab(dotTab)
+        self.init_label
+        self.tape_color(0)
+        self.dot_tab(dot_tab)
         for line in lines:
             self.line(line)
-        self.skipLines(56)  # advance printed matter past cutter
-        self.skipLines(56)  # add symmetric margin
-        self.statusRequest()
-        response = self.sendCommand()
-        print response
+        self.skip_lines(56)  # advance printed matter past cutter
+        self.skip_lines(56)  # add symmetric margin
+        self.status_request()
+        response = self.send_command()
+        print(response)
 
 
 def die(message=None):
-    if message: print >> sys.stderr, message
+    if message: print(message, file=sys.stderr)
     sys.exit(1)
 
 
 def pprint(par, fd=sys.stdout):
     rows, columns = struct.unpack('HH', fcntl.ioctl(sys.stderr,
         termios.TIOCGWINSZ, struct.pack('HH', 0, 0)))
-    print >> fd, textwrap.fill(par, columns)
-
-
-def getDeviceFile(classID, vendorID, productID):
-    # find file containing the device's major and minor numbers
-    searchdir = '/sys/bus/hid/devices'
-    pattern = '^%04d:%04X:%04X.[0-9A-F]{4}$' % (classID, vendorID, productID)
-    deviceCandidates = os.listdir(searchdir)
-    foundpath = None
-    for devname in deviceCandidates:
-        if re.match(pattern, devname):
-            foundpath = os.path.join(searchdir, devname)
-            break
-    if not foundpath:
-        return
-    searchdir = os.path.join(foundpath, 'hidraw')
-    devname = os.listdir(searchdir)[0]
-    foundpath = os.path.join(searchdir, devname)
-    filepath = os.path.join(foundpath, 'dev')
-
-    # get the major and minor numbers
-    f = open(filepath, 'r')
-    devnums = [int(n) for n in f.readline().strip().split(':')]
-    f.close()
-    devnum = os.makedev(devnums[0], devnums[1])
-
-    # check if a symlink with the major and minor numbers is available
-    filepath = '/dev/char/%d:%d' % (devnums[0], devnums[1])
-    if os.path.exists(filepath):
-        return os.path.realpath(filepath)
-
-    # check if the relevant sysfs path component matches a file name in
-    # /dev, that has the proper major and minor numbers
-    filepath = os.path.join('/dev', devname)
-    if os.stat(filepath).st_rdev == devnum:
-        return filepath
-
-    # search for a device file with the proper major and minor numbers
-    for dirpath, dirnames, filenames in os.walk('/dev'):
-        for filename in filenames:
-            filepath = os.path.join(dirpath, filename)
-            if os.stat(filepath).st_rdev == devnum:
-                return filepath
-
-
-def access_error(dev):
-    pprint('You do not have sufficient access to the device file %s:' % dev,
-        sys.stderr)
-    subprocess.call(['ls', '-l', dev], stdout=sys.stderr)
-    print >> sys.stderr
-    pprint('You probably want to add a rule in /etc/udev/rules.d along the '
-        'following lines:', sys.stderr)
-    print >> sys.stderr, 'SUBSYSTEM=="hidraw", ACTION=="add", ATTRS{idVendor}=="%04X", ATTRS{idProduct}=="%04X", GROUP="plugdev"' % (DEV_VENDOR, DEV_PRODUCT)
-    print >> sys.stderr
-    pprint('Following that, turn off your device and back on again to '
-        'activate the new permissions.', sys.stderr)
+    print(textwrap.fill(par, columns), file=fd)
 
 
 def main():
-    # get device file name
-    if not DEV_NODE:
-        dev = getDeviceFile(DEV_CLASS, DEV_VENDOR, DEV_PRODUCT)
-    else:
-        dev = DEV_NODE
-    if not dev:
-        die("The device '%s' could not be found on this system." % DEV_NAME)
-    
+    # set up argument parsing with usage and help output
+    description = ("This script will print labels on a Dymo LabelManager PnP "
+        "connected to your computer via USB.")
+    parser = argparse.ArgumentParser(
+        description=description)
+    parser.add_argument(
+        '-v', '--version',
+        action='version',
+        version='%(prog)s ' + VERSION
+        )
+    parser.add_argument(
+        'lines',
+        metavar='line',
+        nargs='+',
+        help=("A single line will be printed at the maximum available size on "
+            "the label. If multiple lines are specified, they will be reduced "
+            "in size to fit the vertical space and will be placed in a "
+            "column."))
+    args = parser.parse_args()
+
     # create dymo labeler object
-    try:
-        lm = DymoLabeler(dev)
-    except IOError:
-        die(access_error(dev))
-    
-    # check for any text specified on the command line
-    labeltext = [arg.decode(sys.stdin.encoding) for arg in sys.argv[1:]]
-    if len(labeltext) == 0: die("No label text was specified.")
+    lm = DymoLabeler()
     
     # create an empty label image
     labelheight = lm._MAX_BYTES_PER_LINE * 8
-    lineheight = float(labelheight) / len(labeltext)
+    lineheight = float(labelheight) / len(args.lines)
     fontsize = int(round(lineheight * FONT_SIZERATIO))
     font = ImageFont.truetype(FONT_FILENAME, fontsize)
-    labelwidth = max(font.getsize(line)[0] for line in labeltext)
+    labelwidth = max(font.getsize(line)[0] for line in args.lines)
     labelbitmap = Image.new('1', (labelwidth, labelheight))
     
     # write the text into the empty image
     labeldraw = ImageDraw.Draw(labelbitmap)
-    for i, line in enumerate(labeltext):
+    for i, line in enumerate(args.lines):
         lineposition = int(round(i * lineheight))
         labeldraw.text((0, lineposition), line, font=font, fill=255)
     del labeldraw
     
     # convert the image to the proper matrix for the dymo labeler object
     labelrotated = labelbitmap.transpose(Image.ROTATE_270)
-    labelstream = labelrotated.tostring()
-    labelstreamrowlength = labelheight/8 + (1 if labelheight%8 != 0 else 0)
+    labelstream = labelrotated.tobytes()
+    labelstreamrowlength = labelheight//8 + (1 if labelheight%8 != 0 else 0)
     if len(labelstream)/labelstreamrowlength != labelwidth:
         die('An internal problem was encountered while processing the label '
             'bitmap!')
@@ -305,7 +312,7 @@ def main():
             del line[-1]
     
     # print the label
-    lm.printLabel(labelmatrix, dottab)
+    lm.print_label(labelmatrix, dottab)
 
 
 if __name__ == '__main__':
@@ -319,3 +326,10 @@ if __name__ == '__main__':
 # * allow font size specification with command line option (points, pixels?)
 # * provide an option to show a preview of what the label will look like
 # * read and write a .dymoprint file containing user preferences
+# * implement errors using exceptions
+# * implement regular output using standard methods
+# * get rid of die()
+# * get rid of pprint()
+# * get rid of access_error()
+# * provide a version that includes its dependent libraries, and a version that
+#   uses system libraries

I wonder if we still need usbmodeswitch? It's a bit of a hassle to configure it, so it would be great to do without it (#41).

@aleksasp
Copy link

aleksasp commented Nov 21, 2022

@maresb Tried using modified original dymoprint script on a different Ubuntu host without CUPS driver - works like a charm. Adding usb device to "plugdev" group is sufficient to use it in rootless mode.

@maresb
Copy link
Collaborator

maresb commented Nov 21, 2022

Excellent!!!

Any chance you could make a PR for this repo? Then we could officially support the 280. 🎉

I wonder if this would make it feasible to run this on other operating systems as well...

@aleksas
Copy link
Contributor

aleksas commented Nov 21, 2022

#48 made some rough refactoring to use either HID or PyUSB.

@MatthiasLohr
Copy link

Just to let you know: Also haveing a LabelManager 280 and it works perfectly fine! Regarding the privileges: The device was detected as /dev/usb/lp1 with owner root and group lp. Since I'm member of that lp group on my PC it directly worked out of the box.

@srl295
Copy link

srl295 commented Nov 27, 2023

Just to let you know: Also haveing a LabelManager 280 and it works perfectly fine! Regarding the privileges: The device was detected as /dev/usb/lp1 with owner root and group lp. Since I'm member of that lp group on my PC it directly worked out of the box.

"it" means this dymoprint repo as is? or one of the patches mentioned above? thanks!

@maresb
Copy link
Collaborator

maresb commented Nov 27, 2023

@srl295, there have been some substantial changes to the code since that post. The version at that time was 1.3.0. If you're having trouble you can add more details and/or try downgrading with

pipx install --force dymoprint==1.3.0

@srl295
Copy link

srl295 commented Nov 27, 2023

@maresb so would you expect it to work out of the box?

@maresb
Copy link
Collaborator

maresb commented Nov 27, 2023

My expectation would be that it runs for the first time with a permissions error, but the error message should suggest the command to run to fix the permissions error, and then it should work the second time.

@maresb
Copy link
Collaborator

maresb commented Nov 27, 2023

It'd be helpful to know the output you're seeing.

@srl295
Copy link

srl295 commented Nov 27, 2023

@maresb worked perfectly

You do not have sufficient access to the device. You probably want to add the a udev rule in /etc/udev/rules.d with the following command:

  echo 'ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="0922", ATTRS{idProduct}=="1005", MODE="0666"' | sudo tee /etc/udev/rules.d/91-dymo-1005.rules

Next refresh udev with:

  sudo udevadm control --reload-rules
  sudo udevadm trigger --attr-match=idVendor="0922"

Finally, turn your device off and back on again to activate the new permissions.

If this still does not resolve the problem, you might need to reboot. In case rebooting is necessary, please report this at <https://github.com/computerlyrik/dymoprint/pull/56>. We are still trying to figure out a simple procedure which works for everyone. In case you still cannot connect, or if you have any information or ideas, please post them at that link.

i'd recommend closing this as fixed.

@maresb
Copy link
Collaborator

maresb commented Nov 27, 2023

Thanks @srl295!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants