I heard your mom likes social media

There’s a tendency among tech writers to use “Mom” as the prototypical average user. This often raises complaints from people who claim that it’s sexist (never ageist, though—funny, that). I have no doubt that in some cases sexism is at the root of it, especially when the comments are mean-spirited or make Mom out to be clueless. But I’ve seen Mom invoked often enough by women tech writers to know that that’s not the whole story.

I believe Mom comes to mind when tech writers need an Every(wo)man user because that’s who, in their own personal experience, best matches the profile. Certainly in my circle of friends—which consists primarily of Mom- and Dad-aged people—it’s the Mom who’s most likely to have an active Facebook account, and to be on Twitter, Pinterest, and Instagram. In years past, it was Mom who first started texting.

The natural consequence of this is that it’s Mom who’s most likely to get crosswise with bad interface design, poorly written dialog boxes, and devious privacy policies—hence the stories using her as the example. Dad sits on his ass in blissful ignorance.1

So most stories about Mom’s difficulties shouldn’t be considered demeaning. She is, as Teddy Roosevelt would say, in the arena, making mistakes because there is no effort without error and shortcoming. Especially when dealing with Facebook.


  1. Dad does show up in one common tech-related story, though. You know the “racist uncle” who forwards those atrocious emails? That’s certainly someone’s dad, and is probably Dad himself, hidden behind his brother to avoid embarrassment. 


More AnyBar

If you’ve been reading this blog for any length of time, you won’t be surprised to hear that I’ve made some changes to the AnyBar/SuperDuper setup I talked about a couple of days ago. I think the new system is more robust than my first stab at it.

But before I get to that, I want to mention a couple of other menubar-related items. First, in response to a question/request by T.J. Luoma, Brett Terpstra has made a fork of AnyBar that allows text to be displayed in the menubar in addition to or instead of the little graphics that Nikita Prokopov’s original is limited to. With Brett’s version, you can have

T.J.’s a cagy old bird. He knew that acting helpless within earshot would have Brett leaping to his rescue. When I saw T.J.’s tweet, I thought Brett would have a solution before the end of the day. But Brett’s slowing down—it wasn’t ready until the following morning.

Unbeknownst to all of us, though, was TextBar a $3 app from Rich Somerfield that already does what T.J. wants. It doesn’t display graphics, but with Emoji and the rest of Unicode, I doubt that’s a significant limitation. TextBar seems to have a simpler user interface, especially when you want to display more than one signal in your menubar, but it’s meant to be used only for signals that are updated regularly. AnyBar is more flexible, but that flexibility comes with reduced simplicity.

TextBar Preferences

With those announcements out of the way, let’s talk about the changes I made to my AnyBar/SuperDuper setup. As you may recall, the idea is to have an image in the menubar that tells me whether my most recent backup was successful or not. Originally, I set one of SuperDuper’s advanced options to run a script upon completion that would set the signal image. I decided this wasn’t the best way to go about it. If SuperDuper gets hung up at some point—which can happen—I have no guarantee that the signalling script will run. So I changed things to have the script run independently of SuperDuper via launchd, OS X’s system for automatically running programs.

Because I use several Launch Agents, I popped for LaunchControl by soma-zone to manage them. If you need to manage only one or two, you can probably get by with the command-line tool, launchctl, that Apple provides. Soma-zone has written a nice introduction to both launchctl and the whole launchd system.

The automatic running of the signalling script, sdsignal, by launchd is configured through the plist file com.leancrew.sdsignal.plist, which is saved in my ~/Library/LaunchAgents folder. By saving it here, it gets loaded (but not necessarily run) whenever I log in. It can also be loaded manually by running

launchctl load ~/Library/LaunchAgents/com.leancrew.sdsignal.plist

from the Terminal.

The configuration file is set to run sdsignal at 5:56 AM on Tuesday through Saturday. These days were chosen because I have SuperDuper do its backup every weekday evening. There are no backups on Saturday or Sunday night, so there’s no need to run sdsignal on Sunday or Monday morning. Here’s the complete plist file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.leancrew.sdsignal</string>
  <key>Program</key>
  <string>/Users/drdrang/Dropbox/bin/sdsignal</string>
  <key>StartCalendarInterval</key>
  <array>
    <dict>
      <key>Hour</key>
      <integer>5</integer>
      <key>Minute</key>
      <integer>56</integer>
      <key>Weekday</key>
      <integer>2</integer>
    </dict>
    <dict>
      <key>Hour</key>
      <integer>5</integer>
      <key>Minute</key>
      <integer>56</integer>
      <key>Weekday</key>
      <integer>3</integer>
    </dict>
    <dict>
      <key>Hour</key>
      <integer>5</integer>
      <key>Minute</key>
      <integer>56</integer>
      <key>Weekday</key>
      <integer>4</integer>
    </dict>
    <dict>
      <key>Hour</key>
      <integer>5</integer>
      <key>Minute</key>
      <integer>56</integer>
      <key>Weekday</key>
      <integer>5</integer>
    </dict>
    <dict>
      <key>Hour</key>
      <integer>5</integer>
      <key>Minute</key>
      <integer>56</integer>
      <key>Weekday</key>
      <integer>6</integer>
    </dict>
  </array>
</dict>
</plist>

You’d think the plist format would allow you to set day ranges, but no. You need to create a separate <dict> entry for each day. While this is not a particularly complicated plist file, I can’t take credit for writing it. LaunchControl wrote it for me after I filled in a couple of fields.

In addition to changing how sdsignal is invoked, I’ve changed the way it works, too. Here’s the new version:

python:
 1:  #!/usr/bin/python
 2:  
 3:  import os
 4:  import socket
 5:  from datetime import date, timedelta
 6:  
 7:  # This script is expected to be run on the day after a backup. If it's
 8:  # run on the day of a backup or more than one day after a backup, it
 9:  # will indicate a failed backup even if the backup was successful.
10:  
11:  # AnyBar communication info.
12:  abhost = '127.0.0.1'      # localhost IP number
13:  abport = 1738             # default AnyBar port
14:  
15:  # Where the SuperDuper! log files are.
16:  logdir = (os.environ["HOME"] +
17:            "/Library/Application Support/" +
18:            "SuperDuper!/Scheduled Copies/" +
19:            "Smart Update Backup from Macintosh HD.sdsp/Logs/")
20:  
21:  # Get the last log file.
22:  logfiles = [x for x in os.listdir(logdir) if x[-5:] == 'sdlog']
23:  logfiles.sort()
24:  lastlog = logdir + logfiles[-1]
25:  
26:  # Get yesterday's date in the format SuperDuper! uses in its log file.
27:  y = date.today() + timedelta(days=-1)
28:  ystr = y.strftime('%a, %b %-e, %Y')   # %-e is the day of month w/o space
29:  
30:  # Look for the correct "Started on" line.
31:  lastnight = False
32:  with open(lastlog) as f:
33:    for line in f:
34:      if ('| Info | Started on %s at' % ystr) in line:
35:        lastnight = True
36:        break
37:  
38:  # If the date is right, look for the "Copy complete" line.
39:  good = False
40:  if lastnight:
41:    with open(lastlog) as f:
42:      for line in f:
43:        if "| Info | Copy complete." in line:
44:          good = True
45:          break
46:  
47:  # At this point, good is True if the log file has the right date and
48:  # SuperDuper finished successfully.
49:  
50:  # Send AnyBar the black or red signal depending on whether the backup worked.
51:  # AF_INET is for IPv4 and SOCK_DGRAM is for UDP.
52:  anybar = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
53:  anybar.connect((abhost, abport))
54:  if good:
55:    anybar.send('black')
56:  else:
57:    anybar.send('red')

