Idle Handler that runs on specific dates: am I on the right track?

Hey guys,

So I’m building a small Idle Handler that will customize my workspace depending on what season of the year it is. It’s starting out very simple, just changing the desktop image and spitting back a themed quote for the heck of it. Anyways, I had some questions, which I’ll get to, but first some context:

The big picture idea is that every day the idler will check the current date against a series of specific dates I’ve assigned (4 - one for each of the solstices), and if the current date is one of the solstice dates, the desktop will change – and stay changed – until the next solstice.

I’ve tested this on a small scale, where instead of having it compare against dates in the year, I had it compare against minutes in the hour. That seemed to work out just fine. The handler checked what minute it was every minute, and the desktop changed according to the times I assigned and spit back the quote. So far so good.

I wanted to test it on a medium scale (days of the week against current date where “Wednesday” would be Winter, “Thursday” would be Spring, and “Friday” would be Summer). So I set my idle timer to 12 hours, instead of 1 minute (43200 seconds instead of 60). And yet when I looked at my computer this morning, my desktop hadn’t changed and I was missing a quote. This seemed to be where things fell apart, so I had a few questions:

  1. Can Idle handlers still run – and activate – if a computer is locked? (NOT turned off, just locked)
  2. Does unlocking a computer that has an idle handler restart the idle handler’s count?
  3. Below is my code for the final product, not the smaller scale tests: will it work? Why or why not?

Thanks for your help everyone!

PS – the file names are all correct, I double checked that first!


on idle
	
	set WinterSolstice to date ("21/12/2016" as string)
	set SpringSolstice to date ("20/3/2017" as string)
	set SummerSolstice to date ("21/6/2017" as string)
	set FallSolstice to date ("22/9/2017" as string)
	
	set WinterDesk to "Macintosh HD:Users:npeterson:Documents:Desktops:Wild Triangle.jpg"
	set SpringDesk to "Macintosh HD:Users:npeterson:Documents:Desktops:Sky Triangle.jpg"
	set SummerDesk to "Macintosh HD:Users:npeterson:Documents:Desktops:Yellow Sky Triangle.jpg"
	set FallDesk to "Macintosh HD:Users:npeterson:Documents:Desktops:Firewatch.jpg"
	
	if the FallSolstice is the (current date) then
		tell application "Finder" to set desktop picture to FallDesk
		display dialog "Autumn is a second spring when every leaf is a flower.                    - Albert Camus" buttons {"How Serene"} default button 1
		
	else if the WinterSolstice is the (current date) then
		tell application "Finder" to set desktop picture to WinterDesk
		display dialog "The pine stays green in winter...wisdom in hardship                     - Norman Douglas" buttons {"Smart Advice"} default button 1
		
	else if the SpringSolstice is the (current date) then
		tell application "Finder" to set desktop picture to SpringDesk
		display dialog "It was one of those March days when the sun shines hot and the wind blows cold: when it is summer in the light, and winter in the shade.                                                                                                                               - Charles Dickens" buttons {"Every March, amirite?"} default button 1
		
	else if the SummerSolstice is the (current date) then
		tell application "Finder" to set desktop picture to SummerDesk
		display dialog "Summer bachelors like summer breezes, are never as cool as they pretend to be.                                                                                 - Nora Ephron" buttons {"Dem bois"} default button 1
	end if
	
	--this checks the current date against the solstice date every day.
	return 86400
	
end idle

--What I want/To Do List

--Script runs automatically, using a Idle Handler.

--Checks the day every day.

--If it is the day of a new Season (March 20, June 21, Sept 22, Dec 21) then change the desktop image to the pre-defined and seasonally appropriate image and deliver a dialog box when the change is made

--If it is NOT the day of a new Season, don't do anything, just keeping tracking those days, my man!

--Run this check forever.

Hi,

isn’t it easier “ and less expensive “ to create 4 recurring events in Calendar.app and trigger the script from there?

Hey,

I’m not sure what is meant by “less expensive” but in terms of ease, the real point of the exercise is to teach myself how to build an idle handler. I presently don’t have any reason for an idle handler, and I figured having one do something that I could see would at least be nice, if not also good for me to know that I can do it for things that come out in the long term.

(Less) expensive means you are going to check once a day or 365 times a year and you know this is 361 times without avail …

I didn’t tested it but I made some changes.

(1) replace hard coded path by ones built by the system
(2) more important, the dates which are supposed to fire a desktop change have a time component of “00:00:00”
Your code compared them to current date which has the same component only one second in a day. Quite no hope to get a match.


