[Implemented in 2.814] Improving dock badge energy efficiency

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 https://docs.bettertouchtool.net/docs/apple_script.html) 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
	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
	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

Interesting. I guess we’re all just waiting for @Andreas_Hegenberg :wink:

Will try myself sometime soon too.

i’m also eyeing the add_new_trigger function in btt’s documents :thinking:

Okay, I've finally found some time and implemented my above suggestion :smiley:

Works really well ! Only one script for an infinite number of apps :wink:
I even tried setting the refreshing time to 0.1s and don't see any performance issue, no difference in CPU usage :smiley: But of course 0.1 is useless, I'll stick with ~2s I think.
(EDIT : in fact i've just noticed that with 0.1s I got BetterTouchToolAppleScriptRunner going to ~5% CPU, whereas it almost always < 1% with 2s. But this performance change may mostly be related to widget update)

So basically, here is the process :

  • Create a widget for each app we want (in fact a button could be sufficient, but update_touch_bar_widget is, as it name suggests, made for widgets and not for classic buttons). I've set the refresh time of these widgets to 10000 (as I don't want them to refresh themselves)
  • Copy the UDID for every button, and add it to following helper widget script
  • Create the AppleScript helper widget that will update each button when needed, and this script :

UpdateDockBadgesButtons.scpt :

set udidRecords to {Mail:"EE7C0AF6-8406-4278-879F-593CFBCD1000", Messages:"0ECB327C-0CC9-4C12-B300-1BC7210F74B9", Franz:"7E02811B-73E3-458E-967A-9DFD11D101DB"} --All UDIDs from BTT widgets for apps that you are interested in notifications badges

set notificationBadgeApps to (do shell script "/Applications/Utils/dock-notification-count")

-- Can't make a loop here since I can't find a way to fetch records from udidRecords with variable…
checkAndUpdateCurrentBadgeStatusForApp("Mail", notificationBadgeApps, (get Mail of udidRecords))
checkAndUpdateCurrentBadgeStatusForApp("Messages", notificationBadgeApps, (get Messages of udidRecords))
checkAndUpdateCurrentBadgeStatusForApp("Franz", notificationBadgeApps, (get Franz of udidRecords))

return "" --hidden helper widget

on checkAndUpdateCurrentBadgeStatusForApp(appName, notificationBadgeApps, udid)
	if notificationBadgeApps contains appName then
		updateButtonWithUDID(udid, "show") --here we can try fetching badges count from notificationBadgeApps if we are interested in getting badge count, and replace true with count
		updateButtonWithUDID(udid, "") --hide button
	end if
end checkAndUpdateCurrentBadgeStatusForApp

on updateButtonWithUDID(udidToUpdate, newText)
	tell application "BetterTouchTool"
		update_touch_bar_widget udidToUpdate text newText
	end tell
end updateButtonWithUDID

Still, creating buttons on the fly will be even better so we won't have to deal with UDIDs, but I've also looked into add_new_trigger and I don't think it could create a button. This could definitely be a great new feature.

1 Like

Personally I don't mind making a bunch of badges for every app, though being able to create and remove "virtual" buttons on-the-fly is something I've wanted for a while and would make the reminders/calendar/browser tabs groups behave a lot nicer. I might make a separate thread to request this feature, I know it's come up in the past and add_new_trigger isn't a good option for this because it creates permanent buttons.

@Andreas_Hegenberg any updates on this? :slight_smile: