Skip to content

Commit e7b4e4d

Browse files
committed
Add live battery stats
1 parent fa5442d commit e7b4e4d

15 files changed

Lines changed: 633 additions & 665 deletions

po/POTFILES.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ yafi/window.py
88
yafi/thermals.py
99
yafi/leds.py
1010
yafi/battery.py
11+
yafi/battery_limiter.py
1112
yafi/hardware.py
1213
yafi/ui/window.ui
1314
yafi/ui/thermals.ui
1415
yafi/ui/leds.ui
1516
yafi/ui/battery.ui
17+
yafi/ui/battery_limiter.ui
1618
yafi/ui/hardware.ui

yafi/battery.py

Lines changed: 67 additions & 197 deletions
Original file line numberDiff line numberDiff line change
@@ -25,211 +25,81 @@
2525
import cros_ec_python.commands as ec_commands
2626
import cros_ec_python.exceptions as ec_exceptions
2727

28-
@Gtk.Template(resource_path='/au/stevetech/yafi/ui/battery.ui')
28+
29+
@Gtk.Template(resource_path="/au/stevetech/yafi/ui/battery.ui")
2930
class BatteryPage(Gtk.Box):
30-
__gtype_name__ = 'BatteryPage'
31+
__gtype_name__ = "BatteryPage"
32+
33+
batt_status = Gtk.Template.Child()
3134

32-
chg_limit_enable = Gtk.Template.Child()
33-
chg_limit = Gtk.Template.Child()
34-
chg_limit_label = Gtk.Template.Child()
35-
chg_limit_scale = Gtk.Template.Child()
36-
bat_limit = Gtk.Template.Child()
37-
bat_limit_label = Gtk.Template.Child()
38-
bat_limit_scale = Gtk.Template.Child()
39-
chg_limit_override = Gtk.Template.Child()
40-
chg_limit_override_btn = Gtk.Template.Child()
35+
batt_charge = Gtk.Template.Child()
36+
batt_health = Gtk.Template.Child()
37+
batt_cycles_label = Gtk.Template.Child()
38+
batt_volts_label = Gtk.Template.Child()
39+
batt_watts_label = Gtk.Template.Child()
40+
batt_cap_rem_label = Gtk.Template.Child()
41+
batt_cap_full_label = Gtk.Template.Child()
4142

42-
bat_ext_group = Gtk.Template.Child()
43-
bat_ext_enable = Gtk.Template.Child()
44-
bat_ext_stage = Gtk.Template.Child()
45-
bat_ext_trigger_time = Gtk.Template.Child()
46-
bat_ext_reset_time = Gtk.Template.Child()
47-
bat_ext_trigger = Gtk.Template.Child()
48-
bat_ext_reset = Gtk.Template.Child()
43+
batt_manu = Gtk.Template.Child()
44+
batt_model = Gtk.Template.Child()
45+
batt_serial = Gtk.Template.Child()
46+
batt_type = Gtk.Template.Child()
47+
batt_orig_cap = Gtk.Template.Child()
48+
batt_orig_volts = Gtk.Template.Child()
4949

5050
def __init__(self, **kwargs):
5151
super().__init__(**kwargs)
5252

