Returning JSON for Custom Touchbar widget

Hi,

I'm writing a custom widget that display my CPU history in the touch bar.

Unfortunately I can't figure how to update the widget icon every time the script is called.

My script generates an image with the history (of size 40x44) and returns a json that looks like this

"{
"icon_path":"/tmp/cpulogimage.heic",
"background_color": "255,85,100,255",
"font_color": "100,200,100,255",
"font_size": 10
}"

I can tell the script is executed periodically and works as intended but nothing appears in the touch bar button.

I tried returning escaped quotes, I tried returning the other fields as well but it does not seem to do a difference, the result is the same: nothing appears in the touch bar

Here is a screenshot of the touchbar, the big blank button is supposed to host the image I generate. I do not know why it is so large

Any help is welcome.

I have no idea whether the macOS APIs support heic images. Have you tried a different format? Escaping would definitely be necessary

I tried JPG and I tried escaping the path as well

\"{
\"icon_path":\"\/tmp\/cpulogimage.jpg\",
\"background_color\": \"255,85,100,255\",
\"font_color\": \"100,200,100,255\",
\"font_size\": 10
}\"

No change.

Here is the configuration I'm using, maybe I'm doing something wrong here?

In your escaped above one slash is missing for the icon path:

return "{\"icon_path\":\"\/tmp\/cpulogimage.jpg\",
\"background_color\": \"255,85,100,255\",
\"font_color\": \"100,200,100,255\",
\"font_size\": 10}"

nice catch but that didn't fix it

Just edited it again due to a copy & paste error I first made.

I copied your string and this is what I got, touchbar is still empty

55

Ah one more thing I just noticed is the extra slashes in the path. "/" doesn't need to be escaped:

return "{\"icon_path\":\"/tmp/cpulogimage.heic\",\"background_color\": \"255,85,100,255\",\"font_color\": \"100,200,100,255\",\"font_size\": 10}"

In general it's a good idea to use some tool like https://www.freeformatter.com/java-dotnet-escape.html#ad-output to do the escaping

No change, I even tried a reboot with no luck.

Could this be documented somewhere?

ps: Why are the escaping quotes even necessary? I can't even use most JSON serializers/deserializers because they don't escape quotes

Maybe post your complete example or try with something easy first.
For example:

echo "{\"text\":\"test text\", \"font_color\": \"100,200,100,255\"}"

It needs to be escaped because it's just a string. Every string escape tool should work fine.
Are you using the Apple Script or Shell Script widget?

Your widget is probably big because it is displaying a error message thrown by your command, you can decrease the font size of the widget to see it.

I finally figured it out and here is everything I learned and wished I knew 2 hours ago:

  1. The text field is mandatory, BTT will not display anything if the text field is absent or empty
  2. Quotes don't need to be escaped. At least not for the output. Depending on your programming language you might need to escape them if you enter a string as input but nobody should do that and everybody should use a JSON library
  3. heic is supported
  4. If BTT fail to parse the json then the button is huge, I'm guessing this is BTT itself returning an error and trying to fit the parsing error in the button
  5. Slashes in file path can be escaped or not, both "icon_path":"\/tmp\/cpulogimage.heic" and "icon_path":"/tmp/cpulogimage.heic" work
  6. Font color is mandatory otherwise the text is black
  7. Everything else is optional.
  8. extra credit: icon can't be bigger than 44x44

Here is a screenshot of it working using a zero-width space as text

Could you (or someone else) please document the JSON format thoroughly somewhere? I couldn't find anything and all those strange behaviors are extremely off-putting

Here is the whole source as well as explanations on how to install it https://github.com/andrevidela/CPU-Activity-Touchbar

Hi, I tried everything but can't seem to get the icon in the touchbar. All others work. It is for Chrome, and I use te following code:

tell application "Google Chrome"
set i to 1 as integer
set j to 15 #length of button
set activeColor to "128,128,128,255"
set normalColor to "220,220,220,175"
set testTabName to ( get title of tab i of ( first window whose index is 1))
set stringLength to length of testTabName
set tabIcon to execute of tab i of ( first window whose index is 1) javascript "document.head.querySelector('link[rel~=icon]').href;"

#set tabIcon to "https://web.whatsapp.com/img/f01_78cd893586564a736b94f919cd2851f7.png"

if stringLength < j then
set tempString to testTabName as string
else
set tempString to characters 1 thru (j - 3) of testTabName as string
set tempString to tempString & "..."
end if
set activeTabIndex to active tab index of ( first window whose index is 1) as integer

