What is a JSON feed? Learn more

JSON Feed Viewer

Browse through the showcased feeds, or enter a feed URL below.

Now supporting RSS and Atom feeds thanks to Andrew Chilton's feed2json.org service

CURRENT FEED

Geoff Taylor's Blog

technology, photography, travel and other interests

A feed by Geoff Taylor

JSON


Scriptable Sonos controller

Permalink - Posted on 2021-01-24 13:22

I created a Sonos controller for Scriptable. In addition to Scriptable (and a Sonos speaker), the Sonos HTTP API server must be running on a computer that’s on the same network as your Sonos system. See the GitHub repository for instructions, documentation and example scripts.


How to tell the difference between RAW and ProRAW

Permalink - Posted on 2021-01-11 21:59

If you’ve been shooting RAW photos on your iPhone for a few years, and now you’re shooting ProRAW, you probably have a mix of RAW and ProRAW images in the Photos app. How can you tell the difference? Both are DNG files, and the Photos app identifies both simply as “RAW.” Here’s a regular RAW photo in the Photos app.

And here’s a ProRAW photo.

There is a difference, however, as you can see in Halide’s photo viewer. The RAW file’s photometric interpretation is “CFA,” and the ProRAW file’s photometric interpretation is “Linear RAW.”

A RAW photo's attributes viewed in Halide.
A RAW photo's attributes viewed in Halide.
A ProRAW photo's attributes viewed in Halide.
A ProRAW photo's attributes viewed in Halide.

But if I’m in the Photos app, I don’t want to switch to Halide just to find out what kind of RAW file it is. I normally use ViewExif’s extension to inspect EXIF data in the Photos app, but it doesn’t show the photometric interpretation, so I decided to see if Shortcuts could help.

I started with a simple shortcut that gets a dictionary from an image.

The dictionary helpfully includes the photometric interpretation, but it’s a number.

Thanks to the ExifTool website, which includes a comprehensive list of EXIF tags, I learned that a photometric interpretation value of 34892 indicates a Linear RAW, and a value of 32803 indicates a CFA (Color Filter Array, or a regular RAW file).

I created a shortcut that performs these steps.

  1. Get an image via the share sheet.
  2. If the image file’s extension is not “DNG,” just display the extension in an alert.
  3. If the image file’s extension is “DNG,” get a dictionary from the image.
  4. Get the value of the dictionary’s “{TIFF}” key, which is another dictionary.
  5. From that second dictionary, get the value of the “PhotometricInterpretation” key.
  6. From that second dictionary, get the value of the “Make” key.
  7. If the photometric interpretation is 34892 and the make is “Apple,” display “ProRAW” in an alert.
  8. If the photometric interpretation is 34892 and the make is not “Apple,” display “Linear RAW” in an alert.
  9. If the photometric interpretation is 32803, display “RAW” in an alert.
  10. Otherwise display “Unknown” in an alert.

The logic is that, since all ProRAW files are Linear RAW, and ProRAW is a format used only by Apple cameras, a Linear RAW file created by an Apple camera is a ProRAW file. However, Linear RAW has been around longer than ProRAW, so your phone may hold Linear RAW files created by other devices or apps. The shortcut will simply display those as “Linear RAW.” Any CFA file is displayed as “RAW.” Since ProRAW photos and RAW photos taken with an iPhone are always DNG files, this only works for DNG files. If the file has any other extension, the shortcut will just report the extension as the image type. (You can download the shortcut here.)

This approach is admittedly a little naive. Changes to the photo metadata could break the shortcut, but at least for now, it works. This is what it looks like when you run the shortcut from the share sheet.

Running the shortcut with a ProRAW photo
Running the shortcut with a ProRAW photo
Running the shortcut with a RAW photo
Running the shortcut with a RAW photo

How do you tell the difference on a Mac? I recommend using ExifTool. After you’ve installed ExifTool, open Terminal and enter this command:

exiftool -S -PhotometricInterpretation -Make /path/to/file.dng

(Replace /path/to/file.dng with the path to a DNG file.) It will display either

PhotometricInterpretation: Color Filter ArrayMake: (Camera Make)

or

PhotometricInterpretation: Linear RawMake: (Camera Make)

where “(Camera Make)” is the name of the camera manufacturer.

You can use this information to determine if the DNG file is a ProRAW image, another type of Linear RAW image, or a regular RAW image. For example, for a ProRAW file, the command would display this output:

PhotometricInterpretation: Linear RawMake: Apple


Scripting the menu bar in macOS Big Sur

Permalink - Posted on 2020-12-13 15:22

This post explores some changes in Big Sur that affect UI scripting, specifically UI scripts that interact with the menu bar. I learned much of this while updating my Keyboard Maestro macro for Sidecar to work with Big Sur, so most of the information is presented in that context.

A brief introduction to UI scripting

UI (user interface) scripting is a really neat macOS feature that allows you to automate almost anything using AppleScript. Even if the app doesn’t expose a scripting interface, UI scripting works because it simulates button presses, keystrokes and other actions that you would perform if you were doing the task manually.

UI scripts use an application called System Events. You manipulate user interface elements by telling System Events to tell another process (application) to do something. For example, this code will click the Export Playlist… item in the Music app’s File menu:

tell application "System Events"  tell its process "Music"      click menu item "Export Playlist…" of menu "Library" of menu item "Library" of menu "File" of menu bar 1  end tellend tell

Often, the hardest part of writing a UI script is figuring out how to tell the process what to click. If you want the script to click a menu item, you usually can’t tell it to click the menu item directly; as in the example above, you have to give it the path from the menu bar down to the item you want it to click: click menu item "Export Playlist..." of menu "Library" of menu item "Library" of menu "File" of menu bar 1. (This example is actually written from the bottom up, but you can see that it includes the path from the menu bar — menu bar 1 — to the menu item Export Playlist...)

In addition to application menus, System Events can click system menus such as the volume menu and the Bluetooth menu. My Keyboard Maestro macro for Sidecar for Catalina and Big Sur uses a UI script to activate Sidecar via the Display menu. The original version of the macro broke under Big Sur, which is what led me down this rabbit hole.

Scripting the menu bar in Catalina

