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