11"""
2- KingWatch Pro - core/battery.py
3- Battery stats via Android BatteryManager (pyjnius) with plyer fallback.
4- No psutil .
2+ KingWatch Pro v17 - core/battery.py
3+ Full battery stats: pct, temp, voltage, current, power,
4+ charging/discharging ETA via capacity_level + charge_full .
55"""
6+ import time
67
8+ _prev_pct = - 1
9+ _prev_time = 0.0
10+ _eta_cache = "Unknown"
711
8- def _read_sys_battery (key : str , default = None ):
9- """Read a value from /sys/class/power_supply/battery/<key>."""
10- paths = [
11- f"/sys/class/power_supply/battery/{ key } " ,
12- f"/sys/class/power_supply/BAT0/{ key } " ,
13- f"/sys/class/power_supply/BAT1/{ key } " ,
14- ]
15- for p in paths :
12+
13+ def _sys (key , default = None ):
14+ for prefix in ("battery" , "BAT0" , "BAT1" , "bms" , "main-battery" ):
1615 try :
17- with open (p ) as f :
16+ with open (f"/sys/class/power_supply/ { prefix } / { key } " ) as f :
1817 return f .read ().strip ()
1918 except Exception :
2019 continue
2120 return default
2221
2322
24- def get_battery () -> dict :
25- # ---- percentage ----
26- pct = 0
23+ def _int (val , default = 0 ):
2724 try :
28- pct = int (_read_sys_battery ( "capacity" , "0" ) )
25+ return int (val )
2926 except Exception :
30- pass
27+ return default
28+
29+
30+ def _eta_string (pct , status ):
31+ """Estimate charge/discharge time from capacity drain rate."""
32+ global _prev_pct , _prev_time , _eta_cache
33+ now = time .monotonic ()
34+
35+ if _prev_pct < 0 :
36+ _prev_pct = pct
37+ _prev_time = now
38+ return status
39+
40+ elapsed = now - _prev_time
41+ delta = pct - _prev_pct # positive = charging, negative = draining
42+
43+ # Only update ETA every 60s
44+ if elapsed >= 60 and delta != 0 :
45+ rate_per_min = abs (delta ) / (elapsed / 60 )
46+ if rate_per_min > 0 :
47+ if delta > 0 :
48+ mins_left = (100 - pct ) / rate_per_min
49+ h , m = divmod (int (mins_left ), 60 )
50+ _eta_cache = f"Full in { h } h { m :02d} m" if h > 0 else f"Full in { m } m"
51+ else :
52+ mins_left = pct / rate_per_min
53+ h , m = divmod (int (mins_left ), 60 )
54+ _eta_cache = f"Empty in { h } h { m :02d} m" if h > 0 else f"Empty in { m } m"
55+ _prev_pct = pct
56+ _prev_time = now
57+
58+ if _eta_cache == "Unknown" :
59+ return status
60+ return _eta_cache
61+
62+
63+ def get_battery () -> dict :
64+ # Percentage
65+ pct = _int (_sys ("capacity" , "0" ))
66+
67+ # Status: Charging / Discharging / Full
68+ status = _sys ("status" , "Unknown" ) or "Unknown"
3169
32- # ---- temperature (tenths of degC) ----
70+ # Temperature (tenths of °C)
3371 temp_str = "N/A"
3472 try :
35- raw = int ( _read_sys_battery ("temp" , "0" ))
73+ raw = _int ( _sys ("temp" , "0" ))
3674 temp_str = f"{ raw / 10 :.1f} C"
3775 except Exception :
3876 pass
3977
40- # ---- voltage (uV -> mV) ----
78+ # Voltage µV → mV
4179 volt_str = "N/A"
80+ v_mv = 0
4281 try :
43- raw = int (_read_sys_battery ("voltage_now" , "0" ))
44- volt_str = f"{ raw // 1000 } mV"
82+ raw = _int (_sys ("voltage_now" , "0" ))
83+ v_mv = raw // 1000
84+ volt_str = f"{ v_mv } mV"
4585 except Exception :
4686 pass
4787
48- # ---- current (uA -> mA, negative = discharging) ----
88+ # Current µA → mA (may be negative when discharging)
4989 current_str = "N/A"
5090 power_str = "N/A"
5191 try :
52- cur_ua = int ( _read_sys_battery ("current_now" , "0" ))
92+ cur_ua = _int ( _sys ("current_now" , "0" ))
5393 cur_ma = abs (cur_ua ) // 1000
54- current_str = f"{ cur_ma } mA"
55- # Power = V * I (mW)
56- if volt_str != "N/A" :
57- v_mv = int (_read_sys_battery ("voltage_now" , "0" )) // 1000
58- power_mw = (v_mv * cur_ma ) // 1000
94+ sign = "+" if status .lower () == "charging" else "-"
95+ current_str = f"{ sign } { cur_ma } mA"
96+ if v_mv > 0 :
97+ power_mw = (v_mv * cur_ma ) // 1000
5998 power_str = f"{ power_mw } mW"
6099 except Exception :
61100 pass
62101
63- # ---- status / ETA ----
64- status = _read_sys_battery ("status" , "Unknown" )
65- eta = status if status else "Unknown"
102+ eta = _eta_string (pct , status )
66103
67104 return {
68105 "pct" : pct ,
@@ -71,4 +108,5 @@ def get_battery() -> dict:
71108 "current" : current_str ,
72109 "power" : power_str ,
73110 "eta" : eta ,
74- }
111+ "status" : status ,
112+ }
0 commit comments