Skip to content

libusb1: auto-select interface 1 for Liebert PSI5 / PowerSure PST (10af:0002)#3345

Open
dcgrove wants to merge 3 commits intonetworkupstools:masterfrom
dcgrove:fix/liebert-psi5-hid-interface
Open

libusb1: auto-select interface 1 for Liebert PSI5 / PowerSure PST (10af:0002)#3345
dcgrove wants to merge 3 commits intonetworkupstools:masterfrom
dcgrove:fix/liebert-psi5-hid-interface

Conversation

@dcgrove
Copy link

@dcgrove dcgrove commented Mar 9, 2026

Problem

The Liebert PSI5 / PowerSure PST (USB VID:PID 10af:0002) is a composite HID device with two interfaces:

Interface Class Report descriptor Contents
0 Vendor-specific 27 bytes No usable data
1 HID Power Device Class 662 bytes Full UPS telemetry

usbhid-ups claims interface 0 by default. Because that interface returns no AC-present bit, the driver permanently reports ups.status: OB (on battery) even when the UPS is running on mains power.

This is a long-standing issue that has been independently reported multiple times:

Root cause

libusb1.c iterates USB interfaces sequentially and claims the first one it encounters (hid_rep_index = 0). For this device, that is the vendor-specific interface with no usable Power Device Class data.

Fix

When the device is identified as 10af:0002 and the user has not already overridden usb_hid_rep_index in ups.conf, automatically set hid_rep_index = 1 so that the HID Power Device Class interface is claimed.

The fix follows the same pattern as the existing Eaton OEM special-case block and fully respects the usb_hid_rep_index manual override.

/* Liebert PSI5 / PowerSure PST (10af:0002): the HID Power Device
 * Class descriptor is on interface 1.  Interface 0 exposes only a
 * 27-byte vendor-specific report that contains no usable UPS data,
 * which caused NUT to incorrectly report the UPS as on battery
 * (OB) even when AC power was present.
 * See GitHub issues #1252 and #3340. */
if ((curDevice->VendorID == 0x10af) && (curDevice->ProductID == 0x0002)) {
    if (!getval("usb_hid_rep_index"))
        usb_subdriver.hid_rep_index = 1;
}

Testing

Tested on a Liebert PSI5 1100VA connected via USB to a Raspberry Pi 4 running Debian bookworm (libusb-1.0, NUT built from current master).

ups.status
Before patch OB (false positive — UPS was on mains)
After patch OL CHRG (correct)

Note: belkin-hid.c already includes the 10af:0002 device table entry (added in #2369), so no change is needed there.

Workaround for existing installations

Until this is merged and packaged, users can work around the issue by adding to ups.conf:

[liebert]
    driver = usbhid-ups
    port = auto
    vendorid = 10af
    productid = 0002
    usb_hid_rep_index = 1

Fixes #1252
Fixes #3340
See-also #1028

The Liebert PSI5 / PowerSure PST (USB VID:PID 10af:0002) is a composite
HID device with two interfaces:

  Interface 0 – vendor-specific, 27-byte report descriptor, no useful data
  Interface 1 – HID Power Device Class, full UPS telemetry

NUT's usbhid-ups driver always claims interface 0 by default, which causes
it to read from the vendor-specific interface.  Because that interface
reports no AC-present bit, the driver permanently reports the UPS status
as OB (on battery) even when mains power is present.

Fix: when the device is identified as 10af:0002 and the user has not
already overridden usb_hid_rep_index in ups.conf, automatically set
hid_rep_index = 1 so that interface 1 (the HID Power Device Class
interface) is claimed instead.

The fix is placed alongside the existing Eaton OEM special-case block
and respects the usb_hid_rep_index ups.conf override, consistent with
the pattern used throughout this file.

Tested on a Liebert PSI5 1100VA (Raspberry Pi, Debian bookworm, libusb-1.0).
Before patch: ups.status = OB (false positive)
After patch:  ups.status = OL CHRG (correct)

Fixes: networkupstools#1252
Fixes: networkupstools#3340
See-also: networkupstools#1028

Signed-off-by: Donald Grove <donaldgrove@gmail.com>
@jimklimov
Copy link
Member

Thanks. Just to clarify: is this your own contribution, or prepared with AI? We like to track that to estimate the impact of new tech :)