In Catalina, the system menus (Bluetooth, volume and so forth) in the menu bar are controlled by the process SystemUIServer. (SystemUIServer also controls the system menus in High Sierra and Mojave. I don’t have an OS older than High Sierra, so I don’t know if it was the same in older versions.)

You can see in this screenshot of Script Debugger running under Catalina that the element hierarchy for the Displays Menu is System Events > SystemUIServer > menu bar 1 > menu bar item 17. Using the description property shown in the screenshot, we can make that a little more intuitive, and not have to worry about finding the menu bar item number, by calling it menu bar item 1 of menu bar 1 whose description contains "Displays". There’s only one menu bar, so menu bar 1 always refers to the menu bar. There’s only one system menu called “Displays Menu,” so menu bar item 1 whose description contains "Displays" refers to the Displays Menu.

Script Debugger showing the Displays Menu in Catalina
Script Debugger showing the Displays Menu in Catalina

Because the Displays Menu hasn’t been clicked, its menu items aren’t visible, so Script Debugger can’t show you is the items in the Displays Menu. For that you need an app like UI Browser.

Using UI Browser, you can find the scriptable menu elements as you click the menu. In this case, I want to find “Geoff’s iPad” in the Displays Menu.

UI Browser showing menu path to iPad in Catalina
UI Browser showing menu path to iPad in Catalina

Once we know how to refer to the menu items, we can write the AppleScript code:

set deviceName to "Geoff's iPad"tell application "System Events"  tell process "SystemUIServer"    click (menu bar item 1 of menu bar 1 whose description contains "Displays")    set displayMenu to menu 1 of result    if ((menu item 1 where its name starts with deviceName) of displayMenu) exists then      -- Not connected, so click the name of the device to connect      click ((menu item 1 where its name starts with deviceName) of displayMenu)    else      -- Connected, so click "Disconnect" to disconnect      click ((menu item 1 where its name starts with "Disconnect") of displayMenu)    end if  end tellend tell

(You can use Script Editor, which is installed by default on every Mac, instead of Script Debugger, but it’s not as full-featured. Likewise, Accessibility Inspector is a less capable but free alternative to UI Browser. It’s included with Xcode, which is a free download from the Mac App Store.)

Changes in Big Sur

Big Sur introduced breaking changes to the system menus. The first change you can see in the screenshot below is that most of the system menus aren’t controlled by SystemUIServer. In Big Sur, they’re controlled by the ControlCenter process.

The SystemUIServer process in Big Sur
The SystemUIServer process in Big Sur
The ControlCenter process in Big Sur
The ControlCenter process in Big Sur

Second, there’s no Displays Menu. Like many system menu items, it’s part of the Control Center menu and not displayed in the menu bar by default. And it’s now called “Display” instead of “Displays Menu.”

Big Sur Control Center menu
Big Sur Control Center menu

You can move the Display menu to the menu bar by going to System Preferences > Dock & Menu Bar and setting the Display menu to always display in the menu bar.

System Preferences > Dock & Menu Bar > Display
System Preferences > Dock & Menu Bar > Display

And now the menu bar contains a Display menu that shows my iPad as a Sidecar device.

Big Sur Display menu
Big Sur Display menu

You would think that at this point, we should be able to script the Big Sur menu bar in roughly the same way we did in Catalina. But there’s yet another big change: The system menus aren’t menu elements, and menu items aren’t menu item elements. In Big Sur, system menus are application windows, and menu items are buttons or checkboxes that are nested in groups and scroll areas.

Sidecar Preferences is a button...
Sidecar Preferences is a button...
... and my iPad is a checkbox
... and my iPad is a checkbox

Much like you have to specify the full menu hierarchy to make the script click a menu item, you must give the script the full path to the desired button. A note here on the different ways certain elements are identified: “Control Center” is identified as a system dialog and a window. My iPad is identified as a toggle button and a checkbox. The names in parentheses after the element (window and checkbox in this case) are the names you use in the script.

Putting it all together

Now that we know how the menus work in Big Sur, how do we put it all together to control the menu with a script? Step one is to determine if the Display menu is present in the menu bar. (I couldn’t get it to work unless the Display menu was always shown in the menu bar.) To accomplish this part, I took the inelegant approach of initializing the variable displayMenu to an empty string (""). Then the script checks all the menu bar items for one whose name contains ‘Display’. If it finds the Display menu, displayMenu becomes a reference to the menu.

set deviceName to "Geoff's iPad" -- Change this to the name of your iPadset displayMenu to "" -- Do not change thistell application "System Events"  tell its application process "ControlCenter"    -- Get all menu bar items.    set menuBarItems to menu bar items of menu bar 1        -- Determine if the Display menu is in the menu bar.    repeat with mbi in menuBarItems      if name of mbi contains "Display" then        set displayMenu to mbi      end if    end repeat

The script clicks the Display menu, which allows the script to “view” the hierarchy of elements and find the checkbox with the same name as the deviceName variable. Then it clicks the checkbox, which activates Sidecar. (Admittedly, this section needs some error handling because the script will crash if the device isn’t in range of the Mac. But as long as the device is in range, it does the job.)

-- If the Display menu is in the menu bar, get the Sidecar device.    -- In Big Sur, it's a toggle button (checkbox) instead of a menu item.    if displayMenu is not equal to "" then      click displayMenu      set deviceToggle to checkbox 1 of scroll area 1 of group 1 of window "Control Center" whose title contains deviceName            -- Click the device.      click deviceToggle

There’s one more accommodation we need to make for Big Sur. In Catalina, activating Sidecar closes the Displays Menu. In Big Sur, the Display menu stays open after activating Sidecar. We can close the menu by simply clicking it again, except that the menu name changes to “Display, Sidecar on” when Sidecar is active.

Script Debugger showing Display menu when Sidecar is active
Script Debugger showing Display menu when Sidecar is active

The script doesn’t know about the menu called “Display, Sidecar on,” so we use this code to correctly identify the menu and click to close it:

-- The menu name changes when Sidecar is active, so we need to get the Display menu again, then click to close it.    set displayMenu to (first menu bar item whose name contains "Display") of menu bar 1    click displayMenu

