Skip to content

fix(gui): resolve assets for HID++ 1.0 Unifying devices via WPID fallback - #751

Open
luisfacarmo wants to merge 4 commits into
AprilNEA:masterfrom
luisfacarmo:fix/resolve-assets-for-hidpp1-devices
Open

fix(gui): resolve assets for HID++ 1.0 Unifying devices via WPID fallback#751
luisfacarmo wants to merge 4 commits into
AprilNEA:masterfrom
luisfacarmo:fix/resolve-assets-for-hidpp1-devices

Conversation

@luisfacarmo

Copy link
Copy Markdown

Summary

HID++ 1.0 Unifying devices (many keyboards like K540/K545, K375s, etc.) report DeviceModelInfo with all-zero model_ids because they lack feature 0x0003. The normal asset resolution path fails for these since neither the strict nor suffix candidates match the registry — even though the registry contains their assets keyed by WPID.

Changes

crates/openlogi-desktop/src/services/assets.rs

  • Added resolve_by_wpid() method on AssetResolver that formats the WPID as a 4-hex suffix and matches against the registry via find_by_model_id_suffix. Falls back to codename/displayName matching.

crates/openlogi-desktop/src/state/devices.rs

  • In build_device_list(): when cache.resolve(model, codename) returns None, fall back to cache.resolve_by_wpid(paired.wpid, paired.codename). This covers the case where model_info is Some but contains all-zero IDs.
  • When asset resolution fails entirely, consult the persisted identity in config to preserve a known display name instead of falling back to "Slot N".

crates/openlogi-desktop/src/state/inventory.rs

  • Added anti-downgrade guard in persist_identities(): a resolved identity (one with a real product name) cannot be overwritten by a fallback identity ("Slot 3") when the resolver temporarily fails.
  • Added identity_is_resolved() and is_fallback_display_name() helpers.

Testing

Tested on real hardware: Logitech K540/K545 (WPID 0x4076) via Unifying receiver on Debian 13 Trixie.

  • Device correctly shows "K540/K545" with front.png asset in carousel and detail view
  • Identity remains stable across multiple inventory refresh cycles (2+ minutes tested)
  • Config does not regress to "Slot 3" after restarts
  • No regression on HID++ 2.0 devices (mouse on same receiver works normally)
  • No duplicate device entries created

How to test on hardware:

  1. Pair a Unifying keyboard that uses HID++ 1.0 (K540, K545, K375s, etc.)
  2. Run OPENLOGI_LOG=debug openlogi list — should show wpid=4076 for the keyboard
  3. Open the GUI — device should display its product name and image, not "Slot N"
  4. Wait 1-2 minutes — name should remain stable

Fixes #(new issue — HID++ 1.0 Unifying devices show as "Slot N" despite having assets in the registry)

…back

Devices using HID++ 1.0 (many Unifying keyboards like K540/K545, K375s,
etc.) report model_info with all-zero model_ids because they lack feature
0x0003. The normal asset resolution path fails for these since neither the
strict nor suffix candidates match the registry.

This commit adds a WPID-based fallback: when the normal resolve() returns
None and the device has a WPID, try resolve_by_wpid() which formats the
WPID as a 4-hex suffix and matches it against the asset registry's modelId.
This works because the Unifying WPID IS the eQuad transport PID.

Additionally, persist_identities() now guards against identity downgrade:
a resolved display name (e.g. "K540/K545") cannot be overwritten by a
fallback name ("Slot 3") when the asset resolver temporarily fails.

Tested on hardware: Logitech K540/K545 (wpid 0x4076) via Unifying receiver
on Debian 13 Trixie. The device correctly shows its product name and
front.png asset across multiple inventory refresh cycles.

Fixes devices that have assets in the registry but were showing as
"Slot N" or "Unknown device" due to HID++ 1.0 limitations.
@luisfacarmo
luisfacarmo requested a review from AprilNEA as a code owner August 21, 2026 10:01
@greptile-apps