on idle
	
	set WinterSolstice to date ("21/12/2016") # the time component would be "00:00:00"
	set SpringSolstice to date ("20/3/2017") # the time component would be "00:00:00"
	set SummerSolstice to date ("21/6/2017") # the time component would be "00:00:00"
	set FallSolstice to date ("22/9/2017") # the time component would be "00:00:00"
	
	set pathToDocuments to path to documents folder as text # ADDED
	set WinterDesk to pathToDocuments & "Desktops:Wild Triangle.jpg"# EDITED
	set SpringDesk to pathToDocuments & "Desktops:Sky Triangle.jpg"# EDITED
	set SummerDesk to pathToDocuments & "Desktops:Yellow Sky Triangle.jpg"# EDITED
	set FallDesk to pathToDocuments & "Desktops:Firewatch.jpg"# EDITED
	
	# Comparison would fail if you don't clear the time component
	set currentDate to date (short date string of (current date)) # now the time component would be "00:00:00"
	if the FallSolstice is currentDate then # EDITED
		tell application "Finder" to set desktop picture to FallDesk
		display dialog "Autumn is a second spring when every leaf is a flower.                    - Albert Camus" buttons {"How Serene"} default button 1
		
	else if the WinterSolstice is currentDate then # EDITED
		tell application "Finder" to set desktop picture to WinterDesk
		display dialog "The pine stays green in winter...wisdom in hardship                     - Norman Douglas" buttons {"Smart Advice"} default button 1
		
	else if the SpringSolstice is currentDate then # EDITED
		tell application "Finder" to set desktop picture to SpringDesk
		display dialog "It was one of those March days when the sun shines hot and the wind blows cold: when it is summer in the light, and winter in the shade.                                                                                                                               - Charles Dickens" buttons {"Every March, amirite?"} default button 1
		
	else if the SummerSolstice is currentDate then # EDITED
		tell application "Finder" to set desktop picture to SummerDesk
		display dialog "Summer bachelors like summer breezes, are never as cool as they pretend to be.                                                                                 - Nora Ephron" buttons {"Dem bois"} default button 1
	end if
	
	--this checks the current date against the solstice date every day.
	return 86400
	
end idle

--What I want/To Do List

--Script runs automatically, using a Idle Handler.

--Checks the day every day.

--If it is the day of a new Season (March 20, June 21, Sept 22, Dec 21) then change the desktop image to the pre-defined and seasonally appropriate image and deliver a dialog box when the change is made

--If it is NOT the day of a new Season, don't do anything, just keeping tracking those days, my man!

--Run this check forever.

I thought to compare date strings dropping the year value but it doesn’t apply if you use a date string format putting the year at the beginning.

Yvan KOENIG running Sierra 10.12.1 in French (VALLAURIS, France) mercredi 23 novembre 2016 16:13:00

Hi, Stefan. Some Mac OS versions are buggy with calling script files directly from Calendar. It won’t work for me, at least.

I’m not sure if it applies to this region format, but the current date’s short string’s year is only 2 digits on my system, so the celestial event dates may need to be shortened for matching. As an additional FYI to the OP: There is no such thing as a Spring or Fall solstice. :slight_smile:

set {WinterSolstice, VernalEquinox, SummerSolstice, AutumnalEquinox} to {"21/12/16", "20/3/17", "21/6/17", "22/9/17"}

Thanks Yvan!

So how would I go about fixing what you mentioned below?

And for Marc, yeah you’re totally right, I was just being lazy haha.

I took care to comment the changes applied in the posted script.
You just have to open your eyes and read carefully :wink:

Yvan KOENIG running Sierra 10.12.1 in French (VALLAURIS, France) mercredi 23 novembre 2016 21:16:18

Whoops! I see them now. Serves me right for skimming! Thanks again!

It depends on who launched the script and how the lock screen is defined. If the user doesn’t log out and the console user remains active (doesn’t go to sleep) the idle handler will continue running. Keep in mind that the handler time can very and is scheduled when it is really idle. When timing an idle handles it is always more than the time you returned but normally not more than a second more delay. Keep in mind that AppleScript is single threaded and that the idle handler can be never called:

on run
	--just create an tight loop to keep the machine busy
	repeat

	end repeat
end run

on idle
	-- this will never be called 
	display dialog "hello"
	return 5
end idle

Also when an handler is called from outside the applet, it will interfere with the idle handler.

It shouldn’t but if it never did I’m not sure. The property of a script is persistent so it should be save to increment a property.

May I suggest another solution? Don’t use idle handlers for these kind of tasks. It’s very costly compared to what they do. launchd is perfect for these kind of jobs, you can create a job with calendar dates or periodically as you want but an process is only executed when needed. If you define an date interval and the job should be executed while the machine was asleep it will be executed for you when the machine wakes up.