Here’s the full script:

set deviceName to "Geoff's iPad" -- Change this to the name of your iPadset displayMenu to "" -- Do not change thistell application "System Events"  tell its application process "ControlCenter"    -- Get all menu bar items.    set menuBarItems to menu bar items of menu bar 1        -- Determine if the Display menu is in the menu bar.    repeat with mbi in menuBarItems      if name of mbi contains "Display" then        set displayMenu to mbi      end if    end repeat        -- If the Display menu is in the menu bar, get the Sidecar device.    -- In Big Sur, it's a toggle button (checkbox) instead of a menu item.    if displayMenu is not equal to "" then      click displayMenu      set deviceToggle to checkbox 1 of scroll area 1 of group 1 of window "Control Center" whose title contains deviceName            -- Click the device.      click deviceToggle            -- The menu name changes when Sidecar is active, so we need to get the Display menu again, then click to close it.      set displayMenu to (first menu bar item whose name contains "Display") of menu bar 1      click displayMenu    else      -- If the Display menu isn't in the menu bar, display an error message.      set errorMessage to "Display menu not found in menu bar. Open System Preferences > Dock & Menu Bar. Set Display to \"Show in Menu Bar > Always.\""      display dialog errorMessage with icon caution    end if  end tellend tell

The changes in Big Sur make it harder to properly identify the menu items for scripting, and Apple’s UI Scripting documentation — which is outdated and cursory — doesn’t help. Hopefully this post proves helpful in understanding the changes and adapting old scripts for Big Sur.


Keyboard Maestro, Sidecar and Big Sur

Permalink - Posted on 2020-12-05 00:58

It was recently brought to my attention that my Keyboard Maestro macro for Sidecar doesn’t work under Big Sur. I have Big Sur installed, but my main OS is still Catalina, so I hadn’t yet thought to try the macro under Big Sur.

After a couple of hours of debugging with Script Debugger and Accessibility Inspector – not to mention a good deal of trial and error – I discovered that Big Sur significantly changed the way the menu bar is exposed to AppleScript. (I’ll try to cover that in a separate post; it’s a bit too much to go into in this post.) I finally figured out how to access the right menu bar items with AppleScript and updated the macro’s script to work under Big Sur.

You can download the updated macro here or just create a macro with an Execute AppleScript action. Copy and paste this code into the action:

set deviceName to "The name of your iPad" -- Change this to the name of your iPadset displayMenu to "" -- Do not change thistell application "System Events"	tell its application process "ControlCenter"		-- Get all menu bar items.		set menuBarItems to menu bar items of menu bar 1				-- Determine if the Display menu is in the menu bar.		repeat with mbi in menuBarItems			if name of mbi contains "Display" then				set displayMenu to mbi			end if		end repeat				-- If the Display menu is in the menu bar, get the Sidecar device.		-- In Big Sur, it's a toggle button (checkbox) instead of a menu item.		if displayMenu is not equal to "" then			click displayMenu			set deviceToggle to checkbox 1 of scroll area 1 of group 1 of window "Control Center" whose title contains deviceName						-- Click the device.			click deviceToggle						-- The menu name changes when Sidecar is toggled, so we need to get the Display menu again, then click to close it.			set displayMenu to (first menu bar item whose name contains "Display") of menu bar 1			click displayMenu		else			-- If the Display menu isn't in the menu bar, display an error message.			set errorMessage to "Display menu not found in menu bar. Open System Preferences > Dock & Menu Bar. Set Display to \"Show in Menu Bar > Always.\""			display dialog errorMessage with icon caution		end if	end tellend tell

You need to change two things before you can use this code. First, change “The name of your iPad” (in the first line of the script) to the real name of your iPad (leave the quotation marks around the name). Second, open System Preferences > Dock & Menu Bar, and set the Display menu to always show in the menu bar.

Dock & Menu Bar Preferences
Dock & Menu Bar Preferences


PopClip extension: Backticks

Permalink - Posted on 2020-09-04 04:00

I made a new PopClip extension. It’s similar to the official Quotes or Brackets extension, except for programmers. It encloses the selected text within ` ` , ' ', " ", ( ), [ ], { } or < >. Download it here. View the source on GitHub.


PopClip extension for Choosy

Permalink - Posted on 2020-07-30 00:30

I made a PopClip extension for Choosy. Download it here. View the source on GitHub.


Job application websites

Permalink - Posted on 2020-07-11 04:00

I was looking around in my home directory and found a file from 2013. That was the year that I finished my MBA, and I was applying for new jobs — and judging by the content of the file, I was thoroughly frustrated with many companies’ job application websites. Here’s the list of annoyances I catalogued in that file.

Unhelpful error messages:

  • I filled out an application, attached my resume and cover letter, and clicked Submit. Result: “Server error. Contact the service desk.”
  • I filled out and submitted an application, and a popup dialog informed me that “Only a-z, A-Z and 0-9 are allowed.” It would’ve been helpful to tell me which of the many fields it was referring to.

Asking you to provide superfluous information:

  • “Please choose a username.” Why not just use the email address that I already provided?
  • “Two phone numbers are required.” How many people actually have two phone numbers?
  • “Create a unique identifier, for example the last four digits of your phone number followed by your zip code.” I imagine I could go to any major metropolitan area and find at least two people who have the same “unique” combination. Why can’t the email address I already provided serve as a unique identifier?

Not understanding that answering a question a certain way precludes you from answering a follow-up question:

  • “May we contact this employer? (Yes/No)” Since it was asking about my current employer, I selected “No” and left the contact information blank. Result: “Contact Name and Contact Phone are required.”
  • “Is this your current employer? (required)” I selected “Yes.” Result: “Reason for leaving” was required.

Asking you to provide the same information twice:

  • “Type or paste your resume in the space below (required).” Then, five screens later: “Attach your resume (required).”

And finally, this oddly worded question:

  • “To get an understanding of your salary expectations, I need to know what salary you are targeting for a new position. Some examples: My current rate is… / My last salary was… / I won’t work for less than… I need to understand if our expectations with respect to compensation are aligned before we move forward.”

I’m not sure why they wrote such a long, casually worded sentence when they could’ve just written “salary requirements.” It sounds like someone was dictating the question, and it was copied into the application verbatim.


Backing up Music playlists in macOS Catalina

Permalink - Posted on 2020-07-02 21:36

After one of my Music playlists disappeared from my Mac and my iPhone, I wanted a way to make automatic, scheduled backups of my playlists. On macOS Catalina, this turned out to be harder than I expected.

Catalina’s Music app allows you export a playlist as an XML file. If the playlist is ever deleted, you can import that XML file to recreate the playlist. But this is a manual, cumbersome process. You have to manually select each playlist, click File > Library > Export Playlist and — as long as you’re OK with the default file name and folder — click Save.

I thought I could automate this process with AppleScript, but the Music app’s AppleScript interface doesn’t provide an export capability. The logical fallback was UI (user interface) scripting via System Events, which allows you to script UI actions like clicking a menu. This came with its own challenges.

First, you have to know the correct names of the UI elements. Almost nothing has an intuitive name that you could grasp by visually inspecting the application window. First, I used this Keyboard Maestro macro to get a list of all of the UI elements. It outputs a plain text file, so I just searched that text file for the name of a playlist.

A portion of the output of the Keyboard Maestro macro:

application Process "Music"    window Music        splitter group 1            scroll area 1                outline 1                ...                    row 23                        UI element 1                            static text "80s"

Once I knew where the playlists were in the UI hierarchy, I used Script Debugger’s Explorer, which allows you to drill down from System Events to the relevant process (application) and from there to the application’s UI elements. Once you find the element you need, you can right-click it and select Copy Reference to copy an AppleScript code block that will provide access to that element. (At $99.99, Script Debugger is expensive, but there’s a free trial, and it offers many powerful features beyond the Explorer.)

(There are other ways to get this information, including Xcode’s Accessibility Inspector, which is free but not very intuitive.)

 
In Script Debugger, it’s easy to drill down to a specific playlist because the UI elements’ properties are displayed in the tree view.

There’s still some trial and error involved to distinguish the UI elements that can perform or accept actions from the elements that are just containers to navigate through. For example, to get the name of a playlist with System Events, you have to drill through System Events > process “Music” > window “Music” > splitter group 1 > scroll area 1 > outline 1 > row > UI element 1 > static text. The static text element contains a name property that contains the name of the playlist. (It also contains a value property that contains the same value. I don’t know if one or the other is the “correct” property in this case, but name worked for me.)

If you want to interact with the playlist, however, you refer to the row. But do you click it or select it? According to its definition, it responds to both. Finding the right one, select, was just trial and error.

 
A row responds to both click and select. Finding the right command is often trial and error.

After figuring out all of that, and doing a little more work, I had a working script that could export all of my playlists without any action on my part (other than running the script). Here’s the completed script (and GitHub repo if you’d rather see it there):

use AppleScript version "2.4" -- Yosemite (10.10) or lateruse scripting additionson pathExists(pathName)  (* Check if pathName is an existing file or folder.    First, try to get a reference pathName by using alias. If it succeeds, return true.    If it fails, try to get a reference to pathName as a POSIX path. If it succeeds, return true.    Else return false.  *)  try    pathName as alias    return true  on error    try      POSIX file pathName as alias      return true    on error      return false    end try  end tryend pathExistsset alertResult to display alert ¬  "Back up Music playlists now?" message "Quitting in 10 seconds..." buttons {"Yes", "No"} ¬  default button "Yes" giving up after 10set runBackup to button returned of alertResultset gaveUp to gave up of alertResultif runBackup = "No" or gaveUp then  error number -128end ifset backupFolder to "Playlist Backup" -- The name of the folder that will contain the backups, grouped in subfolders by dateset baseFolder to POSIX path of (path to documents folder) & backupFolder -- backupFolder is a subfolder of ~/Documentsset baseFolderPath to POSIX file baseFolder -- Get baseFolder's POSIX path so that we can concatenate it with the current dateset currentDate to do shell script "date +'%Y%m%d'" -- Get the current date in YYYYMMDD formatset saveFolder to POSIX path of baseFolderPath & "/" & currentDate -- The path to today's folderset baseFolderExists to pathExists(baseFolderPath) -- Check if baseFolder existsset saveFolderExists to pathExists(saveFolder) -- Check if saveFolder existsset the clipboard to saveFolder -- Put saveFolder's path on the clipboard so we can use it latertell application "Finder"  if baseFolderExists then    -- If baseFolder exists, check if saveFolder exists. If not, create saveFolder.    if saveFolderExists is false then set newSaveFolder to make new folder at baseFolderPath with properties {name:currentDate}  else    -- Else create baseFolder and saveFolder    set newBaseFolder to make new folder at (path to documents folder) with properties {name:backupFolder}    set newSaveFolder to make new folder at baseFolderPath with properties {name:currentDate}  end ifend telltell application "Music"  activate  set userPlaylists to (name of every user playlist whose name does not start with "Purchased" and smart is false) -- Get all user playlists except "Purchased on iPhone," etc. and smart playlistsend tell(* The Music app's AppleScipt interface doesn't offer a way to export playlists, so we have to script the UI using System Events.    This block iterates over the playlists in the Music app. When it finds a playlist with a name that matches userPlaylists,    it selects the playlist, clicks File > Library > Export Playlist, and saves the playlist as an XML file in saveFolder.    This uses the default file name, which is <playlist name>.xml.*)tell application "System Events"  tell its process "Music"    tell its window "Music"      tell its splitter group 1        tell its scroll area 1          tell its outline 1            repeat with thisRow in (every row)              tell thisRow                tell its UI element 1                  repeat with staticText in (every static text)                    if userPlaylists contains (name of staticText) then                      set selectedRow to select thisRow                      tell application "System Events"                        tell its process "Music"                          set focused of window "Music" to true                          delay 2                          click menu item "Export Playlist…" of menu "Library" of menu item "Library" of menu "File" of menu bar 1                          delay 2                          tell its window "Save"                            keystroke "G" using {command down, shift down} -- Shift-Cmd-G to change directories using the "Go to Folder" dialog                            delay 2                            keystroke (the clipboard) -- Paste saveFolder's location into the "Go to Folder" dialog                            delay 2                            keystroke return                            delay 2                            click button "Save"                            delay 1                          end tell                        end tell                      end tell                    end if                  end repeat                end tell              end tell            end repeat          end tell        end tell      end tell    end tell  end tellend telltell application "Music"  quitend tell

My next challenge was getting the script to run on a schedule. Since Keyboard Maestro can run AppleScripts, I first tried creating a macro with an “At time” trigger. That worked fine up until the tell application "System Events" block, which didn’t run. After doing some research, I concluded that a launch agent was probably the right approach. After more research, I concluded that manually creating the file wouldn’t be fun, so I bought LaunchControl. It made the task of creating the launch agent easy. I tested the launch agent, and everything worked. But I wanted it to run at night when I’m asleep, not while I’m using my computer. I ran another test when the display, but not the computer, was sleeping. This worked until it hit the tell application "System Events" block, then it stopped. Eventually I figured out that, while the script will run when the display is asleep, the Music app doesn’t get focus, and System Events can’t click the menus if Music isn’t the frontmost app. I found and tried several possible solutions, but nothing worked. I concluded that the script will only work if the display is awake.

The solution I settled on isn’t ideal, but at least now I can back up my playlists with almost no action on my part. I added a display alert line at the beginning of the script which asks if I want to run the backup job. If I click “No” or do nothing for 10 seconds, the script just quits. If I click “Yes,” the script runs, and I have to stop using the computer for several minutes so that the Music app stays in the foreground.

 
An prompt asks if you want to run the backup.

I changed the launch agent schedule to run at a time in the late afternoon when I’m likely to be using the computer. I no longer have to think about backing up my playlists; I just have to be in front of the computer at the right time so that I can click “Yes.”

It would be nice if Apple would update the scripting interface so that this could be done without UI scripting and thus happen completely in the background (or revert to storing playlists as XML files in the file system so that I could just include them in my regular backups). At any rate, I have a solution that requires almost no effort on my part, and that’s good enough for now.


Switching to Thunderbird from Apple Mail

Permalink - Posted on 2020-05-08 04:00

Thunderbird logo

A long time ago, and for many years, I used Thunderbird as my desktop email client. I don’t remember when I stopped using it. I know I wasn’t using it when I bought my MacBook Air in 2011, so it was roughly nine years ago, if not longer. My wife kept using Thunderbird, and every time I saw it, I wasn’t even sure it was under active development; it never seemed to change.

What I need from an email client

I tried newer email clients over the years, but settled on Apple Mail because it was simple and met all of my needs:

  • Hierarchical folders displayed in a sidebar
  • Rules to process mail as it’s received
  • Smart mailboxes to automatically organize mail
  • Presumably under active development

At some point during the years that I used Mail, I started using rules to flag financial emails (bills, bank statements and receipts). I got by with colors and flags, which are the only options available natively. In January, I decided I really didn’t like the color/flag system anymore. I really wanted tags, but Mail has no native tag feature. I searched for a solution and found a capable but expensive add-on called MailSuite. I liked the tagging capability added by MailSuite, but it costs $60, which I couldn’t justify spending just for tags. (It has other features, but I wasn’t using them.) There’s also the danger that, since it’s an add-on to a native Apple app, it could be rendered useless by future changes to Apple’s APIs.

Around the same time, I read this post on the Thunderbird blog and realized that Thunderbird was alive and well. It has native tags, so I decided to give it another try. I alternated between using Thunderbird and Apple Mail for a while, but before long I was using Thunderbird exclusively.

Thunderbird’s key features

I found that Thunderbird satisfies the four main requirements I listed above, and it has several other features that made it the clear winner.

Tags

Tags are the main feature that brought me back to Thunderbird. If I need to save an email for future reference (an airline reservation, for example), I’ll file it in a folder. But many emails don’t need to be saved permanently; they need attention temporarily, but not always immediately. I use tags and rules to categorize and triage those emails, and it’s much easier to read a tag than it is to remember what a color means.

Tags are created under Preferences > Display > Tags.

Thunderbird tag preferences
Thunderbird tag preferences

The one thing I don’t like about Thunderbird’s tags is that you can’t change the order of the tags, and the order determines the shortcut key for each tag. (There’s an 11-year-old bug for this very issue.)

Tags can be assigned to messages in a number of ways. You can right-click a message and select the tag from the Tag submenu.

Assigning a tag from the context menu
Tags can be assigned from a message's context menu

With the message selected, you can click the Tag button in the Mail Toolbar, or you can just press the keyboard shortcut for the tag. The shortcut for each tag is shown in the menu. (The shortcut key is determined by the tag’s position in the tag list.)

Assigning a tag with the Tag button
Tags can be assigned with the Tag button

In addition, message filters can automatically assign tags based on conditions that you define.

Assigning a tag with a message filter
Tags can be assigned automatically with message filters

You can also assign a color to each tag. Messages assigned a particular tag are displayed in that color. If you have the Tag column displayed in the message list pane, the message’s tags are displayed there.

A tagged message

If the Tag column isn’t displayed, right-click any column heading in the message list pane and select Tag from the menu.

Configuring the columns in the message list pane
Configuring the columns in the message list pane

Tags can be used in Saved Searches (Thunderbird’s analogue to Apple Mail’s Smart Mailboxes). I use this feature to show all of my financial emails (and only those emails) in one folder.

A Saved Search using tags
A Saved Search using tags

Privacy and security

Thunderbird provides a couple of useful privacy and security features by default. First, remote content in messages is blocked by default. Images and other content loaded from a remote server are a common way to track if a specific person has opened an email, as well as other information you may not want to share. (This isn’t limited to just the images you see; some emails contain very tiny images called tracking pixels.) Since Thunderbird blocks remote content by default, you’re protected from this tracking mechanism.

