Adding battery charge to Tmux status bar

If you are not aware of tmux, do check it out. It’s a solution for problem you didn’t know you have. And, once you’re in its grasp, it’s very hard to get out.

One nice feature it does have is permanent status bar. My status bar used to just show CPU load.

set-window-option -g status-right ' #( vmstat 1 2 | tail -1 | awk "{ USAGE=100-\$15; if (USAGE<20) { printf \"#[fg=green]\"; } else if (USAGE<80) { printf \"#[fg=brightyellow]\"; } else { printf \"#[bg=red]#[fg=brightwhite]\"; }; print \" \" USAGE \"%\" }" ) '

However, after my laptop turned off on me while logged on remotely once too many, I decided to have battery charge there too. Since I don’t really care about the exact percentage, I decided to have it represented as a single “block” (▁▂▃▄▅▆▇█) character. To get capacity, I just used /sys/class/power_supply/BAT*/capacity. With percentage in hand, I then used a bit of math to have each block represent 14% of the charge, with the last 2% of charge using the last block. Since I use reverse coloring for status bar, blocks are ordered in increasing order.

#( awk '\''{print substr("▁▂▃▄▅▆▇█", int($1*7/98)+1, 1)}'\'' /sys/class/power_supply/BAT*/capacity || echo "█" )'

So, mission accomplished! Well, there’s always some scope creep. In my case, I also wanted to see charging status. This can be found in /sys/class/power_supply/BAT*/status. If value is “Charging”, I wanted negative space above battery block to be a different color.

#( awk '\''$1=="Charging" {print "#[fg=brightmagenta]"; next} {print "#[fg=black]"}'\'' /sys/class/power_supply/BAT*/status )#[reverse]#( awk '\''{print substr("▁▂▃▄▅▆▇█", int($1*7/98)+1, 1)}'\'' /sys/class/power_supply/BAT*/capacity || echo "█" )'

Illustration

Essentially, if status is “Charging”, awk will output bright magenta ANSI foreground code. This then gets reversed (i.e. background and foreground swapped), followed by battery charge block. Probably as much information as one can push into a single character.


PS: For curious, this is my full status line.

set-window-option -g status-right ' #( vmstat 1 2 | tail -1 | awk "{ USAGE=100-\$15; if (USAGE<20) { printf \"#[fg=green]\"; } else if (USAGE<80) { printf \"#[fg=brightyellow]\"; } else { printf \"#[bg=red]#[fg=brightwhite]\"; }; print \" \" USAGE \"%\" }" )#( awk '\''$1=="Charging" {print "#[fg=brightmagenta]"; next} {print "#[fg=black]"}'\'' /sys/class/power_supply/BAT*/status )#[reverse]#( awk '\''{print substr("▁▂▃▄▅▆▇█", int($1*7/98)+1, 1)}'\'' /sys/class/power_supply/BAT*/capacity || echo "█" )'

Goto in Spirit But Not in Name

I’ve heard statements like “good programmer should never use goto” a million times. Due to various reasons, some good and some bad, goto command is really hated. I would say it’s hated so much that quite a few young programmers are even not aware of its existence. One could say its existence in C# is treated as measels. And, just like measels, it’s making a comeback.

First of all, why would you use goto? For example, if we’re checking something deep in loops, there is no easier way to exit them fully than a humble goto. Consider the following code:

for (var i = 0; i < 100; i++) {
  for (var j = 0; j < 100; j++) {
    if (something) { goto Done; }
  }
}

Done:
// proceed with other code

Yes, you can get the same effect by using break at each level but this is definitely a nicer way to do it. And I would argue that this is NOT a misuse of goto. Regardless, C# 15 is preparing a death nail for this usage too.

New feature is called Labeled break and continue. In C# 15, my example above would now look like this:

Work:
for (var i = 0; i < 100; i++) {
  for (var j = 0; j < 100; j++) {
    if (something) { break Work; }
  }
}

// proceed with other code

I would argue this is a goto in disguise. Just a syntactic sugar that changes what gets labeled but doesn’t really change the flow.

That said, I am not really against it. I see how in many situations it will be more expressive than goto was. Maybe it’s time for goto to retire.

I for one welcome “Labeled break and continue” aka goto2. :)

MKV Video Bitrate

Getting bitrate used for video is easy using ffmpeg.

ffprobe -v error -select_streams v:0 -show_entries stream=bit_rate -of csv=p=0 "$FILENAME"

That is, unless the file is .mkv where this command will return one big fat N/A. Due to various intricacies of Matroska container, you need to do a few more steps.

First step would be to get duration. And that goes as vel as expected.

ffprobe -v error -show_entries format=duration -of csv=p=0 "$FILENAME"

Then we need to get the size of the video stream. We cannot do this directly since our stream is “chopped” into many pieces. But with the help of awk we can sum all those chunks.

ffprobe -v error -select_streams v:0 -show_entries packet=size -of csv=p=0 "$FILENAME" | awk '{sum += $1} END {print sum+0}'

With those two we have all building blocks to determine video (and just video) bandwidth.