if activeTabIndex = i then
set tempString to "{"text":"" & tempString & "", "background_color": "" & activeColor & "", "font_color": "255,255,255,255", "icon_data": "base64_icon_data", "icon_path":"" & tabIcon & ""}"
else
set tempString to "{"text":"" & tempString & "", "background_color": "" & normalColor & "", "font_color": "0,0,0,0"}"
end if

return tempString

#return "{"text":"Temp", "background_color": "255,85,100,255", "font_color": "100,200,100,255", "font_size": 10, "icon_path":"" & tabIcon & "",}"
end tell

There are basically 2 lines to consider:

set tabIcon to execute of tab i of ( first window whose index is 1) javascript "document.head.querySelector('link[rel~=icon]').href;"
set tempString to "{"text":"" & tempString & "", "background_color": "" & activeColor & "", "font_color": "255,255,255,255", "icon_data": "base64_icon_data", "icon_path":"" & tabIcon & ""}"

Could you help me?

Kind regards,
Robert

UPDATE.

I used the following to check my code: Change button background color if...

This does work. I converted this to my code and the moment I change the location of the icon to the website-location of the icon it stops working.

So I am a step further. I will keep this discussion up to date for others with identical problems.

tell application "Google Chrome"
set i to 1 as integer
set j to 15 #length of button
set activeColor to "128,128,128,255"
set normalColor to "220,220,220,175"

set tabName to (get title of tab i of (first window whose index is 1))
set stringLength to length of tabName

set tabIcon to execute of tab i of (first window whose index is 1) javascript "document.head.querySelector('link[rel~=icon]').href;"
#set tabIcon to "https://web.whatsapp.com/img/f01_78cd893586564a736b94f919cd2851f7.png"

if stringLength < j then
	set titleName to tabName as string
else
	set titleName to (characters 1 thru (j - 3) of tabName as string) & "..."
	#set titleName to titleName & "..."
end if

set activeTabIndex to active tab index of (first window whose index is 1) as integer
if activeTabIndex = i then
	set titleString to "\"text\":\"" & titleName & "\""
	#set iconString to "\"icon_path\":\"~/Desktop/battery/bCharging.png\""
	set iconString to "\"icon_path\":\"" & tabIcon & "\""
	set fontColor to "\"font_color\":\"255,255,255,255\""
	set backgroundColor to "\"background_color\": \"" & activeColor & "\""
	return "{" & titleString & "," & backgroundColor & "," & fontColor & "," & iconString & "}"
	#set tempString to "{\"text\":\"" & titleName & "\", \"background_color\": \"" & activeColor & "\", \"font_color\": \"255,255,255,255\", \"icon_data\": \"base64_icon_data\", \"icon_path\":\"" & tabIcon & "\"}"
else
	return "{\"text\":\"" & titleName & "\", \"background_color\": \"" & normalColor & "\", \"font_color\": \"0,0,0,0\"}"
end if

#return tempString
#return "{\"text\":\"Temp\", \"background_color\": \"255,85,100,255\", \"font_color\": \"100,200,100,255\",  \"font_size\": 10, \"icon_path\":\"" & tabIcon & "\",}"
	
end tell

EDIT: downloading the icon from the link and using it with same pathway as in the example works. So the icon itself is not the problem. Conclusion: The problem is with the icon-link directly to a web-address.

@Andreas_Hegenberg, can you explain to me why it is not possible to use a website-url as icon_path?
In my example I use this favicon url: https://s.ytimg.com/yts/img/favicon-vfl8qSV2F.ico

This does not return anything.

because there is currently no code that would load the data from that url :slight_smile:
Might be a good feature but is more complicated than it looks (caching, error handling etc.)

ok, thanks for the quick response

I found a workaround for this. I use the following function:

do shell script "curl " & tabIcon & " -o " & tabIconPwd

This downloads the icon to a location on my local pc. Next I use this location as a reference for BTT. This works like a charm.

I am having a hard time getting JSON output from a Shortcuts to be recognised and displayed correctly. It does not resolve the JSON, but only displays the raw JSON text.

Even with a very simple Shortcut that returns the value of a Text object containing:

{\"text\":\"test text\", \"font_color\": \"100,200,100,255\"}

It just displays the raw text on the Touch Bar. I guess I must be missing some settings to activate.

Has anyone got JSON from a ShortCut working? If so can you paste an example of the text the Shortcut is returning. Thanks in advance.
Thanks.