I’m updating the settings for my Harmony 520 and discovered that I couldn’t change the names of activities in Harmony Remote Software v7.7.0. I am not alone, but I couldn’t find a solution. While I had the problem on Mac OS X 10.6.8, others reported problems on various other operating systems, including Microsoft Windows.
The software wouldn’t let me to change the name of more than one activity. Instead, it would let me select and highlight the text, but not edit it. It was maddening, especially when it would beep instead of accepting characters I typed.
Then, by happenstance, I clicked over to Safari to do something while I was frustrated. When I flipped back to the Harmony Remote Software, it finally accepted my typed changes.
Therefore, if you have come across this problem, try switching to another application and back to the Harmony software again.
I’m continually astounded at how aggravating the Harmony remote can be, after all the hype. I suppose it is still better than many, if not most, of the alternatives, but that isn’t saying much.
I had a need to read some settings from a Python script on Mac OS X recently. I wanted to be able to change selected parameters for the script — some of which could be site or implementation specific — without embedding them directly in the code. Since the script was Mac OS X-only, using a property list seemed like a good idea.
With customizable settings from a property list, a script could become useful and more customizable for a wider community — or even different internal audiences.
I thought reading preferences was going to take a lot of effort. I was, however, pleasantly surprised at how easily I was able to accomplish it.
Asking others who had been down this route before resulted in some links to Apple docs. I wanted a more concrete example of how it was done in Python, and I got a great one.
That example came from reading the source to munkilib from the open source project, Munki. Since Munki had its own preference file, it needed to read from it, and its example was very enlightening.
Frogor directed me to a specific spot in the Munki code. That spot demonstrated how to read a preferences file with CoreFoundation.
Munki also has its own internal defaults for preferences. The munkilib/munkicommon.py example showed how to implement default settings in their absence in a property list. That provides a fallback position so that you always have some value available. In Munki, setting the defaults was done within a function. It seemed that it would be more generic if those defaults were separated out of the preference-reading function.
My own example of how to read a plist is outlined below. This focuses on just what you need in order to read the preferences and provide default settings for a larger script. A single set of preferences can be shared between scripts, and each script can encode its set own defaults. While you can create your own keys and values, for the example below, I will use the keys “StringPreference,” “BooleanPreference,” “ArrayPreference,” and “DictionaryPreference.”
The script won’t do anything yet, until we create a main() or other functions to call the get_preferences function. Since you’d be reading preferences as part of a larger script, this is fine for now. However, with what’s written already, you can test out reading preferences interactively with the shell and Python interpreter.
That’s it, the expected results were returned. Notice that the output for StringPreference and BooleanPreference are taken from the property list, while the empty array and dictionary come from the default_preferences in the script.
Update: Greg pointed out that there is a certain degree of danger using #!/usr/bin/env python as the shebang line in the script. Should a system have a non-Apple installation of Python, then /usr/bin/env python might return that. Another Python is probably less likely to be able to bridge CoreFoundation, and thus wouldn’t be able to use the CFPreferencesCopyAppValue call to read preferences.
I think the overall shebang danger is small because few Mac OS X systems will have an alternative Python installed, but clearly your chances of that increase in certain situations. To eliminate this risk, do what Munki does and insert the #!/usr/bin/python shebang instead.
Here is my current installation method for Mercurial 1.9 and Hg-Git on Mac OS X Snow Leopard. I use Hg-Git on a sporadic basis to work with projects on Github. That activity is frequent enough that it’s really helpful to be able to get Hg-Git set up and working quickly.
I had a rough time with Hg-Git after I upgraded to Mercurial 1.9 a few weeks ago. It was notable because that was the first time I can remember having incompatibilities between this stack of software. Since then, Hg-Git has changes available that allow it to install and work with newer versions of Mercurial, including version 1.9, and the most recent Dulwich release. These changes weren’t as readily available at the time I upgraded, so I thought it was worth writing this to remind me how to put the right pieces together.
I don’t yet have a working installation method for all of this software on Mac OS X Lion. I ran into a stumbling block getting Dulwich installed there, and Dulwich is a necessity for Hg-Git. Update: The situation changed a few days later, and I have instructions for Mac OS X Lion available now.
This may have been a problem isolated to the particular Lion system I was using. I haven’t had the chance to duplicate it elsewhere or troubleshoot the problem on the system where I encountered it.
Update: Since I wrote this, Hg-Git 0.3.1 became available through PyPI, which means you can install it via easy_install. You no longer need to clone the Hg-Git repository and run the setup process manually. Instead, you can run the following single command to get Hg-Git 0.3.1 and its dependencies (Dulwich 0.8.0 or later):
This simplifies the instructions quite a bit, but I leave the original steps above for reference.
There may be times when you want to obtain the number of the latest available version — not just the latest installed version — of a software package through automated means. If the vendor or project provides a syndication feed (either RSS or Atom) that describes new releases, then you may be able to parse that data and get the newest release from it.
As an example, let’s examine the RSS feed for Group Logic’s ExtremeZ-IP. Other developers provide RSS/Atom feeds for their releases, but the EZIP feed is a good one to start a demonstration with because it is generally structured well.
We can break apart the EZIP feed with the Universal Feed Parser module for Python, which you must obtain separately.
Update: Because of Mark Pilgrim situation (also described here), the Universal Feed Parser Web site is no longer available. There is an alternative source for the Universal Feed Parser at Google Code, and I have cloned the Universal Feed Parser repository to Bitbucket from there.
As you can see, the “ExtremeZ-IP Latest Releases” feed is automatically recognized as RSS 2.0. I prefer to use HTTPS for fetching these feeds whenever possible, so if the developer has an HTTP feed, I try to see if it also works with HTTPS.
Next, let’s find out where the version numbers are kept in the feed. It looks like they are in the entry title, based on reading the feed in Safari RSS. I can confirm that with the Universal Feed Parser. We’ll want to examine the title of every feed item so we can better handle both current and future entries from the feed. There are more entries to the EZIP than I will print out.
We get Unicode strings as output from the Universal Feed Parser. That’s why the quoted strings are preceded with a “u” character.
I’d like to strip out the version string from the title element in each entry. I’m going to do so by splitting on whitespace and getting the last group of characters from the string. (This doesn’t account for text like “Hot Fix,” as seen in the EZIP feed, but it is still a good enough starting point for my purposes.)
By stripping out the build number after “x” in the version string, you potentially lose some data. In the EZIP feed, there are entries where two consecutive version numbers are the same except for the build number after the “x.” However, depending on your needs, it may still be useful to eliminate that part of the version string, so we’ll do that next.
We really only need the most current or “top” item in the feed, since that should give us the newest release number. The newest version in this particular feed should be in the first entry. That’s “entries[0]” below, because we’re using Python and it zero references the first item in lists.
There, we now have the version number of the most current release, 7.1.1, for the product. We have drawn it straight from the developer’s syndicated feed, so it is as current as the developer makes it.
How could that be useful? The output can be compared against other data, like the currently-installed version. The comparison, in turn, could be made part of a monitoring workflow, so you could get alerts if you fall behind.
If we didn’t strip the build number after the “x,” we would be left with a complex version number. Some Python tools, like distutils, will not currently handle the trailing characters in the version number well.
I have found that you can improve upon distutils’ StrictVersion/LooseVersion version number handling by switching to parse_version in pkg_rsources (“from pkg_resources import parse_version as V”). More coverage of that topic appears in PEP 386. If you are comparing the original version strings from the EZIP feed with similarly complex output from elsewhere, then I would probably use the pkg_resources module.
Brix Anderson’s post about Downloading Sony GPS Assist Data Manually includes a Perl script that downloads the GPS Assist data for Sony cameras.
The GPS Assist data is stored on the memory card. When a memory card with GPS Assist data is inserted into a compatible GPS-equipped Sony camera — like the awesome Alpha SLT-A55V I got a few months ago — the camera has much faster lock-on times when searching for its location. The lock-on may take 10 seconds instead of requiring minutes.
I had been working without GPS Assist data since I bought the camera. I didn’t understand its value, but then I had captured photos where most of the shoot was incorrectly geotagged. After investigating this, I found the GPS Assist data should help, but that it required software on the desktop to update that data. Sony did not supply the relevant package for Mac OS X to do this.
Since I wanted to make sure I got updated location information on my photos, I did some searching and came across Brix’s page. I was honestly surprised to find that the GPS Assist data was written to the same memory cards as the photos, but it does make sense after some more thought.
Brix’s Perl script there certainly did the trick; my SLT-A55V picked up on the new GPS Assist data on the first card I tried it with. I have a limited number of cards, so I wrote a wrapper shell script that would run the Perl downloader script if certain volumes were present in /Volumes. I control the names of the cards, so I also control their paths.
I then call that wrapper script via a LaunchAgent. The LaunchAgent is triggered via “StartOnMount,” so it watches for new volumes to be mounted by Mac OS X. When a volume is mounted, the LaunchAgent runs, calls the wrapper script, the wrapper script checks for volume names, and if there is a match, runs the GPS Assist Perl script.
Now, every time I insert my memory cards to download photos, I get freshly-updated GPS Assist data for my camera.
I stumbled into winning a copy of Beginning Drupal 7 by Todd Tomlinson from Apress and the nice hosts of the Drupal Easy podcast.
The announcement on the podcast had me laughing out loud. (Luckily, I was home with a sick child who was sleeping upstairs.) I hope Mike and Andrew don’t mind that I transcribed the bit where they talked about me (starting at 46:24 into the podcast):
Mike: Anyway, [laughs] so we picked the winners earlier today, and they have not gotten back to me yet, but I’m going to say, I’m going to feel pretty good that they will get back to me so I’m going to announce their names, anyway. Jeremy Reichman, who — get this — his user ID on Drupal.org is 2039. Now, that doesn’t seem that small, but I think we’re up over five hundred thousand right now. Is that correct?
Andrew: Yeah, I believe so. It’s a pretty high number now. My number is 98,079.
Mike: Right. So this guy’s way better than you.
Andrew: Yes.
Mike: Way better. But he’s been a user for over nine — nine and half years — on Drupal.org.
Andrew: Wow.
Mike: So I’m not sure what he needs a Beginning Drupal 7 book for but, you know, I would guess it’s probably for his child that he’s had between the time that he started iwth Drupal and now.
Andrew: Although the could still be using Drupal 2.
Mike: Yeah, that’s true.
Indeed, my user ID is 2039. Indeed, I have been a member on Drupal.org for “9 years 37 weeks,” according to my profile. That’s a long time! The low user ID certainly doesn’t make my account better than someone else’s, but it is kind-of fun to have now that Drupal has become so popular.
One possibility they did not raise in that announcement was that I could just have been lurking on Drupal.org for five or six years — biding my time with my increasingly-creaky UserLand Manila Web site — before I decided to take the plunge with Drupal. This may be why my Certified To Rock score is a mere 3.
But perhaps I will share Beginning Drupal 7 with my children when the book arrives. This is not a bad idea, and I already have precedent for it. Our new arrival needs some technical reading material — and I’m going to have to upgrade to Drupal 7 sometime.
In the course of a conversation with Jesse today, I launched into action to fix the copyright statement in my site’s footer block. It still listed the copyright dates as “1999-2009” when the year had clearly rolled over into 2010 some time ago. Ahem.
Drupal 6, to my knowledge, doesn’t have a built-in way to insert a variable into the body of a text field in a node or block. However, it does have the very-powerful Token API, of which I’m a big fan. Tokens let you insert variable data. It’s just not always easy to figure out how a mere mortal — rather than a module developer — can take advantage of tokens in Drupal. (Variable insertion was a key feature of the UserLand Manila CMS I started using in the late 90’s, and I miss that sort of capability.)
In Drupal 5, I had previously used the Copyright module. Copyright module integrated with the Token API so you could use tokens in the block it created. The module was never upgraded for Drupal 6, so my footer copyright block became static text.
I realized that an Input Filter that worked with the Token API would probably let me do what I want, and I found a module that provided that missing link.
Here’s what I did so I could insert a constantly-advancing date variable into my copyright footer block:
Now, my copyright block should always show the current year in the date range, like this: 1999-2012.
I came across this hint about display properties on StackOverflow and thought it was worthwhile to write down for later. If you want to get the screen or Desktop resolution of a Mac via Python, you can do so with PyObjC.
First, let’s get the information about the main screen:
If you want just the horizontal and vertical resolution from that blob of data, you can pull the width and height out:
This might be useful in situations where you don’t have any of the “hundred of portable libs in Python that give you access to that information” — such as in your stock Mac OS X Python installation. To clarify: I’m in no way meaning to belittle that there are portable libraries that would let you do the same thing, but you also have to program for your audience and its constraints. One of the reasons I appreciate Python over some scripting languages is that you get so much capability in the Standard Library. However, on Mac OS X, you don’t get modules like pygame by default (yet … and maybe never) while you do get PyObjC.
I’ve had occasion to create some installer packages lately, and the topic of assigning a version number to them has rattled around in my head. The version number of an installer package is shown in the Finder’s Get Info windows, and in the preview pane of column view, so it has some usefulness just for telling one installer apart from another. And, importantly, the version number provides a way for Installer to determine whether a package has previously been installed and simply needs to be upgraded.
At first, I was just going to assign the date in YYYYMMDD format as the version, just to “keep things simple” by using:
Then I was rebuilding the packages several times a day, and wanted to distinguish between the builds, so I switched to YYYYMMDD.hhmm.
This seemed ridiculous after a day. Although I could distinguish between builds, the installers I was building weren’t really changing per se. They were the same payload and scripts and resources, just rebuilt by the command line packagemaker tool using The Luggage.
I crowdsourced my question about versioning, and Steve Losh helpfully responded with the idea to use Mercurial tags along with Semantic Versioning.
My package sources are already checked into Mercurial repositories, so this made a good deal of sense. It meant that I’d have to use tags to identify a version number — it would no longer be as automatic as using the current date. However, certain parts of the version string would be automatic, and the only manual part would be tagging revisions in Hg.
I had already bookmarked Semantic Versioning a while back, but had forgotten about it and never done much of anything with it. If I followed the Semantic Versioning standard, I’d create a “semver” tag to set up the repository, and then a new “vX.Y.Z” tag for actual releases thereafter. Steve further suggested the use of the “latest tag distance” and “short node hash” for the Hg revision, and that information is automatically available from Hg.
Semantic Versioning states, “when tagging releases in a version control system, the tag for a version MUST be ’vX.Y.Z’ e.g. ’v3.1.0’.” I felt I would have to strip the “v” from the version tag to use it cleanly as a package version. This results in commands like the following, where I’ve used sed to remove that character:
This is something I can build into makefiles for use with The Luggage, so I’m going to try it out. On first glance, I think it even fits in nicely with the concept of The Luggage. It automates much of the version-assignment process. And, it takes advantage of version control for both repeatable results and peer review.
I came across an interesting “problem” with Active Directory binding on Mac OS X Leopard. The symptoms were:
Since I’ve written (what seems like a) a book about Active Directory troubleshooting, I threw the book at this problem. It ended up taking quite some time to troubleshoot, and the answer ended up being very simple. However, it wasn’t on my normal list of culprits.
The biggest clue I found, besides the symptoms above, was that the DirectoryService debug logs yielded this during Active Directory logins from loginwindow:
It didn’t seem like a smoking gun, but I’d never come across this “false” response on a bound system before. So, what group was so important to the login process that the DirectoryService debug logs cared enough to note the failure? I was darned if I knew, and I had no other promising clues at that point.
So, I investigated that group further, and found it by its UUID using dsmemberutil:
Well, that helped a little, but the name would have helped a lot more. I had to find which group corresponded to the GID of 200. That GID was not at all familiar to me, but it was under 500, so there was a pretty good chance it came from Mac OS X.
This was my eureka! moment. I wasn’t entirely sure, but I was pretty confident that the “com.apple.access_loginwindow” group was the access control list group for the loginwindow process. Loginwindow controls all graphical logins to Mac OS X, and is the parent process of each GUI login session.
Looking up the group’s description confirmed that it was the ACL group. I did the lookup in Workgroup Manager, which was set to view the DSLocal directory service. While I was there, I also checked the membership: it listed only the computational group “localaccounts.” The “localaccounts” group is essentially a query that returns all accounts in the local directory service.
Well, that would certainly prevent Active Directory users from logging in with loginwindow. The ACL consulted the membership of the “com.apple.access_loginwindow” group to determine who was allowed to log in via the GUI. Because it contained only the “localaccounts” group, the ACL was preventing all non-local users from logging in.
Not knowing how this group was handled or even what had last edited it, I compared the affected system to a different AD-bound Leopard computer, which also had Workgroup Manager. (It’s handy to have the Mac OS X Server Admin Tools deployed out to your computers even if you don’t have a server to maintain.) The second computer didn’t have the group at all, which perplexed me a bit.
However, that made me reasonably sure I could simply delete that group. I backed it up from the filesystem at the command line, just to make sure, and then deleted it with Workgroup Manager on the affected computer.
After that, logins for all Active Directory accounts I tried proceeded normally at the loginwindow on that system.
With the problem solved, I sought more information about the workings of the “com.apple.access_loginwindow” group. I confirmed that it is created when the “Allow network users to login in at login window”
option is turned on in System Preferences > Accounts > Login Options. This should be turned on by default, and that initial state results in no “com.apple.access_loginwindow” group at all.
Since the option is on by default, the really simple solutions to this kind of problem are:
Deleting the “com.apple.access_loginwindow” group removes it completely and reinstates login capability for both local and network user accounts.
Toggling the System Preferences option back on, adds the “netaccounts” group to the “com.apple.access_loginwindow” group, reenabling login for both local and network users. It does not, however, remove the group “com.apple.access_loginwindow,” which remains on the system afterwards.
Here’s what that looks like in Workgroup Manager:
To prevent this on managed clients, I could see a system administrator proactively creating and managing the membership of the “com.apple.access_loginwindow” group. To ensure that managed clients bound to an Active Directory allow both local and network users to log in, make sure the group is populated with the appropriate nested groups: “localaccounts” and “netaccounts.”