The biggest differences are:

  1. It now looks for the date on which the last SuperDuper backup was run and sends the failure signal if that date was not the day before sdsignal is run. This is in keeping with how SuperDuper and sdsignal are scheduled. It also, as before, sends a failure signal if SuperDuper’s log file indicates that the backup never finished.
  2. It now uses a black dot to indicate success instead of a green dot. I decided a black dot was less obtrusive and more indicative of “normal” in the menubar.

This is not a particularly exciting example of automation, mainly because SuperDuper is so reliable. From my experience, it’ll be a long time before I see a red dot in my menubar.


AnyBar and SuperDuper!

For years I’ve been using a combination of this script (or its predecessor) and GeekTool to let me know whether my nightly SuperDuper! backup was successful. It works fine, printing a short summary of the backup log file onto the upper left corner of my Desktop screen.

Old SuperDuper summary

I’ve always thought, though, that there’s a better, less obtrusive way to let me know if the backup failed.1

Today, One Thing Well posted an article on a utility called AnyBar, written by Nikita Prokopov, that lets you put a colored dot in your menubar by sending your computer a UDP message. As soon as I read the article, I knew I could make some use of it.

There has to be something I can use this for: github.com/tonsky/AnyBar
Dr. Drang (@drdrang) Apr 7 2015 12:54 PM

Justin Scholz rang the bell:

@drdrang hey cool, will be my new “my custom backup scripts” indicator light ;-)
Justin Scholz (@JMoVS) Apr 7 2015 1:12 PM

Instead of putting a bunch of words on my Desktop, I could just put a colored dot in my menubar—green for a successful backup, red for a failure.

It turned out to be pretty simple. Although I know next to nothing about compiling Mac programs, I downloaded and unzipped the AnyBar repository, opened it in Xcode, and built the app without incident. I moved it into my /Applications directory and used the Users & Groups System Preference to make it one of my login items.

When you first start AnyBar, it puts a hollow circle in your menubar. You can then test it out by running commands like this in the Terminal:

echo -n "purple" | nc -4u -w0 localhost 1738

This one sends a “purple” message to UDP port 1738 on your computer and should turn the hollow circle into a solid purple dot. To turn in back to the hollow white circle, do this:

echo -n "white" | nc -4u -w0 localhost 1738

The AnyBar README on GitHub tells you all the messages you can send and different ways you can customize both the image in the menubar and the UDP port that AnyBar listens to.

With AnyBar working, I took my sdsummary script and rewrote parts of it to use Python’s socket library to send a “red” or “green” message to AnyBar depending on the contents of the latest SuperDuper! log file. Here’s the new script, called sdsignal:

python:
 1:  #!/usr/bin/python
 2:  
 3:  import os
 4:  import socket
 5:  
 6:  # Where the SuperDuper! log files are.
 7:  logdir = (os.environ["HOME"] +
 8:            "/Library/Application Support/" +
 9:            "SuperDuper!/Scheduled Copies/" +
10:            "Smart Update Backup from Macintosh HD.sdsp/Logs/")
11:  
12:  # AnyBar communication info.
13:  abhost = '127.0.0.1'
14:  abport = 1738
15:  
16:  # Get the last log file.
17:  logfiles = [x for x in os.listdir(logdir) if x[-5:] == 'sdlog']
18:  logfiles.sort()
19:  lastlog = logdir + logfiles[-1]
20:  
21:  # Look for the "Copy complete" line.
22:  good = False
23:  with open(lastlog) as f:
24:    for line in f:
25:      if "| Info | Copy complete." in line:
26:        good = True
27:        break
28:  
29:  # Send AnyBar the green or red signal depending on whether the backup worked.
30:  # AF_INET is for IPv4 and SOCK_DGRAM is for UDP.
31:  anybar = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
32:  anybar.connect((abhost, abport))
33:  if good:
34:    anybar.send('green')
35:  else:
36:    anybar.send('red')

Lines 7–10 define the folder where the log files are. They’re named according to the date on which they’re generated, so it’s easy to find the latest one by sorting them and choosing the last one. That’s what Lines 17–19 do.