5353
def setup(self, app):
54-
# Charge limiter
55-
try:
56-
ec_limit = ec_commands.framework_laptop.get_charge_limit(app.cros_ec)
57-
ec_limit_enabled = ec_limit != (0, 0)
58-
self.chg_limit_enable.set_active(ec_limit_enabled)
59-
if ec_limit_enabled:
60-
self.chg_limit_scale.set_value(ec_limit[0])
61-
self.chg_limit_label.set_label(f"{ec_limit[0]}%")
62-
self.bat_limit_scale.set_value(ec_limit[1])
63-
self.bat_limit_label.set_label(f"{ec_limit[1]}%")
64-
self.chg_limit.set_sensitive(True)
65-
self.bat_limit.set_sensitive(True)
66-
self.chg_limit_override.set_sensitive(True)
67-
68-
def handle_chg_limit_change(min, max):
69-
ec_commands.framework_laptop.set_charge_limit(
70-
app.cros_ec, int(min), int(max)
71-
)
72-
73-
def handle_chg_limit_enable(switch):
74-
active = switch.get_active()
75-
if active:
76-
handle_chg_limit_change(
77-
self.chg_limit_scale.get_value(), self.bat_limit_scale.get_value()
78-
)
79-
else:
80-
ec_commands.framework_laptop.disable_charge_limit(app.cros_ec)
81-
82-
self.chg_limit.set_sensitive(active)
83-
self.bat_limit.set_sensitive(active)
84-
self.chg_limit_override.set_sensitive(active)
85-
86-
self.chg_limit_enable.connect(
87-
"notify::active", lambda switch, _: handle_chg_limit_enable(switch)
88-
)
89-
self.chg_limit_scale.connect(
90-
"value-changed",
91-
lambda scale: handle_chg_limit_change(
92-
scale.get_value(), self.bat_limit_scale.get_value()
93-
),
94-
)
95-
self.bat_limit_scale.connect(
96-
"value-changed",
97-
lambda scale: handle_chg_limit_change(
98-
self.chg_limit_scale.get_value(), scale.get_value()
99-
),
100-
)
101-
102-
self.chg_limit_override_btn.connect(
103-
"clicked",
104-
lambda _: ec_commands.framework_laptop.override_charge_limit(
105-
app.cros_ec
106-
),
107-
)
108-
except ec_exceptions.ECError as e:
109-
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
110-
app.no_support.append(ec_commands.framework_laptop.EC_CMD_CHARGE_LIMIT_CONTROL)
111-
self.chg_limit_enable.set_sensitive(False)
112-
else:
113-
raise e
114-
115-
# Battery Extender
116-
try:
117-
ec_extender = ec_commands.framework_laptop.get_battery_extender(
118-
app.cros_ec
119-
)
120-
self.bat_ext_enable.set_active(not ec_extender["disable"])
121-
self.bat_ext_stage.set_sensitive(not ec_extender["disable"])
122-
self.bat_ext_trigger_time.set_sensitive(not ec_extender["disable"])
123-
self.bat_ext_reset_time.set_sensitive(not ec_extender["disable"])
124-
self.bat_ext_trigger.set_sensitive(not ec_extender["disable"])
125-
self.bat_ext_reset.set_sensitive(not ec_extender["disable"])
126-
127-
self.bat_ext_stage.set_subtitle(str(ec_extender["current_stage"]))
128-
self.bat_ext_trigger_time.set_subtitle(
129-
format_timedelta(ec_extender["trigger_timedelta"])
130-
)
131-
self.bat_ext_reset_time.set_subtitle(
132-
format_timedelta(ec_extender["reset_timedelta"])
133-
)
134-
self.bat_ext_trigger.set_value(ec_extender["trigger_days"])
135-
self.bat_ext_reset.set_value(ec_extender["reset_minutes"])
136-
137-
def handle_extender_enable(switch):
138-
active = switch.get_active()
139-
ec_commands.framework_laptop.set_battery_extender(
140-
app.cros_ec,
141-
not active,
142-
int(self.bat_ext_trigger.get_value()),
143-
int(self.bat_ext_reset.get_value()),
144-
)
145-
self.bat_ext_stage.set_sensitive(active)
146-
self.bat_ext_trigger_time.set_sensitive(active)
147-
self.bat_ext_reset_time.set_sensitive(active)
148-
self.bat_ext_trigger.set_sensitive(active)
149-
self.bat_ext_reset.set_sensitive(active)
150-
151-
self.bat_ext_enable.connect(
152-
"notify::active", lambda switch, _: handle_extender_enable(switch)
153-
)
154-
self.bat_ext_trigger.connect(
155-
"notify::value",
156-
lambda scale, _: ec_commands.framework_laptop.set_battery_extender(
157-
app.cros_ec,
158-
not self.bat_ext_enable.get_active(),
159-
int(scale.get_value()),
160-
int(self.bat_ext_reset.get_value()),
161-
),
162-
)
163-
self.bat_ext_reset.connect(
164-
"notify::value",
165-
lambda scale, _: ec_commands.framework_laptop.set_battery_extender(
166-
app.cros_ec,
167-
not self.bat_ext_enable.get_active(),
168-
int(self.bat_ext_trigger.get_value()),
169-
int(scale.get_value()),
170-
),
171-
)
172-
except ec_exceptions.ECError as e:
173-
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
174-
app.no_support.append(
175-
ec_commands.framework_laptop.EC_CMD_BATTERY_EXTENDER
176-
)
177-
self.bat_ext_group.set_visible(False)
178-
else:
179-
raise e
180-
54+
battery = ec_commands.memmap.get_battery_values(app.cros_ec)
55+
self.batt_manu.set_subtitle(battery["manufacturer"])
56+
self.batt_model.set_subtitle(battery["model"])
57+
self.batt_serial.set_subtitle(battery["serial"])
58+
self.batt_type.set_subtitle(battery["type"])
59+
self.batt_orig_cap.set_subtitle(f"{self._get_watts(battery, 'design_capacity'):.2f}Wh")
60+
self.batt_orig_volts.set_subtitle(f"{battery['design_voltage']/1000}V")
61+
self._update_battery(app, battery)
18162
# Schedule _update_battery to run every second
182-
GLib.timeout_add_seconds(
183-
1,
184-
self._update_battery,
185-
app
63+
GLib.timeout_add_seconds(1, self._update_battery, app)
64+
65+
def _get_watts(self, battery, key, volt_key="design_voltage"):
66+
return (battery[key] * battery[volt_key]) / 1000_000
67+
68+
def _update_battery(self, app, battery=None):
69+
if battery is None:
70+
battery = ec_commands.memmap.get_battery_values(app.cros_ec)
71+
72+
status_messages = []
73+
if battery["invalid_data"]:
74+
status_messages.append("Invalid Data")
75+
if not battery["batt_present"]:
76+
status_messages.append("No Battery")
77+
if battery["ac_present"]:
78+
status_messages.append("Plugged in")
79+
if battery["level_critical"]:
80+
status_messages.append("Critical")
81+
if battery["discharging"]:
82+
status_messages.append("Discharging")
83+
if battery["charging"]:
84+
status_messages.append("Charging")
85+
self.batt_status.set_subtitle(", ".join(status_messages))
86+
87+
self.batt_charge.set_fraction(
88+
battery["capacity"] / battery["last_full_charge_capacity"]
89+
)
90+
self.batt_health.set_fraction(
91+
battery["last_full_charge_capacity"] / battery["design_capacity"]
92+
)
93+
self.batt_cycles_label.set_label(str(battery["cycle_count"]))
94+
self.batt_volts_label.set_label(f"{battery['volt']/1000:.2f}V")
95+
self.batt_watts_label.set_label(
96+
f"{self._get_watts(battery, 'rate', 'volt') * (-1 if battery['charging'] else 1):.2f}W"
97+
)
98+
self.batt_cap_rem_label.set_label(
99+
f"{self._get_watts(battery, 'capacity'):.2f}Wh"
100+
)
101+
self.batt_cap_full_label.set_label(
102+
f"{self._get_watts(battery, 'last_full_charge_capacity'):.2f}Wh"
186103
)
187104

188-
def _update_battery(self, app):
189-
success = False
190-
191-
# Charge Limiter
192-
if not ec_commands.framework_laptop.EC_CMD_CHARGE_LIMIT_CONTROL in app.no_support:
193-
try:
194-
ec_limit = ec_commands.framework_laptop.get_charge_limit(app.cros_ec)
195-
self.chg_limit_label.set_label(f"{ec_limit[0]}%")
196-
self.bat_limit_label.set_label(f"{ec_limit[1]}%")
197-
198-
success = True
199-
except ec_exceptions.ECError as e:
200-
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
201-
app.no_support.append(ec_commands.framework_laptop.EC_CMD_CHARGE_LIMIT_CONTROL)
202-
else:
203-
raise e
204-
205-
# Battery Extender
206-
if not ec_commands.framework_laptop.EC_CMD_BATTERY_EXTENDER in app.no_support:
207-
try:
208-
ec_extender = ec_commands.framework_laptop.get_battery_extender(
209-
app.cros_ec
210-
)
211-
212-
self.bat_ext_stage.set_subtitle(str(ec_extender["current_stage"]))
213-
self.bat_ext_trigger_time.set_subtitle(
214-
format_timedelta(ec_extender["trigger_timedelta"])
215-
)
216-
self.bat_ext_reset_time.set_subtitle(
217-
format_timedelta(ec_extender["reset_timedelta"])
218-
)
219-
220-
success = True
221-
except ec_exceptions.ECError as e:
222-
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
223-
app.no_support.append(
224-
ec_commands.framework_laptop.EC_CMD_BATTERY_EXTENDER
225-
)
226-
else:
227-
raise e
228-
229-
return app.current_page == 2 and success
230-
231-
def format_timedelta(timedelta):
232-
days = f"{timedelta.days} days, " if timedelta.days else ""
233-
hours, remainder = divmod(timedelta.seconds, 3600)
234-
minutes, seconds = divmod(remainder, 60)
235-
return days + f"{hours}:{minutes:02}:{seconds:02}"
105+
return app.current_page == 2

0 commit comments

Comments
 (0)