Linux Utility to Help Track Framework Battery Usage

I built a small utility today to help me track battery usage. This first version is a simple tool to track battery drain and do some math for suspend/resume (and eventually hibernation and on/off).

It’s it’s not very featureful and might be renamed, but I couldn’t find anything else like it and it still might be useful, so I figure I’d put it out there in case it helps anyone curious about calculating power drain on their Framework laptop.

I’ve only tested it on my Arch laptop so far but it’s fairly straightforward to use. The included should be pretty portable but I recommend people to look at what it’s actually doing since packaging is not one of my strong suits.

The output, if it’s working, should look something like this (by default the script tries to find the last resume and suspend):

Slept for 7.38 hours
Used 4.88 Wh, an average rate of 0.66 W
For your 53.42 Wh battery this is 1.24%/hr or 29.73%/day

What a great little tool! Thank you for doing this.

Here’s my report from earlier today:


Glad you found it useful (and that it actually worked :sweat_smile:). I have a bunch of cleanup and additional features I’m planning on adding, but happy to take any feedback/feature requests. If I make any schema changes to the db I’ll make sure it tries to smoothly upgrade. My main goal is to be able to keep a good running history/track performance.

I’m also collecting existing power tracking tools in the README.

1 Like

This tool looks great! I might take a stab at packaging it in Debian, see ITP 1021396, which also includes a quick comparison with battery-stats, which I have been using so far (and contributed to).

Ah cool, let me know if there’s anything you’d like that would make this more suitable as a standard app. I assume that one of the things that would be desirable would be assigning where the state DB gets stored (I’d assume that most distros would want something in /var or .local.

I was planning on giving this tool a bit of love recently (making the command line UI a bit more robust, adding better reporting, allowing notes to be stored for doing specific tests for example). I recently made a couple changes - a sanity check for making sure the DB schema is loaded (in case someone doesn’t run the INSTALL script) and to check for a BAT# instead of hard-coding for BAT1 (a couple of my other laptops use BAT0 for example).

1 Like

oh dear, since you asked… :slight_smile:

Yes. This could be in /var… somewhere. I’ve been struggling with this, actually, in my own apps. The XDG spec doesn’t talk much about system-level apps, and assume desktop applications, so we don’t have that good of a place for those things.

Databases (e.g. PostgreSQL) store their stuff in (say) /var/lib/postgresql so that would be one logical place.

battery-stats stores its logs in… /var/log/, but they are literally logs, as in text files that are appended to and can be rotated. I wouldn’t expect to see a SQLite database in there…

So yeah, I’d probably say /var/lib/batterylog/?

In feed2exec, I used $HOME/.local/share AKA $XDG_DATA_HOME but that’s the equivalent of /usr/local/share which is actually wrong. It looks like the right place for this is $XDG_STATE_HOME, which is $HOME/.local/state and doesn’t have a global equivalent (ugh!).

yeaah… about that… it’s really nice that the script is short and sweet, and it makes it much more readable. but as you cram more things in there, it’s going to become… less so. i encourage you to slap an argparse in there, a main() and split things up in different functions a bit.

it might even make sense to add some unit tests. for small scripts like this I like to use doctest since (1) it’s in stdlib and (2) it’s simple and doesn’t require extra functions.

one thing that battery-stats does well is that it regularly collects samples and then has tools to produce a graph of the result. that’s something I’d be missing if I’d want to switch from battery-stats to batterylog. specifically, i wrote this graphing tool that would do a linear regression to guess when a battery would be dead, based on its capacity decrease over time.

quite useful!

but maybe it’s okay to have one tool do its own thing… batterylog is excellent at telling you “this suspend took that much power” and battery-stats is good at doing long term guesses. i’m just a little worried that running both in parallel (or, actually, just battery-stats) would needlessly drain … my battery! :slight_smile:

what did you have in mind here actually?

What I did in feed2exec is that I just initialize a new DB if it doesn’t exist, and ship the schema directly in the source code. (I kind of built a small ORM by accident there, so it doesn’t look very obvious, see for example this line.)

You have a single table in there, you could probably afford to embed it in the source code anyways.

ah that’s great, thanks! :slight_smile:

one last thing i would recommend would be to make a basic and (especially) setup.cfg. i know it’s kind of a pain in the back to deal with, but it’s basically something you have to do once and you’re done for the lifetime of the project. it makes your software easier to package for people like me, and easier to install for everyone else.

feel free to reuse this simple example from my undertime script where everything is in setup.cfg and is a stub. I also maintain a release process in contributing.rst because I always forget how to do this and there are always slight variations between my projects. in the case of undertime, it’s basically:

git tag -s x.y.z &&
python3 bdist_wheel &&
pip3 install $(ls -1tr dist/*.whl | tail -1) &&
~/.local/bin/undertime &&
~/.local/bin/undertime --selftest &&
echo self-test suite passess in pip &&
~/.local/bin/undertime --version &&
pip3 uninstall undertime

I hope that helps!

Ha yeah, guess I did ask for it, and I appreciate the spirit in which the feedback was given. :slight_smile:

I spent the beginning of the year writing a 30K LOC Python app, so I’m good with keeping the codebase manageable if it grows, but I’d also like to not add more complexity in than it needs. My plan is to keep it to stdlib only, so will probably use argparse if adding more commands, and may package it up for PyPi.

I don’t think regular sampling is something I’d add, although there’s no reason that you couldn’t add your own cron call (the event field is sort of built for that in mind). It’s not something I’d necessarily want it to do by default though.

I’ll look at battery-stats and see if my tool could be extended to do everything that does.

Once this app is feature complete, I may actually explore rewriting it in something like Nim and seeing if the performance gains are worthwhile, but if it’s not worthwhile, I’ll leave it in Python so its easier for people to modify for their own uses.


Yeah I wouldn’t bother too much about this in the short term. As I said, people that do need what battery-stats is doing can just… use battery-stats. :slight_smile:

That I’m less sure of. Nim, for example, would be much harder to package in Debian; I don’t think we have the toolchain ready even the slightest.

But yes, Python might not be the best match for this kind of tool. The startup time is excruciatingly slow and while performance is generally acceptable, we might be wasting wattage with this silly startup time.

That’s something I looked into for undertime, as that was one of the features I wanted; it needed to feel near-instant, so a few tens or hundreds of milisecond, definitely below a second. Turns out that just starting the Python burns 10ms, and once you start loading libraries, you waste of lot of cycles… There’s been talk about lazy-loading imports recently, and there are command-line flags you can use to reduce that hit though…

For what it’s worth, I used batterylog and my homegrown thing to do measurements of the suspend battery life with various settings and expansion cards. See the details here:

1 Like