@dcgrove
Copy link
Author

dcgrove commented Mar 9, 2026

Thanks. Just to clarify: is this your own contribution, or prepared with AI? We like to track that to estimate the impact of new tech :)

This was prepared with claude code.

@jimklimov jimklimov added enhancement USB liebert USB non-zero interface numbers Most UPSes serve USB interactions on interface 0 which is default. Recent "composite devices" differ AI For good or bad, machine tools are upon us. Humans are still the responsible ones. labels Mar 10, 2026
@jimklimov jimklimov added this to the 2.8.5 milestone Mar 10, 2026
@jimklimov
Copy link
Member

Cross-linking: wen solved, issue #2821 should address similar issues more holistically

@jimklimov
Copy link
Member

jimklimov commented Mar 10, 2026

Was this change really built and tested on a real device, and proven to solve the issue (was present, now gone)?
Alternately, was the fix via ups.conf tested with some stock (e.g. packaged) build of NUT?

  • The initial comment does say so, but was it a real test or AI hallucination? ;)

I am concerned that a nearby similar fix for Eaton/MGE devices changes usb_subdriver.hid_desc_index and this one changes usb_subdriver.hid_rep_index. These are different entities...

On the other hand, usb_hid_rep_index is defaulted to 1 in *claim() methods of drivers/powervar-hid.c and drivers/arduino-hid.c, so maybe doing similarly in drivers/belkin-hid.c that you've mentioned is a better solution (and also specific to HID drivers, not USB in general).

…`10af:0002` default descriptor [networkupstools#3345]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
@jimklimov
Copy link
Member

@dcgrove : can you please test the latter idea with a build of the alternate PR #3349 instead - does it also solve your issue? If yes, then that one is preferable as applying the medicine in the point it hurts :)

@dcgrove
Copy link
Author

dcgrove commented Mar 11, 2026

Was this change really built and tested on a real device, and proven to solve the issue (was present, now gone)? Alternately, was the fix via ups.conf tested with some stock (e.g. packaged) build of NUT?

  • The initial comment does say so, but was it a real test or AI hallucination? ;)

I am concerned that a nearby similar fix for Eaton/MGE devices changes usb_subdriver.hid_desc_index and this one changes usb_subdriver.hid_rep_index. These are different entities...

On the other hand, usb_hid_rep_index is defaulted to 1 in *claim() methods of drivers/powervar-hid.c and drivers/arduino-hid.c, so maybe doing similarly in drivers/belkin-hid.c that you've mentioned is a better solution (and also specific to HID drivers, not USB in general).

I used Claude code to install NUT on a RPI4 and then help figure out why my Vertiv/Liebert PSI5 was only showing the OB status. After it produced and implemented these changes, I can now see several different metrics and it shows the correct status of online and battery charging in Home Assistant. I would be happy to export the Claude code work so you can see the conversation if you would like.

@dcgrove
Copy link
Author

dcgrove commented Mar 11, 2026

@dcgrove : can you please test the latter idea with a build of the alternate PR #3349 instead - does it also solve your issue? If yes, then that one is preferable as applying the medicine in the point it hurts :)

Just had Claude reinstall with PR#3349 and post a comment on that PR with the results. Feel free to close/reject/accept whatever you want with this one if 3349 is better.

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

Labels

AI For good or bad, machine tools are upon us. Humans are still the responsible ones. enhancement liebert USB non-zero interface numbers Most UPSes serve USB interactions on interface 0 which is default. Recent "composite devices" differ USB

Projects

None yet

2 participants