hidpp/feature/battery_status.rs
1//! Implements the legacy `BatteryStatus` feature (ID `0x1000`) that reports a
2//! device's battery charge as a discharge level plus a charging status.
3//!
4//! This is the predecessor of `UnifiedBattery` (`0x1004`): older mice such as
5//! the MX Master 2S expose `0x1000` and never `0x1004`, so the inventory probe
6//! falls back to this feature when the unified one is absent — the same
7//! enhanced-then-legacy pattern `SmartShift` uses for `0x2111` / `0x2110`.
8//!
9//! Only `getBatteryLevelStatus` (function `0`) is implemented; the optional
10//! `getBatteryCapability` (function `1`) and the broadcast event aren't needed
11//! to display a charge reading.
12
13use std::hash::Hash;
14
15use num_enum::{IntoPrimitive, TryFromPrimitive};
16use openlogi_hidpp_derive::Feature;
17
18use crate::{feature::FeatureEndpoint, protocol::v20::Hidpp20Error};
19
20/// Implements the legacy `BatteryStatus` / `0x1000` feature.
21#[derive(Feature)]
22#[creatable(id = 0x1000, version = 0)]
23pub struct BatteryStatusFeature {
24 /// The endpoint this feature talks to.
25 endpoint: FeatureEndpoint,
26}
27
28impl BatteryStatusFeature {
29 /// Reads the current battery level and charging status (function `0`,
30 /// `getBatteryLevelStatus`).
31 pub async fn get_battery_level_status(&self) -> Result<LegacyBatteryInfo, Hidpp20Error> {
32 let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
33
34 Ok(LegacyBatteryInfo {
35 discharge_level: payload[0],
36 next_level: payload[1],
37 status: LegacyBatteryStatus::try_from(payload[2])
38 .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
39 })
40 }
41}
42
43/// A reading from the legacy `0x1000` `getBatteryLevelStatus` function.
44#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize))]
46#[non_exhaustive]
47pub struct LegacyBatteryInfo {
48 /// Current battery charge as a percentage (`0`–`100`). Logitech firmware
49 /// reports this in coarse steps rather than a continuous value.
50 pub discharge_level: u8,
51
52 /// The next lower discharge step the firmware will report — a hint at the
53 /// reporting granularity. Unused for display.
54 pub next_level: u8,
55
56 /// The current charging status.
57 pub status: LegacyBatteryStatus,
58}
59
60/// Charging status reported by the legacy `0x1000` feature. Values follow the
61/// HID++ `batteryStatus` enumeration (see Solaar / `hid-logitech-hidpp`).
62#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
63#[cfg_attr(feature = "serde", derive(serde::Serialize))]
64#[non_exhaustive]
65#[repr(u8)]
66pub enum LegacyBatteryStatus {
67 /// Battery is discharging.
68 Discharging = 0,
69 /// Battery is recharging.
70 Recharging = 1,
71 /// Battery is charging and nearly full.
72 AlmostFull = 2,
73 /// Battery charge is complete.
74 Full = 3,
75 /// Battery is recharging below optimal speed.
76 SlowRecharge = 4,
77 /// The battery type is invalid.
78 InvalidBattery = 5,
79 /// The battery subsystem reported a thermal error.
80 ThermalError = 6,
81 /// "Other charging error" (Solaar lists value 7). Kept explicit so a device
82 /// reporting it surfaces as Unknown instead of failing the parse and making
83 /// the battery indicator vanish from the UI.
84 Other = 7,
85}