feat(battery): support legacy 0x1000 BatteryStatus and its charging quirk - #312
Conversation
Greptile SummaryThis PR adds support for the legacy
Confidence Score: 5/5
|
| Filename | Overview |
|---|---|
| crates/openlogi-hidpp/src/feature/battery_status/mod.rs | New BatteryStatusFeature (0x1000) implementation. Enum covers all known status values 0–7, including the previously-raised Other = 7 variant that prevents a parse failure from making the battery indicator vanish. #[non_exhaustive] is set and the upstream mapper has a wildcard arm, so any undocumented future value ≥ 8 will propagate as Unknown rather than crashing. |
| crates/openlogi-hid/src/inventory/features.rs | Introduces BatteryProbe enum and refactors battery_feature_index to prefer 0x1004 and fall back to 0x1000. The previously-raised ?-early-exit concern is addressed with break. New tests cover all four cases (unified found, legacy-only, unified preferred over legacy, neither). |
| crates/openlogi-hid/src/inventory/cache.rs | Adds hold_percentage_while_charging with an explicit BatteryProbe::Legacy guard — the previously-raised issue of the hold applying unconditionally to Unified probes is now fixed. Correctly threaded through both the full-probe and cache-hit paths, with unit tests covering all four branches. |
| crates/openlogi-hid/src/mappings.rs | Adds map_legacy_battery_status and legacy_battery_level_from_percentage. Status mapping is complete and matches the Solaar reference. The value-7 (Other) test confirms TryFromPrimitive + map_legacy_battery_status round-trips to Unknown rather than silently erasing the battery widget. |
| crates/openlogi-gui/src/app/widgets.rs | Adds battery_charging_no_reading for cold-start charging display and the associated UI logic in battery_summary. The function correctly suppresses the progress bar and percentage text when showing a bogus 0% during charging. |
| crates/openlogi-hid/src/write/diagnostics.rs | Adds read_battery_raw for openlogi diag battery. Probes 0x1004 first, falls back to 0x1000, and reports the corrected feature_hex: 0x1004 error when neither is present — addressing the previously-raised hardcoded-0x1000 issue. |
| crates/openlogi-gui/src/app_menu.rs | Tray menu correctly handles the cold-start charging case by testing battery_charging_no_reading before formatting the percentage string, keeping the menu item label accurate. |
| crates/openlogi-gui/src/app/home.rs | Gallery card battery view uses battery_charging_no_reading to choose between "Charging" text and the percentage string. Battery icon is still rendered in both branches so the charging glyph remains visible during cold start. |
| crates/openlogi-cli/src/cmd/diag/battery.rs | New diagnostic command that selects a device exposing 0x1000 or 0x1004 and prints the raw firmware report via read_battery_raw. |
Sequence Diagram
sequenceDiagram
participant GUI
participant Cache as probe_or_reuse (cache.rs)
participant Features as features.rs
participant HID as HID++ wire
GUI->>Cache: enumerate tick
alt cache miss / stale
Cache->>Features: probe_features(channel, slot)
Features->>HID: Device::new + enumerate_features()
HID-->>Features: feature-ID table
Features->>Features: "battery_feature_index(ids)<br/>prefers 0x1004, falls back 0x1000"
Features->>HID: read_battery(probe) → Unified or Legacy path
HID-->>Features: BatteryInfo (percentage, level, status)
Features-->>Cache: "(ProbedFeatures, Option<BatteryProbe>)"
Cache->>Cache: "hold_percentage_while_charging()<br/>Only for BatteryProbe::Legacy:<br/>carry prior % when charging+0%"
Cache-->>GUI: ProbedFeatures (battery held if Legacy quirk)
else cache hit
Cache->>HID: read_battery(c.battery probe) — one round-trip
HID-->>Cache: BatteryInfo
Cache->>Cache: "hold_percentage_while_charging()<br/>(Legacy guard same as above)"
Cache-->>GUI: updated ProbedFeatures
end
GUI->>GUI: "battery_charging_no_reading(b)<br/>true when Charging+0% (cold-start)<br/>→ show "Charging" without bogus 0%"
GUI->>GUI: "battery_view / battery_summary / tray menu<br/>render suppressed or held percentage"
Reviews (7): Last reviewed commit: "style(hid): rustfmt write re-exports" | Re-trigger Greptile
4cffc1a to
86e4154
Compare
|
Thanks @greptile-apps — applied all three:
|
|
All three changes look correct and well-executed. A few observations: 1. 2. 3. let other = HidppLegacyBatteryStatus::try_from(7u8).expect("7 is a known variant");
assert_eq!(map_legacy_battery_status(other), BatteryStatus::Unknown);This validates both that the One minor nit on The PR is in good shape. LGTM. Tip: You can customize Greptile's behavior for this repo with |
86e4154 to
ffa4f23
Compare
AprilNEA
left a comment
There was a problem hiding this comment.
Thanks for the careful follow-up here — the 0x1000 fallback, diagnostic command, and regression tests all look well-scoped, and CI is green.
I found two edge cases worth tightening before merge:
-
hold_percentage_while_chargingshould probably be scoped toBatteryProbe::Legacy. The helper and comments describe the MX2S /0x1000charging quirk, but it is currently applied afterprobe_featuresand on cache refresh regardless of whether the selected probe isUnifiedorLegacy(crates/openlogi-hid/src/inventory.rs, around the calls after fresh reads). On a0x1004device that legitimately reportscharging_percentage = 0while charging, or reports no percentage support, a previous non-zero cached value could be carried forward even though the legacy quirk is not involved. Matching onBatteryProbe::Legacyat the call site would keep the workaround tied to the feature that needs it. -
The cold-start GUI suppression hides the
0%text, but the detail summary progress bar still renders frombattery.percentage(relative_percent(0)andbattery_color(0)). That means the summary can still show an empty/critical-looking bar for the same “Charging, no reliable reading” case. It may be worth hiding the bar, using a neutral/charging style, or otherwise routingbattery_charging_no_reading(battery)through the progress-bar rendering too.
Otherwise this is a nice, focused addition — especially the raw openlogi diag battery path for verifying the firmware behavior on real devices.
ffa4f23 to
7e11836
Compare
|
Both addressed:
|
7e11836 to
d54b363
Compare
|
Want your agent to iterate on Greptile's feedback? Try greploops. |
1ca97cd to
64291e8
Compare
…uirk Older devices such as a Bluetooth-direct MX Master 2S expose only the legacy BatteryStatus feature (0x1000), never the unified 0x1004, so they showed no battery at all. Implement 0x1000 mirroring the SmartShift 0x2110/0x2111 design and have the inventory probe prefer 0x1004, falling back to 0x1000 — the same enhanced-then-legacy order SmartShift uses. 0x1000 reports a percentage but no level bitmask, so the coarse BatteryLevel is derived from fixed display buckets. The 0x1000 firmware can't gauge charge under load: it reports discharge_level=0 status=Recharging while charging, which surfaced as a misleading "Charging · 0%". Two layers handle it: - hold_percentage_while_charging() carries the last-known percentage forward through a charge session (frozen pre-charge value, cache only) so the reading stays trackable once one discharge read exists. - On a cold start (charger plugged before the app opens) there's no prior to hold, so the GUI shows "Charging" instead of the bogus 0% across the card, summary, and tray menu. Adds `openlogi diag battery`, which prints the raw 0x1004/0x1000 report so a claim like "MX2S shows 0% while charging" can be confirmed against the wire. Verified on hardware: MX2S reports 50% (discharging) over BT-direct, and 0% status=Recharging while charging.
64291e8 to
1a1ae7a
Compare
Summary
Older mice like a Bluetooth-direct MX Master 2S only expose the legacy BatteryStatus feature (0x1000), never the unified 0x1004, so they showed no battery at all. This adds 0x1000 support and handles its quirk: the firmware reports 0% while charging.
Changes
openlogi diag batteryto print the raw 0x1004/0x1000 report.Testing
cargo fmt --all -- --check,cargo clippy --workspace,cargo test(bucket mapping, hold-% across all four branches, GUI label).