STREAM_DURATION=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$FILENAME" | cut -d. -f1)
STREAM_BYTES=$(ffprobe -v error -select_streams v:0 -show_entries packet=size -of csv=p=0 "$FILENAME" | awk '{sum += $1} END {print sum+0}')
BIT_RATE=$((STREAM_BYTES * 8 / STREAM_DURATION))
echo "$((BIT_RATE / 1000))K"

Timing Time in Bash

As someone who uses bash all the time, I got used to $EPOCHSECONDS as a source of time. As a shell variable, it’s probably the fastest way to get that information. That said, getting down to using date is still something I occassionally do. It’s supported accross the shells and it’s still fast enough.

But, I started wondering - what is the speed difference? As an example I decided to use SECONDS % 60 as it’s not only getting seconds but also processing them a bit. I would say that is a representative usage of the time - not just reading but also adding some light arithmetic operation. So, I prepared a script to execute the following:

echo $EPOCHREALTIME
echo $((EPOCHSECONDS %60))
echo $EPOCHREALTIME
printf '%(%S)T\n'
echo $EPOCHREALTIME
echo $(date +%S)
echo $EPOCHREALTIME

On bash this will run my three equivalent examples of how to get a number of seconds and get their execution time in microseconds. Yes, echo here is bluring results a bit but it’s bluring them for all examples the same. For a quick test, it’s good enough. So, I placed this into a 100x loop and here are the results.

CommandAverageStDevRatio
echo $((EPOCHSECONDS %60))0.0000380.0000141.00
printf '%(%S)T\n'0.0001110.0000362.95
echo $(date +%S)0.0046540.000726123.78

As expected, using $EPOCHSECONDS is the fastest. I am slightly surprised that printf is 3x slower. I guess it makes sense because it is doing additional parsing, but I expected the result to be closer. But both of those are in microseconds on my laptop.

The only test to reach the milliseconds was date. Invoking date was 42x slower than printf and almost 124x slower than using the variable directly. Now, while this looks dreadful in comparison, in reality it’s not that bad as this doesn’t mean CPU was busy for the duration. But I find it still a bit worrying considering how often I used it myself.

But let’s be realistic - you are not echoing date command. You are probably just storing it into variable - what is the effect then?

echo $EPOCHREALTIME
TEST=$((EPOCHSECONDS %60))
echo $EPOCHREALTIME
printf -v TEST '%(%S)T'
echo $EPOCHREALTIME
TEST=$(date +%S)
echo $EPOCHREALTIME

Well, this run resulted in the following table.

CommandAverageStDevRatio
TEST=$((EPOCHSECONDS %60))0.0000180.0000071.00
printf -v TEST '%(%S)T'0.0000930.0000235.17
TEST=$(date +%S)0.0040760.000451226.58

Times did reduce a bit, especially for printf, but the ratios got even worse.

In the end, if you need to deal with a local time zone or you need a POSIX shell, date is still worth it. But, for bash-specific scripts, I will start paying a bit more attention to use EPOCHSECONDS when I can.

Trim on the Framework Expansion Cards

To handle trim on framework expansion cards, you can adjust udev rules:

cat << EOF | sudo tee /etc/udev/rules.d/42-framework-storage.rules
ACTION=="add|change", SUBSYSTEM=="scsi_disk", ATTRS{idVendor}=="13fe", ATTRS{idProduct}=="6500", ATTR{provisioning_mode}="unmap"
ACTION=="add|change", SUBSYSTEM=="scsi_disk", ATTRS{idVendor}=="32ac", ATTRS{idProduct}=="0005", ATTR{provisioning_mode}="unmap"
ACTION=="add|change", SUBSYSTEM=="scsi_disk", ATTRS{idVendor}=="32ac", ATTRS{idProduct}=="0010", ATTR{provisioning_mode}="unmap"
EOF

If you don’t want to restart, follow that with:

sudo udevadm control --reload-rules
sudo udevadm trigger

And that works.

However, under kernel 7.0 I noticed that unplugging and repluggin device would disable trim (as checked with lsblk -D). To be honest, this might have been happening under older kernel too and I just didn’t notice. Regardless, I needed a solution.

To cut a long story short, my solution was to manually trigger udevadm 2 seconds after USB device has been added. Not the most elegant solution, but it does work.

cat << EOF | sudo tee /etc/udev/rules.d/42-framework-storage.rules
ACTION=="add|change", SUBSYSTEM=="scsi_disk", ATTRS{idVendor}=="13fe", ATTRS{idProduct}=="6500", ATTR{provisioning_mode}="unmap"
ACTION=="add|change", SUBSYSTEM=="scsi_disk", ATTRS{idVendor}=="32ac", ATTRS{idProduct}=="0005", ATTR{provisioning_mode}="unmap"
ACTION=="add|change", SUBSYSTEM=="scsi_disk", ATTRS{idVendor}=="32ac", ATTRS{idProduct}=="0010", ATTR{provisioning_mode}="unmap"
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="32ac", ATTR{idProduct}=="0005", RUN+="/bin/sh -c 'sleep 2; udevadm trigger'"
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="32ac", ATTR{idProduct}=="0010", RUN+="/bin/sh -c 'sleep 2; udevadm trigger'"
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="13fe", ATTR{idProduct}=="6500", RUN+="/bin/sh -c 'sleep 2; udevadm trigger'"
EOF