greptile-apps Bot commented Aug 21, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds WPID-based asset resolution for HID++ 1.0 devices and protects resolved device identities from transient fallback values. The attempted receiver-slot reuse fix remains incomplete because the matching guard discards the available WPID before deciding that an unidentified replacement is the previous product.

  • Resolves asset registry entries by WPID suffix, with codename fallback.
  • Reuses persisted product names during temporary asset-resolution failures.
  • Adds anti-downgrade identity persistence and model-comparison guards.

Confidence Score: 4/5

The PR is not yet safe to merge because a same-kind device re-paired into an existing receiver slot can still inherit the previous occupant's identity when codename and model information are unavailable.

The new guard defaults to treating an unidentified same-kind slot occupant as the prior product while omitting the live WPID discriminator, leaving the previously reported stale identity, artwork, and capability metadata reachable.

Files Needing Attention: crates/openlogi-desktop/src/state/inventory.rs and crates/openlogi-desktop/src/state/devices.rs

Important Files Changed

Filename Overview
crates/openlogi-desktop/src/services/assets.rs Adds WPID-suffix and codename asset resolution for devices lacking usable HID++ model information.
crates/openlogi-desktop/src/state/devices.rs Integrates WPID asset fallback and persisted-name reuse, but the identity check cannot use WPID when other model clues are absent.
crates/openlogi-desktop/src/state/inventory.rs Adds anti-downgrade persistence, but its no-discriminator branch still preserves stale identity after same-kind receiver-slot replacement.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Receiver slot inventory] --> B{Asset resolves by model or WPID?}
  B -->|Yes| C[Build resolved identity]
  B -->|No| D[Build fallback identity]
  D --> E{Codename or model_info distinguishes occupant?}
  E -->|Yes, different| F[Replace persisted identity]
  E -->|No evidence| G[Preserve previous slot identity]
  G --> H[Replacement can show stale product metadata]
Loading

Fix all with Greploop Fix All in Codex Fix All in Claude Code

Reviews (5): Last reviewed commit: "fix(gui): scope anti-downgrade guard to ..." | Re-trigger Greptile

Comment thread crates/openlogi-desktop/src/state/inventory.rs Outdated
Address review feedback: when a Unifying/Bolt slot is re-paired with a
different device type (e.g. keyboard replaced by mouse), the anti-downgrade
guard must not preserve the previous occupant's identity. Adding a kind
check ensures the guard only fires when the device classification matches,
so a re-pairing correctly starts fresh.
@davidbudnick davidbudnick added type: bug Something is broken or behaves incorrectly platform: all Cross-platform issue labels Aug 21, 2026
Luis Carmo and others added 2 commits August 21, 2026 18:43
…ce kind

The identity-preservation guards in persist_identities() and the
display-name fallback in build_device_list() previously used only
DeviceKind as the discriminator to decide whether a persisted identity
should carry over. This allowed a same-kind re-pair (e.g. swapping one
mouse for another in the same receiver slot) to inherit the old device's
display name, capabilities, and model metadata.

Strengthen both checks: compare codename and model_info.config_key()
when available, so a different product model paired into the same slot
is correctly detected as a re-pairing. When no model-level identifiers
are available on either side, the conservative kind-only fallback is
retained (no regression for devices that never expose richer metadata).
@luisfacarmo
luisfacarmo force-pushed the fix/resolve-assets-for-hidpp1-devices branch from 70b3e6d to d85a266 Compare August 22, 2026 23:47
Comment on lines +442 to +445
// same device (the guard stays active). This matches the pre-existing
// kind-only behaviour for the corner case where neither side has any
// model-level identifiers at all.
true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Receiver-slot identity remains stale

When a same-kind device is re-paired into an existing receiver slot without a codename or model_info, same_model_identity treats the absence of contradictory evidence as a match even when the live pairing record has a different WPID. The guard then preserves the previous occupant's display name, model metadata, capabilities, and registry identity until another discriminator becomes available.

Knowledge Base Used:

Fix in Codex Fix in Claude Code

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

Labels

platform: all Cross-platform issue type: bug Something is broken or behaves incorrectly

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants