[Implemented in 2.814] Improving dock badge energy efficiency

Since it seems you have some experience with it, could you maybe try to whip up a JXA version of these?

Yeah it may be less performant in logic but who knows what the outcome would be, i mean unless there’s a solid reason we’d need to try it before we would definitely know right?

Hi all,

Like you, I was really interested in Dock badges. I've found this Swift command line project in this BTT feature request.

It returns all Dock icons that have a badge and their badge value in this format Mail:2 Franz:3 Message:1. I initially wanted to run this once and parse results in TouchBar in different buttons but can't find a way to do it.
So I've edited it a bit to add an optional app name parameter, so that if I run /Applications/Utils/dock-notification-count Mail it returns "2". If no notification, it returns "", therefore hiding the TouchBar button. It works really well.

It is also using Accessibility API but yet I didn't see any performance issue using it (I've watched Activity Monitor but I can't find any anormal CPU usage). I run each button update every 10sec.

It may help you doing what you want. And if you have an idea for parsing and making only one call, I may be interested :wink:

whoa, this sounds cool!

Could you try to put this setup in a seperate preset and post it here? how would this need to be setup?

very interesting!

I just explain quickly because I'm going to bed :stuck_out_tongue:

Basically I've built this command line tool (from linked project in my previous post, with few modifications) : dock-notification-count.zip (2.3 MB)

Opening it will launch Terminal and display all Applications from the Dock that have a notification badge, and their associated number.

But as I want a button per each app that could potentially have a badge, I've edited the code to allow parameter. It can be launched from command line like this : /Applications/Utils/dock-notification-count Mail (note that I've moved the command line utility in Applications/Utils which is a custom folder where I put utilities like this). And as it can be launched from command line, it therefore can be launched through BTT :

I have a widget like this one for the 3 apps I want to listen for notifications, and they are executed every 10 seconds.

It looks like this :

The icons are the original on which I've added a red dot to simulate the notification badge :wink:

I hope it's clear but don't hesitate for more information :slight_smile:

Interesting.. So do you have multiple copies of the exec for each app?

Also how did you compile it? and what's the code behind it too if you don't mind..?

I'm just wondering if there is a way to embed it entirely into a BTT preset so that users don't need to hassle with putting it in the right folder, etc.

Here is the modified source code : DockNotificationCount_CommandLine.zip (41.0 KB)
It is compiled through Xcode (Build > Archive).

I do… Didn't find a way to do another way. I thought of using update_touch_bar_widget (from Apps) but as I want a button for each app it doesn't seem doable : using update_touch_bar_widget I can set the correct icon but I will only be able to display one app at a time. And as I'm only interested in 3 apps, I think performances are not affected.

Just something that comes to my mind, we may be able to run the script (without parameter) as an Other action, and store it's result using set_string_variable and then read this for every button, and show the button if needed. Basically, if variable contains appName return show else return hide. May be better ?

I don't know about including an utility directly into a preset, but I'm pretty sure you do :stuck_out_tongue_winking_eye:

EDIT regarding my previous suggestion :

Sorry, I thought we could set up an Other action that runs script every X seconds but it's not the case. So I've added a TouchBar button to run this script. Here is the code :

Button to update badged apps :

tell application "BetterTouchTool"
	set_string_variable "notificationBadgeApps" to (do shell script "/Applications/Utils/dock-notification-count")
	return ""
end tell

For each app button :

tell application "BetterTouchTool"
	set notificationBadgeApps to get_string_variable "notificationBadgeApps"
end tell

if (notificationBadgeApps contains "Mail") then
	return true
else
	return ""
end if

It may use less resources, what do you think ?

Seems good. I haven’t seen any files but I see what you’re getting at here. I’ll need some time to check this out first then i’ll tell you what I think!

I see you have a helper widget (well thats what I call them) that sets the variable and then every other widget reads it. The code is much smaller than what me and @GoldenChaos use too (compare to his first post) so i’m guessing it’s more efficient? not sure here.

One thing though, can we return the number of notifications and any handoff badges? as it is what the current ones do.

I’m thinking we could either use text handling for each widget to get the corresponding number or we could have the helper widget write to a plist file which the others read. I’m not exactly sure which is more efficient, but I think the plist would be more efficient as its less of a process to get the corresponding number? not sure.

Also it’ll be great if you could check if handoff works. Currently if handoff becomes available, it’ll display something like “iPhone” which you can tap to use handoff.

good work here!

Is there a way to load this script into BTT so a user doesn't have to manually compile or download it, or have the functionality integrated into BTT somehow? Can we know if this is actually more energy efficient than the AppleScript method?

cc'ing @Andreas_Hegenberg since this seems promising and I want him to destroy my hopes and dreams

@GoldenChaos sure :smiley:: the app is doing the same as the compiled Apple Script (using the accessibility API), so unless Apple Script is buggy it shouldn't be much faster. However as it's starting a new process which is quitting immediately again it will not show a big CPU impact anywhere.

However I'm happy to add a function that executes this code directly in BTT and can be called using the BTT scripting interface, then you can do some more experiments :slight_smile:

1 Like

Wait, that's the opposite of crushing my dreams - this sounds like the solution! :open_mouth:

Let me know when it's in BTT and I'll proceed rewriting all the badge scripts. We'll see what happens when they're all turned on by default :smiley:

What no! I just said it should not be much faster than your existing solution - it just hides the CPU usage better :smiley:

But it sounds like it could get all the dock badges at once, or does that one call just end up taking all the CPU that the separate calls would have?

In the end it's also just looping over all of the items, that you could do with your existing script as well.

But let's see. I'll add it, then we can do some more tests :slight_smile:

Ahhh, I see now! Sorry, I was confused. Standing by to test inside BTT! :smile:

1 Like

I think it could be more efficient since the script is ran only once every N seconds, instead of having to do it for each button (like initial Apple Script).

I've made some performances experiments with top -pid $( /Applications/Utils/dock-notification-count > /dev/null & echo $! ) but can't really compare it to previous implementation :

Load Avg: 1.51, 1.91, 2.13  CPU usage: 3.53% user, 3.18% sys, 93.28% idle  SharedLibs: 228M resident, 45M data, 28M linkedit.
MemRegions: 151217 total, 5093M resident, 155M private, 2303M shared. PhysMem: 14G used (3177M wired), 1755M unused.
VM: 2662G vsize, 1298M framework vsize, 3739548(0) swapins, 4244583(0) swapouts.  Networks: packets: 1110609/1837M in, 1670440/1639M out.
Disks: 3592108/56G read, 1569031/39G written.

By the way, @GoldenChaos, when you said "they notoriously hog energy", how were you able to tell it ? By comparing BTT script usage from Activity Monitor with and without script enabled ?

1 Like

When dock badges were first implemented, they were all enabled by default. Users (including myself) suddenly noticed that our systems were getting one hour of battery life and constantly running at ~20-40% CPU at ridiculous temperatures. @Harrumph made a great post with CPU stats to verify: GoldenChaos-BTT Support and Feedback Thread

The underlying code for dock badges hasn't changed since then. We were unable to find a true solution, all we could do is turn on as few dock badges as possible by default, which led to the current default setup of apple-only apps being enabled.

The holy grail, naturally, is being able to support enabling all dock badges at once by default, eliminating the need for user configuration.

Oh okay I see, indeed it was huuuuge CPU and impact on battery !

So after two days of using it, I can confirm that I don't see any negative impact with my implementation. Still have ~8hours battery life, and CPU absolutely not affected (at least < 10% when doing nothing CPU-consuming).

Even if I add a TouchBar button for every app in my Dock, I'm pretty sure it won't affect performances as the only script running in these buttons is fetching the value from the BTT variable.

Could you try actually doing that (enabling every app) and see what happens? I'm curious...

I think it'll be less too, as only one script looks through the whole dock once and spits out a list which we can store somewhere.

Currently every badge digs into there every second, each making a new (request? i don't have professional knowledge here), added the very low efficiency of applescript too.

@AnthoPak, do you have the seperate exec method setup now or did you figure out how to use the one that spits out a list?

@GoldenChaos I've made the try creating a button for each app in my Dock, and as expected, I don't see any difference. Every new button only executes following script, which can't use too much CPU as it only fetch a variable value :

tell application "BetterTouchTool"
	set notificationBadgeApps to get_string_variable "notificationBadgeApps"
end tell

if (notificationBadgeApps contains "Mail") then
	return true
else
	return ""
end if

@yuuiko I'm currently using the implementation I've posted in #22. This is only calling the helper once, and then every button fetch the last response from the helper. So no need to re-run the helper for each button, therefore no performance issues.
The only thing I regret is not having the ability to create buttons on the fly based on helper response, which could be even better. In current implem, I have to create a button for every Dock app, and each one has to update every 10 seconds to see if app name is contained in saved variable.

BUT ! Something just came to my mind (again…), I could use the helper widget, and without saving the result in a var, use update_touch_bar_widget from BTT API to change buttons text, and therefore changing their visibility. This would avoid having each button updates every 10 seconds. I may try this when having some time :slight_smile:

1 Like