Incus - Duplicate USB Hardware IDs

When I recreated my Home Assistant VM, I ran into an issue attaching my USB devices. I have Z-Wave and Zigbee usb dongles that both have the same hardware id of 10c4:ea60. I tried selecting both when creating the VM, but received the following error:

Error: Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/middlewared/api/base/server/ws_handler/rpc.py", line 323, in process_method_call
    result = await method.call(app, params)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/api/base/server/method.py", line 40, in call
    result = await self.middleware.call_with_audit(self.name, self.serviceobj, methodobj, params, app)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/main.py", line 883, in call_with_audit
    result = await self._call(method, serviceobj, methodobj, params, app=app,
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/main.py", line 692, in _call
    return await methodobj(*prepared_call.args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/api/base/decorator.py", line 88, in wrapped
    result = await func(*args)
             ^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/plugins/virt/instance_device.py", line 45, in device_list
    if (device := await self.incus_to_device(k, v, context)) is not None:
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/plugins/virt/instance_device.py", line 122, in incus_to_device
    context['usb_choices'] = await self.middleware.call('virt.device.usb_choices')
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/main.py", line 977, in call
    return await self._call(
           ^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/main.py", line 703, in _call
    return await self.run_in_executor(prepared_call.executor, methodobj, *prepared_call.args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/main.py", line 596, in run_in_executor
    return await loop.run_in_executor(pool, functools.partial(method, *args, **kwargs))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/api/base/decorator.py", line 96, in wrapped
    result = func(*args)
             ^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/plugins/virt/device.py", line 40, in usb_choices
    'manufacturer': i.manufacturer,
                    ^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/usb/core.py", line 898, in manufacturer
    self._manufacturer = util.get_string(self, self.iManufacturer)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/usb/util.py", line 317, in get_string
    buf = get_descriptor(
          ^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/usb/control.py", line 172, in get_descriptor
    desc = dev.ctrl_transfer(
           ^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/usb/core.py", line 1082, in ctrl_transfer
    ret = self._ctx.backend.ctrl_transfer(
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/usb/backend/libusb1.py", line 893, in ctrl_transfer
    ret = _check(self.lib.libusb_control_transfer(
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/usb/backend/libusb1.py", line 604, in _check
    raise USBError(_strerror(ret), ret, _libusb_errno[ret])
usb.core.USBError: [Errno 32] Pipe error

Afterwards, the option to add USB devices was no longer present and working. I rolled back to 24.10 then upgraded again. This time, I created the VM with no USB devices then added them one at a time. After adding the first dongle, the option to add the second disappeared BUT both were passed through to the VM. The instance details page also shows only one device being passed through.

Out of curiosity, which zigbee controller and which Z-Wave controller? I have a few lying around, maybe I can try to reproduce the same error

Vendor ID and Product ID is the same.

So I assume they are identical???

Are they also on the same USB port?

How did you you identify the one to use in 24.10???

I am using Zooz ZST10 700 and Sonoff Zigbee Plus-P. They both use the same serial to USB chip, Silicon Labs CP2102. I assume this is why they have the same hardware ID.

They are on separate usb ports and it was passed through previously by port id. This is the screenshot of my VM config on 24.10.

I don’t have the exact dongles, but I was able to add two devices to my HAOS test VM. I don’t have anything to connect to them, but I was able to install the Z-Wave-JS-UI addon and ZHA (for ZigBee). Both detected the respective dongles and configured them without errors.

I’m updating to the RC now and will test it again.


EDIT

Updated to TrueNAS RC 1 - The VM and both USB devices came back up with no errors. I deleted the VM (incus delete HAOS-TEST) and created a new one. I was able to add both USB devices during creation without error.

I am guessing you won’t have the same issue as me since the hardware IDs are different.

This is before attaching.

And after attaching one, the other disappears.

But both are attached to the instance and present inside Home Assistant OS

I looked at the incus config “incus config show homeassistant” and it seems to add the usb by product id which adds both.

  usb1:
    productid: ea60
    type: usb

It looks like we have the same Sonoff Zigbee dongle, and the hardware IDs are the same, so I guess we can assume that is the correct ID for that device. Which means for whatever reason, it’s the Z-Wave dongle with the incorrect ID.

Have you tried the Z-Wave dongle in a different USB port? What if you remove both devices, refresh the TrueNAS UI, then plug in the Z-Wave dongle by itself? Does the ID change? If you plug in the Sonoff dongle second, does it share the Z-Wave dongles ID or does the Z-Wave Dongle switch to the Sonoff ID?

I don’t know what it means, but those would be things I would try.


To be clear, I mean trying those things and looking at the ID before attaching. It’s already wrong at that point, so anything beyond is not going to work.


Also, what the output of lsusb when run from you TrueNAS shell? Do your devices still share the same ID or are they correct?

The ID’s have always been the same unfortunately. I had this issue in the past with Proxmox and Unraid. I was able to get around it there and previously with TrueNAS by specifying the usb port id, instead of the usb vendor/product id.

It appears other options are available for Incus like busnum and devnum, but through the TrueNAS GUI it only allows productid.

https://linuxcontainers.org/incus/docs/main/reference/devices_usb/

EDIT

I started getting this error on the instances page:

Error: Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/middlewared/api/base/server/ws_handler/rpc.py", line 323, in process_method_call
    result = await method.call(app, params)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/api/base/server/method.py", line 40, in call
    result = await self.middleware.call_with_audit(self.name, self.serviceobj, methodobj, params, app)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/main.py", line 883, in call_with_audit
    result = await self._call(method, serviceobj, methodobj, params, app=app,
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/main.py", line 692, in _call
    return await methodobj(*prepared_call.args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/api/base/decorator.py", line 88, in wrapped
    result = await func(*args)
             ^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/plugins/virt/instance_device.py", line 45, in device_list
    if (device := await self.incus_to_device(k, v, context)) is not None:
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/plugins/virt/instance_device.py", line 122, in incus_to_device
    context['usb_choices'] = await self.middleware.call('virt.device.usb_choices')
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/main.py", line 977, in call
    return await self._call(
           ^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/main.py", line 703, in _call
    return await self.run_in_executor(prepared_call.executor, methodobj, *prepared_call.args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/main.py", line 596, in run_in_executor
    return await loop.run_in_executor(pool, functools.partial(method, *args, **kwargs))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/api/base/decorator.py", line 96, in wrapped
    result = func(*args)
             ^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/middlewared/plugins/virt/device.py", line 39, in usb_choices
    'product': i.product,
               ^^^^^^^^^
  File "/usr/lib/python3/dist-packages/usb/core.py", line 875, in product
    self._product = util.get_string(self, self.iProduct)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/usb/util.py", line 313, in get_string
    raise ValueError("The device has no langid"
ValueError: The device has no langid (permission issue, no string descriptors supported or device error)

And now the USB devices section is missing when creating a new instance

Oh man that’s rough!

I was gonna say, I followed that link you posted and tried adding a USB device by busnum - devnum and it worked. Even shows up in the TrueNAS UI.

incus config device add HAOS-VM-TEST ZigbeeDongle usb busnum=001 devnum=009

The TrueNAS UI shows the vendor/product id

But incus config show reflects the desired options

devices:
  ZigbeeDongle:
    busnum: "001"
    devnum: "009"
    type: usb

(I was just testing the ConBee II again. It still doesn’t work)

If I add both manually using busnum and devnum, I get the same error in my first post on the GUI. I think for now I will just have to add by productid. It only shows the one device in the GUI, but at least they are both available in Home Assistant.

EDIT

Well I found a workaround. I was able to use Simplicity Labs to reprogram the PID for one of my dongles. Now everything works well.

Ideally it would be good to still allow using busnum and devnum for situations where you have two usb devices with matching hardware id.

You can make a Feature request or a bug report. However, I doubt that its an easy fix.

1 Like