Advantages:

  • No continuously running processing that runs an AppleScript instance 24/7 for an relative simple task
  • If you restart your machine the “idle” task is started immediately again
  • If the machine sleeps and the date has exceeded, the script will be immediately executed
  • If the script quits or unexpectedly quit for some reason the interval continues

An simple script could be to run every hour

or if you want to run the script every day at 20:00

and store this file in ~/Library/LaunchAgents so it will only be launched as a job when you are logged in.

No problem.

Please, look one more time to my message, I modified four instructions - removing coercion as string which is useless.

Yvan KOENIG running Sierra 10.12.1 in French (VALLAURIS, France) mercredi 23 novembre 2016 21:31:32

Huh. I’m getting an error actually.

“Invalid date and time date 21/12/2016 of «script». (-30720)”

I thought applescript was okay with day, month, year?

Did you used the [Open this Scriplet in your Editor:] button to get the script in your Editor ?

Here it behaves flawlessly.

set WinterSolstice to date ("21/12/2016") # the time component would be "00:00:00"
set SpringSolstice to date ("20/3/2017") # the time component would be "00:00:00"
set SummerSolstice to date ("21/6/2017") # the time component would be "00:00:00"
set FallSolstice to date ("22/9/2017") # the time component would be "00:00:00"
{WinterSolstice, SpringSolstice, SummerSolstice, FallSolstice}
--> {date "mercredi 21 décembre 2016 à 00:00:00", date "lundi 20 mars 2017 à 00:00:00", date "mercredi 21 juin 2017 à 00:00:00", date "vendredi 22 septembre 2017 à 00:00:00"}

I left the date as they appeared in your very first message.

I was surprised to see dates with the format used here in France (dd/MM/yyyy) on a forum where I am accustomed to see the format MM/dd/yyyy.
If you are using the later format you will have to edit the script accordingly as I did below but I can’t execute this one.

set WinterSolstice to date ("12/21/2016") # the time component would be "00:00:00"
set SpringSolstice to date ("3/20/2017") # the time component would be "00:00:00"
set SummerSolstice to date ("6/21/2017") # the time component would be "00:00:00"
set FallSolstice to date ("9/22/2017") # the time component would be "00:00:00"
{WinterSolstice, SpringSolstice, SummerSolstice, FallSolstice}

In fact, when I ran it I got the infamous error number -30720 so I’m quite sure that it’s the true explanation.

When I write scripts I don’t rely upon that, I create dates from their components so I have a code behaving flawlessly worldwide.

set fakeDate to date ("12/12/12")
copy fakeDate to WinterSolstice
set year of WinterSolstice to 2016
set month of WinterSolstice to 12
set day of WinterSolstice to 21

copy fakeDate to SpringSolstice
set year of SpringSolstice to 2017
set month of SpringSolstice to 3
set day of SpringSolstice to 20

copy fakeDate to SummerSolstice
set year of SummerSolstice to 2017
set month of SummerSolstice to 6
set day of SummerSolstice to 21

copy fakeDate to FallSolstice
set year of FallSolstice to 2017
set month of FallSolstice to 9
set day of FallSolstice to 22

{WinterSolstice, SpringSolstice, SummerSolstice, FallSolstice}
--> {date "mercredi 21 décembre 2016 à 00:00:00", date "lundi 20 mars 2017 à 00:00:00", date "mercredi 21 juin 2017 à 00:00:00", date "vendredi 22 septembre 2017 à 00:00:00"}

Yvan KOENIG running Sierra 10.12.1 in French (VALLAURIS, France) jeudi 24 novembre 2016 10:21:50

Figured it out, it did want mm/dd/yy, not dd/mm/yy. I guess applescript is region coded? (I’m in Canada and usually our date-format is the American mm/dd/yy, but I’ve always used dd/mm/yy myself because it just always made more sense!)

Anyways, it works now! Thanks!

Thanks for the feedback.

On my side my best choice is the international format : yyyy/MM/dd which is much more efficient for sorting date stamped filenames. I will never drop the numerous accented characters used in French but exchanging dd/MM/yyyy for yyyy/MM/dd is really not a problem.

Back to your original message.
Were you using the dd/MM/yyyy format when you posted your question or were MM/dd/yyyy dates converted silently somewhere into dd/MM/yyyy ?

Yvan KOENIG running Sierra 10.12.1 in French (VALLAURIS, France) jeudi 24 novembre 2016 15:05:02

Interesting. I had always been using dd/mm/yy. I didn’t switch it until this morning when I tried the script again and thought the date format was the program.