cadmus_core/battery/
kobo.rs1use super::{Battery, Status};
2use crate::device::CURRENT_DEVICE;
3use anyhow::{format_err, Error};
4use std::fs::File;
5use std::io::{Read, Seek, SeekFrom};
6use std::path::Path;
7
8const BATTERY_INTERFACES: [&str; 3] = [
9 "/sys/class/power_supply/bd71827_bat",
10 "/sys/class/power_supply/mc13892_bat",
11 "/sys/class/power_supply/battery",
12];
13const POWER_COVER_INTERFACE: &str = "/sys/class/misc/cilix";
14
15const BATTERY_CAPACITY: &str = "capacity";
16const BATTERY_STATUS: &str = "status";
17
18const POWER_COVER_CAPACITY: &str = "cilix_bat_capacity";
19const POWER_COVER_STATUS: &str = "charge_status";
20const POWER_COVER_CONNECTED: &str = "cilix_conn";
21
22pub struct PowerCover {
23 capacity: File,
24 status: File,
25 connected: File,
26}
27
28pub struct KoboBattery {
30 capacity: File,
31 status: File,
32 power_cover: Option<PowerCover>,
33}
34
35impl KoboBattery {
36 pub fn new() -> Result<KoboBattery, Error> {
37 let base = Path::new(
38 BATTERY_INTERFACES
39 .iter()
40 .find(|bi| Path::new(bi).exists())
41 .ok_or_else(|| format_err!("battery path missing"))?,
42 );
43 let capacity = File::open(base.join(BATTERY_CAPACITY))?;
44 let status = File::open(base.join(BATTERY_STATUS))?;
45 let power_cover = if CURRENT_DEVICE.has_power_cover() {
46 let base = Path::new(POWER_COVER_INTERFACE);
47 let capacity = File::open(base.join(POWER_COVER_CAPACITY))?;
48 let status = File::open(base.join(POWER_COVER_STATUS))?;
49 let connected = File::open(base.join(POWER_COVER_CONNECTED))?;
50 Some(PowerCover {
51 capacity,
52 status,
53 connected,
54 })
55 } else {
56 None
57 };
58 Ok(KoboBattery {
59 capacity,
60 status,
61 power_cover,
62 })
63 }
64}
65
66impl KoboBattery {
67 fn is_power_cover_connected(&mut self) -> Result<bool, Error> {
68 if let Some(power_cover) = self.power_cover.as_mut() {
69 let mut buf = String::new();
70 power_cover.connected.seek(SeekFrom::Start(0))?;
71 power_cover.connected.read_to_string(&mut buf)?;
72 Ok(buf.trim_end().parse::<u8>().map_or(false, |v| v == 1))
73 } else {
74 Ok(false)
75 }
76 }
77}
78
79impl Battery for KoboBattery {
80 fn capacity(&mut self) -> Result<Vec<f32>, Error> {
81 let mut buf = String::new();
82 self.capacity.seek(SeekFrom::Start(0))?;
83 self.capacity.read_to_string(&mut buf)?;
84 let capacity = buf.trim_end().parse::<f32>().unwrap_or(0.0);
85 if matches!(self.is_power_cover_connected(), Ok(true)) {
86 let mut buf = String::new();
87 self.power_cover.iter_mut().for_each(|power_cover| {
88 power_cover.capacity.seek(SeekFrom::Start(0)).ok();
89 power_cover.capacity.read_to_string(&mut buf).ok();
90 });
91 let aux_capacity = buf.trim_end().parse::<f32>().unwrap_or(0.0);
92 Ok(vec![capacity, aux_capacity])
93 } else {
94 Ok(vec![capacity])
95 }
96 }
97
98 fn status(&mut self) -> Result<Vec<Status>, Error> {
99 let mut buf = String::new();
100 self.status.seek(SeekFrom::Start(0))?;
101 self.status.read_to_string(&mut buf)?;
102 let status = match buf.trim_end() {
103 "Discharging" => Status::Discharging,
104 "Charging" => Status::Charging,
105 "Not charging" | "Full" => Status::Charged,
106 _ => Status::Unknown,
107 };
108 if matches!(self.is_power_cover_connected(), Ok(true)) {
109 let mut buf = String::new();
110 self.power_cover.iter_mut().for_each(|power_cover| {
111 power_cover.status.seek(SeekFrom::Start(0)).ok();
112 power_cover.status.read_to_string(&mut buf).ok();
113 });
114 let aux_status = match buf.trim_end().parse::<i8>() {
115 Ok(0) => Status::Discharging,
116 Ok(2) => Status::Charging,
117 Ok(3) => Status::Charged,
118 _ => Status::Unknown,
119 };
120 Ok(vec![status, aux_status])
121 } else {
122 Ok(vec![status])
123 }
124 }
125}