Lines 22–27 scan through the log file, looking for a particular string that appears near the end of the file when the backup has been successful. A Boolean variable called good is set according to whether than string is found.

Lines 31–36 use the socket library to send the appropriate signal. Line 31 creates the socket using IPv4 and UDP. Line 32 connects the socket to the localhost (127.0.0.1) at port 1738. The host and port numbers we need to communicate with AnyBar are set in Lines 13–14. Lines 33–36 then send the appropriate signal.

Because last night’s backup was successful, this is what I got when I ran sdsignal:

SuperDuper signal in menubar

I have Bartender running, so I can put the AnyBar dot wherever I want. I’m not sure this is where I’m going to keep it.

Of course, the point of all this is to have sdsignal run automatically sometime after the backup is done. This could be done through the launchd system, but I think it’s simpler to have SuperDuper! do it through a setting in the advanced options pane.

SuperDuper! advanced options

I suspect this isn’t the end of my messing around with AnyBar. One thing I feel certain I’ll do is create new images to use as my SuperDuper! signal. As I said earlier, AnyBar lets you use you own customized menubar icons, and I’d like to have something a little more expressive than a simple dot.


  1. It almost never does, because SuperDuper is quite reliable. I had some problems once when an old backup drive was starting to fail, and I’ve occassionally seen errors when the backup drive didn’t get mounted. 


Car UI

It’s common for Apple users to say they wish Apple could take over their car’s user interface because the auto manufacturers do such a bad job of it. This is typically a comment on the electronic user interface, as many cars now have a little computer screen in the center of the dashboard with poorly laid out buttons and displays. I agree and take it further: I wish Apple (or anyone who thinks carefully about design for use) would have a go at the physical controls, too.

My pet peeve on my Toyota Camry is its windshield wiper stalk. It grows out of the right side of the steering column and has a variety of controls. The stalk as a whole can move up and down into one of five positions shown in the little graphic near the left side of the photos below. From the off position, you can move it down into the intermittent position, the low speed position, and the high speed position—the heavier the rain, the further down you move the stalk.1

Wiper stalk

In the middle of the stalk is a rotating collar that provides a sort of subcontrol for the intermittent wiper mode. As you can see in the four photos, turning the collar moves a horizontal marker on the collar up or down to align with one of four positions on what looks like a small bar chart. It’s how this collar works that bugs me.

When the wiper is in intermittent mode, it moves at the same speed as it does in low-speed mode. The difference is that it pauses between wipes. How long it pauses is what’s controlled by the collar position. To me, twisting the collar to move the marker down should make the pause between wipes shorter. This would be consistent with the way the stalk itself is moved: down to handle heavier rain. This is also consistent with the quasi-bar chart graphic, which I interpret as a graph of the pause, or interval, between wipes.

You will not be surprised to learn that the collar works exactly the opposite of how I think it should work. The interval between wipes increases as you turn the collar to move the marker down; the bottom position is for the lightest sprinkle. For the stalk, down equals heavier rain; but for the collar, down equals lighter rain.

The chart is apparently meant to represent the frequency of wiping, not the interval between wipes, despite the INT label on the marker. Yes, I know they mean INT to be interpreted as intermittent, not interval, but intermittent is not a quantity that can be graphed. Interval is. Which is why every time I look at that control, my mind says interval and I want to twist it the wrong way.2

In the couple of years I’ve had this car, I’ve trained myself to turn the collar the opposite way that both my instincts and my eyes tell me to turn it. Maybe this is Toyota’s way of keeping my brain active and alert. I’m looking forward to the next Camry model, in which the radio’s volume knob turns counter-clockwise to get louder.


  1. The mist position, which is a one-time wipe, is a momentary position that the stalk won’t stay in. It doesn’t concern us here. Thanks to mediummike on Twitter for fixing this. 

  2. Toyota’s designers are apparently fans of Fourier analysis and prefer the frequency domain to the time domain. Cooley and Tukey would be proud of them.