If you click the Preferences menu in any message where remote content was blocked, you’ll see the origin of all remote content in the message, as shown in the image below. From the menu, you can display the remote content in the message (Show remote content in this message), allow remote content from one or more of the originating domains (e.g., Allow remote content from https://cms.qz.com) or allow remote content from all of the sender’s messages (e.g., Allow remote content from hi@qz.com).

Thunderbird remote content preferences
Remote content preferences in a message

Although I wouldn’t recommend it, you can also allow all remote content by enabling the Allow remote content in messages setting under Preferences > Privacy.

Thunderbird privacy preferences
Thunderbird privacy preferences

I really like how this setting is presented. Both its wording and the fact that it’s disabled by default make it clear that this is an explicit opt-in setting. Even better, there’s a link that provides more detail about this setting and why it’s important to your privacy.

Apple Mail has the same feature, but I like Thunderbird’s functionality and presentation better. In Mail, it’s under the Viewing preferences. While that makes sense, I think framing it as a privacy setting is a better approach. Thunderbird also allows more detailed control over remote content. For example, Apple Mail loads either all or none of the remote content. You can’t allow it per origin or per sender as you can in Thunderbird.

The second feature is one that I haven’t seen in any other email client. If you click a link in an email, and the link text doesn’t match the actual link location, Thunderbird displays a warning and asks you which link you actually want to follow.

Link mismatch warning
Link mismatch warning

Preventing messages from being marked as read

There’s one other feature that I really like: I can prevent messages from being marked as read unless I specifically mark them as read. Sometimes I want to preview a message quickly and then come back to it later; in that case, I don’t want it marked as read. But Apple Mail marks a message as read immediately, even if you view it in the preview pane, and there’s no way to disable it. Thunderbird has an Automatically mark messages as read setting under Preferences > Display > Advanced. The setting can be disabled so that messages are never marked as read unless you mark them as read.

Apple Mail features that I miss (and a few workarounds)

It wasn’t a big change to switch to Thunderbird from Apple Mail; both are standard desktop email clients with the same basic features. However, there are a few things I miss about Apple Mail. Fortunately I was able to find workarounds for most of them. The workarounds aren’t necessarily elegant, but they get the job done. These are minor inconveniences that I’m willing to put up with because Thunderbird has really useful features that Apple Mail lacks.

Keyboard shortcuts

Apple Mail has the ⇧⌘R keyboard shortcut to mark a message as read or unread. Thunderbird doesn’t have a keyboard shortcut for this. While it would be possible to create a keyboard shortcut in macOS’s keyboard preferences, it would require two shortcuts: One for mark as read and one for mark as unread. (Update - July 18, 2020: Someone actually read this post and let me know that Thunderbird does have a keyboard shortcut to mark a message read or unread: M. And there are many other shortcuts that aren’t exposed in the menus.)

Instead, I used Keyboard Maestro so that I only have to remember one key combination (and it’s the same one I’ve been using for years in Apple Mail). If the As Read menu item is enabled, ⇧⌘R selects Thunderbird’s Message > Mark > As Read menu item. Otherwise it selects Message > Mark > As Unread.

Keyboard Maestro macro to mark a message as read/unread
Keyboard Maestro macro to mark a message as read/unread

I made a few other shortcuts just for convenience, rather than to replicate Mail’s functionality:

  • ⌥⌫ to mark a message as read and then delete it.
  • ⌥F to open Message Filters.
  • ⌥A to open Account Settings.

Message filters

Message filters, which are called rules in Apple Mail, are created per-account in Thunderbird, whereas in Apple Mail all rules work in all accounts. Even though I only have two filters at the moment, I didn’t want to recreate and maintain them for each email account, especially since that list of rules may grow over time. (For several reasons — including a couple of merchants who, in the year 2020, can’t successfully process an email address change — I’m monitoring multiple accounts for financial transactions.) Fortunately, there’s an extension called quickFilters that enables copying filters from one account to another. It would be better if Thunderbird just had native global filters, but the extension gets the job done.

User interface

Thunderbird still looks a lot like it did 10 years ago; its user interface hasn’t seen a lot of updates, and it isn’t as polished as Apple Mail’s. This is especially obvious if you use macOS in dark mode. Thunderbird doesn’t adjust its interface to match the operating system’s appearance. A dark theme can be enabled under Preferences > Extensions & Themes > Themes. However, some parts of interface, such as the preferences screen and context menus, don’t conform to the dark theme. Hopefully, since Thunderbird is actively developed now, the interface will get a refresh in the future.

Final thoughts

Minor inconveniences aside, I’ve been happily using Thunderbird for several months now, and I’m glad to see that it’s being actively developed and improved. It’s a solid, uncomplicated email client that does everything I need it to (with a few minor workarounds).


The way I wish all software subscriptions worked

Permalink - Posted on 2020-04-30 04:00

Due is a fantastic reminder app that I’ve been using for almost five years, according to the screenshot below. Due gets a lot of things right: It’s powerful, yet not overly complicated. The interface looks really nice. Automation is relatively easy thanks to its well-documented URL scheme. Add to that the most consumer-friendly subscription model I’ve seen to date.

I have mixed feelings about software subscriptions. Some are very valuable to me, and I pay for them without a second thought. Others I just can’t justify, no matter how much I like the app. When the developer of Due announced that it was moving to a subscription model, I wasn’t sure which category it would fall into. But then I read the blog post explaining how the subscription works. It’s actually something that I wish more apps would do. When you subscribe, you get access to all new features released during your one-year subscription period, in addition to all existing paid features that you haven’t already unlocked. If you choose to cancel the subscription, you get to keep all of the features that you paid for, and you still get free app updates (but not access to new features). And I think it’s reasonably priced at $4.99.

I really like what the developer has done with the app’s subscription screen. It tells you when you purchased Due, when your access to upgrades ended, which paid features you’ve unlocked, and which features you’ll unlock (forever) if you subscribe.

Due subscription screen
Due's subscription screen

I decided to subscribe for the improved Siri Shortcuts implementation. Knowing that I won’t lose any functionality if I decide to cancel certainly made the decision easier.


Use Keyboard Maestro to connect a Mac to an iPad via Sidecar

Permalink - Posted on 2020-03-13 22:39

Note: The macro described in this post works with Catalina. For a version that works with Big Sur, see this post.

If you use your iPad as a second display for your Mac via Sidecar, you can connect to it using a keyboard shortcut with Keyboard Maestro.

You can download the macro here or create it yourself using the following instructions. (If you download it, you’ll still need to set the device name as described below.)

Create a macro with an Execute AppleScript action. Copy and paste this code into the action:

set deviceName to "The name of your iPad"tell application "System Events"	tell process "SystemUIServer"		click (menu bar item 1 of menu bar 1 whose description contains "Displays")		set displaymenu to menu 1 of result		if ((menu item 1 where its name starts with deviceName) of displaymenu) exists then			-- Not connected, so click the name of the device to connect			click ((menu item 1 where its name starts with deviceName) of displaymenu)		else			-- Connected, so click "Disconnect" to disconnect			click ((menu item 1 where its name starts with "Disconnect") of displaymenu)		end if	end tellend tell

In the first line of the script, change "The name of your iPad" to the real name of your iPad (leave the quotation marks around the name). (You can find your iPad’s name in the AirPlay menu.)

  Name of device in AirPlay menu
Find your iPad’s name in the AirPlay menu

Set your desired hot key (I’m using ⌃ Key Pad +), and you’re all set. Press the hot key, and your Mac will connect to the iPad. Press the hot key again, and it will disconnect.

(You could probably connect to an Apple TV using the same script, but I don’t have an Apple TV, so I can’t verify it.)


JXA: How to use openForAccess

Permalink - Posted on 2020-01-29 01:35

Apple’s documentation shows that to open a file for writing in a JXA script, you call the openForAccess method on an Application instance. What the documentation doesn’t make clear is that you must call openForAccess on the instance returned by Application.currentApplication(). Anything else will fail.

If you run this code in Script Editor…

let app = Application('Finder');app.includeStandardAdditions = true;let desktop = app.pathTo('desktop').toString();let myFile = `${desktop}/temp.txt`;let openedFile = app.openForAccess(Path(myFile), { writePermission: true });app.closeAccess(openedFile);

… it produces an privilege violation error.

  Script Editor error message

Script Editor even helpfully tells you that the privilege violation occurred on the line where we attempted to open the file.

But if you change Application('Finder') to Application.currentApplication(), it works.

let app = Application.currentApplication();app.includeStandardAdditions = true;let desktop = app.pathTo('desktop').toString();let myFile = `${desktop}/temp.txt`;let openedFile = app.openForAccess(Path(myFile), { writePermission: true });app.closeAccess(openedFile);
  Script Editor no error message

(“Undefined” simply means the last statement didn’t return a value.)

If you need to open a file for writing, you must set a variable to Application.currentApplication() and call openForAccess on that Application instance — even if you’re already working with another application.


JXA: Find My Purchased Music

Permalink - Posted on 2020-01-24 22:52

I was looking at one of my playlists and wanted to know which songs I owned and which were from Apple Music, so I wrote a JXA (JavaScript for Automation) script. It works in macOS Catalina (10.15) and Mojave (10.14). Learn more and download the script from the GitHub repository.


Last light

Permalink - Posted on 2020-01-07 23:32

Last light
Last light


Southwest Georgia pines and sky

Permalink - Posted on 2020-01-07 17:29

Southwest Georgia pines and sky
Southwest Georgia pines and sky


Fallen trees, one year after Hurricane Michael

Permalink - Posted on 2019-12-26 23:48

Fallen trees, one year after Hurricane Michael
Fallen trees, one year after Hurricane Michael


Device Info Shortcut

Permalink - Posted on 2019-12-20 20:17

Shortcut icon

I recently discovered the app Toolbox Pro, which adds many powerful actions to Shortcuts. Many of the features are free, and an in-app purchase unlocks even more.

I created this Device Info shortcut that shows general information about your device along with information about its storage, battery, screen, audio and network connections. It requires the fully unlocked version of Toolbox Pro. The output is produced using Toolbox Pro’s Preview action, which takes a block of Markdown text and displays it as a web page (albeit a temporary web page, as there’s no way to save or share it).

  Shortcut output
The shortcut displays its output using Toolbox Pro's Preview action.

You can download the shortcut here: https://www.icloud.com/shortcuts/239e5f595d074cc6a00c89146822eace


Office skylight

Permalink - Posted on 2019-12-18 17:40

Office skylight
Office skylight


Candy sleigh

Permalink - Posted on 2019-12-18 13:53

Candy sleigh
Candy sleigh


Brown leaves

Permalink - Posted on 2019-11-29 21:33

Brown leaves
Brown leaves


bash: Open matching files in a specific application

Permalink - Posted on 2019-09-19 01:47

I needed to open a lot of JPG files in Pixelmator, so I wrote a bash function called openallin. It searches the current directory for files matching a pattern and opens them in the specified application. To open all JPG files in Pixelmator, the command would be openallin Pixelmator '*.jpg'. (This works on macOS. I don’t know if it’s possible on other Unix-like systems.)

Call the function with up to four arguments as shown below. The first two arguments are required. The third is required only if you want to pass the fourth (see explanation below), and the fourth is always optional.

openallin APPLICATION 'INCLUDE FILE PATTERN' [MAX DEPTH] ['EXCLUDE FILE PATTERN']

The function accepts four arguments:

  1. APPLICATION (required): The name of the application you want to open the files in.
  2. INCLUDE FILE PATTERN (required): A pattern that identifies the files you want to open. For example, ‘*.jpg’ matches all files with the extension “jpg.” This argument must be quoted so that the shell will interpret any wildcard characters as literal characters and not attempt to expand them.
  3. MAX DEPTH (optional): The function uses the find command, and this argument is passed to find as the -maxdepth option. find will by default search for matching files in the current directory and every directory below the current directory; maxdepth limits the search. If you don’t pass a third argument to openallin, the function uses “1” as the max depth and searches for files in the current directory only. If you want the function to search subdirectories, pass a number as the third argument. For example, “2” tells the function to look in the current directory and directories immediately under the current directory. If you don’t know how deep the search needs to go, just take a guess. The function will search down to the specified max depth or until it hits the last directory, whichever comes first. Just keep in mind that the greater the number, the deeper the search will go, and the more files it will try to open. Passing a really large number as this argument is probably a good way to crash your computer.
  4. EXCLUDE FILE PATTERN (optional): A pattern that identifies files you don’t want to open. This is only needed if you want to open a subset of the files matched by the include file pattern. For example, if I just want to open JPG files that don’t contain “thumbnail” in the file name (this was actually the scenario that prompted me to write the function), the command would be openallin Pixelmator '*.jpg' 1 '*thumbnail*'. Like INCLUDE FILE PATTERN, this argument must be quoted. bash functions only accept positional parameters, and the function expects EXCLUDE FILE PATTERN to be the fourth argument. If you want to pass an exclude file pattern, you must also pass a max depth. (If you don’t know what max depth to use, just pass ‘1’.)

To use the function, add the code below to your .bash_profile file.

1234567891011121314151617
openallin() {    if [ -z "$1" ] || [ -z "$2" ]; then        echo "Usage: openallin APPLICATION 'INCLUDE FILE PATTERN' [MAX DEPTH] ['EXCLUDE FILE PATTERN']"    else        if [ -z "$3" ]; then            maxdepth=1        else            maxdepth="$3"        fi        if [ -z "$4" ]; then            find . -type f -maxdepth "$maxdepth" -iname "$2" -exec open -a "$1" "{}" \;        else            find . -type f -maxdepth "$maxdepth" -iname "$2" ! -iname "$4" -exec open -a "$1" "{}" \;        fi    fi}


macOS Terminal trick: Open current directory in Finder

Permalink - Posted on 2019-09-04 20:51

A cool Terminal trick from Brett Terpstra: Add the line below to your .bash_profile file. Then when you type f in Terminal, and it will open the current directory in Finder.

f() { open -a "Finder" "${1-.}"; }

You can also pass a directory name to the command, and it will open that directory.


Quitting Time

Permalink - Posted on 2019-06-04 21:13

Before Bunch had the Quit Apps in… option, I created a bunch called “Quitting Time” to close all of the apps that I usually have open during the work day. I still get some satisfaction from clicking that one, especially on Fridays, so I kept it around even after Quit Apps in… was added.

  Bunch screenshot


Blue feather

Permalink - Posted on 2018-10-02 22:18

Blue feather
Blue feather


Airborne sunset

Permalink - Posted on 2018-09-13 02:07

Airborne sunset
Airborne sunset


Butterfly on flower

Permalink - Posted on 2018-08-26 23:04

Butterfly on flower
Butterfly on flower


Butterfly and pink flowers

Permalink - Posted on 2018-07-01 19:07

Butterfly and pink flowers
Butterfly and pink flowers


Raindrops on leaf

Permalink - Posted on 2018-06-17 13:28

Raindrops on leaf
Raindrops on leaf


Raspberry

Permalink - Posted on 2018-06-14 23:44

Raspberry
Raspberry


Sad flower. Nice colors, though.

Permalink - Posted on 2018-06-14 22:57

Sad flower. Nice colors, though.
Sad flower. Nice colors, though.


Rotary Telephone, L'Ouest Hôtel

Permalink - Posted on 2018-05-10 03:03

Rotary Telephone, L'Ouest Hôtel, Paris
Rotary Telephone, L'Ouest Hôtel, Paris


Keyboard Maestro: RSA Archer Deep Link

Permalink - Posted on 2018-03-18 20:35

This macro will only be of interest to RSA Archer users, but it’s pretty useful to me, so I thought I’d share it. The macro asks for the tracking ID of an Archer record and creates a deep link to the record.

In this example, I have the macro configured to create a link to a Finding, so I set the trigger to the string fdl. When that string is typed, the macro asks for the tracking ID of the record, constructs the link and pastes it into the application where I typed the trigger string.

In a deep link, Archer percent-encodes any character that isn’t alphanumeric. But I had trouble getting the Keyboard Maestro variables (since they contain percent signs) to play nicely with the percent-encoded URL. I solved this problem by setting the RecordLink variable to a path that’s not percent-encoded: /GenericContent/Record.aspx?id=%Variable%TrackingID%&moduleId=%Variable%ModuleID%. It contains the tokens for the TrackingID and ModuleID variables.

The Execute Shell Script action uses Python 3 to percent-encode the characters that Archer expects to be percent-encoded. (There are other ways to do this; I just used Python because it’s familiar to me.) You may need to update the path to Python in that action.

Here’s a screenshot of the Keyboard Mastro Editor:

Keyboard Maestro screen shot

Here it is in action:

Keyboard Maestro animated demo

You can download the macro here. Change the value of the ArcherURL variable to the URL of your Archer instance. (Don’t include a trailing slash.) Change the value of the ModuleID variable to the ID of the application you want to link to.


Email, Then and Now

Permalink - Posted on 2018-01-20 15:15

Sometime during the mid to late ’90s, I watched an episode of a TV show that I didn’t regularly watch. (Since I can’t even remember the name of the show, I’m guessing that’s the only episode I ever watched.) The only thing I remember about it is that one character gave another character a gift — printed copies of every email they had ever exchanged. Email was regarded as a temporary, passing trend — something that should be preserved on paper.

Now, in 2018, I delete dozens of emails on a daily basis, without a second thought (mainly newsletters, to be fair; I get very few personal emails these days). But ironically, I wish I had printed and saved my earliest emails, particularly the one I received from the CEO of Rickenbacker. In the mid ’90s, email was still was a novelty to most people, and I guess even CEOs received so little of it that personal replies were possible. But I didn’t foresee that I would go through a dozen or more email accounts in my lifetime, losing a little history each time I switched, and it never occurred to me to save or print those emails until I no longer had access to them.


Frozen Fountain

Permalink - Posted on 2018-01-07 23:59

Over several days of subfreezing temperatures, the fountain in the courtyard of my apartment building froze. The falling water wasn’t actually frozen, but it kind of looks that way in the picture.

  Frozen fountain
Frozen fountain
  Frozen fountain closeup
Frozen fountain closeup


Getting Old

Permalink - Posted on 2018-01-04 19:53

A sure sign that I’m getting old: I immediately downloaded Brighter and Bigger after reading this post by Dr. Drang.


Cleveland Marriott Downtown Lobby

Permalink - Posted on 2017-12-22 23:15

Cleveland Marriott Downtown Lobby
Cleveland Marriott Downtown Lobby