I say “waking” because it’s not really asleep, but the display is.
Anywho, I’ve an awesome budget workbook in Excel (functioning as my place to implement new VBA ideas with a degree of practical return on time spent), and the next enhancement was to be able to send myself an email when out and about and have mail run an applescript to open the workbook and place the new line item into it.
It works, and I overcame the problem not being able to get the display to come back on by using a tool called “SleepDisplay”. However, the script will only wake the display, enter the password, and execute if the screen’s been asleep for a short time, less than 30 minutes or so (though I’ve yet to exhaustively pinpoint exactly how long).
If its within this time frame, then everything works perfectly, but if not, then everything works except the script itself, meaning that Mail receives the message the other actions of the rule are executed, but the applescript fails to run.
Upon waking the display and logging back in manually, I can see that excel has been activated (icon appears on the dock), but it doesn’t do anything until I click it’s icon. After clicking excel’s icon, it immediately opens the budget workbook, inserts the line item, and does everything else it was supposed to do.
So what’s my problem? Is there some deeper level of sleeping the display without actually putting the computer to sleep? I know the computer is still awake because I can send it emails and it will beep at me and execute any other rules that are completely within Mail (ie no applescript), including auto-responding with a message. Given the hanging excel instance when I log back into the computer, is the problem to due with calling excel? The display never comes on when the ~30 minutes have been passed, so is there something wrong with the SleepDisplay tool?
Any help is appreciated.
Thanks,
Tim
The full script follows, including subroutines, so if you’d like to test this, just change the pointer to the budget file. Directly below is a link to a blank copy of the glorious budget sheet itself:
Edit This workbook is macro-enabled, but it’s completely safe. Scan it before opening if you’d like to be (more) safe.
https://www.dropbox.com/s/17elck604t3nyvz/budget%20template.xlsm
Use the following formatting in the trigger email to conform with the script:
Budget item:
(day, ## for day of current month, leave blank if for same day;
w, “who” the line item is for, enter 1,2 or 3, points towards names specified in the budget workbook;
t, type, enter i or e, stands for income and expense;
d1, primary description/category, must match category specified in budget workbook;
d2, secondary description, any value or string;
$, number only for amount)
AVOID HYPHENS WHEN ENTERING INFO, AS HYPHENS ARE USED AS THE DELIMITER
day-
w-
t-
d1-
d2-
$-
EXAMPLE:
day-15
w-1
t-e
d1-shopping
d2-test
$-100
some notes on the budget workbook:
- used for two people sharing a single account or monetary supply
- data validation used so only income/expense categories specified in the “year” sheet can be entered
- conditional formatting color codes each line item based on income/expense of person 1, person 2, or a shared income/expense
- dynamic charts on “year” sheet get data from the hidden “charts” sheet
- VBA userforms for entering in all line items, common income/expenses, and sorting line items based on several criteria
Finally, here’s the script itself. I’ve only commented it until the end of the part that wakes the display and logs in.
(*
	Tim Wilson
	3/19/2013
	Script to add line items to budget worksheet from incoming message in Mail
*)
-- first two lines are blocks to react to the rule in Mail
using terms from application "Mail"
	
	on perform mail action with messages theSelectedMessages
		
		-- checks if system sounds are muted and mutes them if not, unmuting when finished
		set isMuted to output muted of (get volume settings)
		set changeMute to false
		if isMuted is false then
			set changeMute to true
			set volume with output muted
		end if
		
		-- check to see if display is asleep, wakes display if asleep and resleeps it when finished
		set isSleeping to false
		set sleeping to 1
		set awake to 4
		-- shell script that returns sleep status of display
		set display_sleep_state to parseLine(do shell script "ioreg -n IODisplayWrangler |grep -i IOPowerManagement", "CurrentPowerState")
		if item 2 of display_sleep_state contains sleeping then
			set isSleeping to true
			set passKey to "my_super_secret_password" -- not really my password
			
			-- shell script calling the "SleepDisplay" tool
			do shell script "/usr/local/bin/SleepDisplay --wake"
			
			tell application "System Events"
				delay 5
				-- entering my password into the login window
				repeat with i from 1 to count of passKey
					keystroke item i of passKey
					delay 0.01
				end repeat
				key code 36 --return key
				delay 1
			end tell
			
			
		else if item 2 of display_sleep_state contains awake then -- unused, but I keep it here anyways :)
		end if
		delay 0.5
		
		set dateTime to current date
		set currentMonth to month of dateTime as number
		set currentYear to year of dateTime as number
		set currentDay to day of dateTime as number
		
		set budgetFile to "HDD:Users:Haiiro:Dropbox:" & currentYear & " Budget.xlsm" as alias
		
		if currentMonth is 1 then
			set sheetName to "Jan"
			set numOfDays to 31
		else if currentMonth is 2 then
			set sheetName to "Feb"
			if currentYear mod 4 = 0 then
				set numOfDays to 29
			else
				set numOfDays to 28
			end if
		else if currentMonth is 3 then
			set sheetName to "Mar"
			set numOfDays to 31
		else if currentMonth is 4 then
			set sheetName to "Apr"
			set numOfDays to 30
		else if currentMonth is 5 then
			set sheetName to "May"
			set numOfDays to 31
		else if currentMonth is 6 then
			set sheetName to "Jun"
			set numOfDays to 30
		else if currentMonth is 7 then
			set sheetName to "Jul"
			set numOfDays to 31
		else if currentMonth is 8 then
			set sheetName to "Aug"
			set numOfDays to 31
		else if currentMonth is 9 then
			set sheetName to "Sep"
			set numOfDays to 30
		else if currentMonth is 10 then
			set sheetName to "Oct"
			set numOfDays to 31
		else if currentMonth is 11 then
			set sheetName to "Nov"
			set numOfDays to 30
		else if currentMonth is 12 then
			set sheetName to "Dec"
			set numOfDays to 31
		end if
		
		repeat with currentMessage in theSelectedMessages
			
			set fileText to content of currentMessage
			
			set itemList to {}
			set i to 0
			set fullItem to 5
			
			set tempItem to {itemDay:"", itemWho:"", itemType:"", itemDesc:"", itemDesc2:"", itemAmount:""}
			
			set err to false
			
			repeat with currentLine in paragraphs of fileText
				set lineInfo to parseLine(currentLine, "-")
				if (count of lineInfo) is not less than 2 then
					if item 1 of lineInfo contains "Day" then
						set itemDay of tempItem to item 2 of lineInfo
						set i to i + 1
						set fullItem to 6
					else if item 1 of lineInfo contains "w" then
						set itemWho of tempItem to item 2 of lineInfo as number
						set i to i + 1
					else if item 1 of lineInfo contains "t" then
						set itemType of tempItem to item 2 of lineInfo
						set i to i + 1
					else if item 1 of lineInfo contains "d1" then
						set itemDesc of tempItem to titleCase(item 2 of lineInfo)
						set i to i + 1
					else if item 1 of lineInfo contains "d2" then
						set itemDesc2 of tempItem to titleCase(item 2 of lineInfo)
						set i to i + 1
					else if item 1 of lineInfo contains "$" then
						set itemAmount of tempItem to item 2 of lineInfo
						set i to i + 1
					end if
				end if
				if i ≥ fullItem then
					if itemType of tempItem is "i" then
						set itemType of tempItem to "Income"
					else if itemType of tempItem is "e" then
						set itemType of tempItem to "Expense"
					end if
					if itemDay of tempItem is not "" and itemDay of tempItem is greater than numOfDays then
						set errorMsg to "The specified day of an item is incorrect. Must be no greater than " & numOfDays & ".
No items were added."
						set err to true
						exit repeat
					end if
					if itemDay of tempItem is not "" then
						set itemDay of tempItem to (currentMonth & "/" & itemDay of tempItem & "/" & currentYear) as string
					end if
					set end of itemList to tempItem
					set tempItem to {itemDay:"", itemWho:"", itemType:"", itemDesc:"", itemDesc2:"", itemAmount:""}
					set i to 0
					set fullItem to 5
				end if
			end repeat
			
			
			if err is false then
				set returnValue to insertItems(budgetFile, sheetName, itemList)
				if item 1 of returnValue is 1 then
					set errorMsg to "The current month is full, so no more items can be entered. Only the first " & item 2 of returnValue & " item(s) were added."
					set err to true
				else if item 1 of returnValue is 2 then
					set errorMsg to "The description of item " & (item 2 of returnValue) + 1 & " didn't match with set descriptions.
The first " & item 2 of returnValue & " item(s) were added.
Expenses:
"
					repeat with desc in (item 3 of returnValue)
						set errorMsg to errorMsg & "      " & desc & "
"
					end repeat
					set errorMsg to errorMsg & "
Income:
"
					repeat with desc in (item 4 of returnValue)
						set errorMsg to errorMsg & "      " & desc & "
"
					end repeat
					set err to true
				else if item 1 of returnValue is 3 then
					set errorMsg to "The person of item " & (item 2 of returnValue) + 1 & " is incorrect. Must be 1, 2, or 3.
The first " & item 2 of returnValue & " item(s) were added."
					set err to true
				end if
			end if
			
			if err is true then
				errorMessage(errorMsg, fileText)
			else
				successMessage(item 2 of returnValue, fileText)
			end if
			
			delete currentMessage
			
		end repeat
		
		if isSleeping is true then do shell script "/usr/local/bin/SleepDisplay"
		
		delay 4
		if changeMute is true then set volume without output muted
		
	end perform mail action with messages
	
end using terms from
on errorMessage(errorMsg, fileText)
	
	set errorMsg to errorMsg & "
Original data: 
" & fileText
	
	if my appisrunning("Mail") then
		set dontQuit to true
	else
		set dontQuit to false
	end if
	
	tell application "Mail"
		set theMessage to make new outgoing message with properties {visible:true, subject:"Budget - Add Item Error", content:errorMsg}
		tell theMessage
			make new recipient at end of to recipients with properties {name:"Tim Wilson", address:"twilsonco@gmail.com"}
		end tell
		set sent to send theMessage
		if dontQuit is false then
			if sent is true then quit
		end if
	end tell
	
end errorMessage
on successMessage(itemList, fileText)
	
	if my appisrunning("Mail") then
		set dontQuit to true
	else
		set dontQuit to false
	end if
	
	set dateTime to current date
	set currentMonth to month of dateTime as number
	set currentYear to year of dateTime as number
	set currentDay to day of dateTime as number
	
	set messageText to "The following item(s) have been added to the budget workbook:
"
	set i to 1
	repeat with currentItem in itemList
		if itemDay of currentItem is "" then
			set itemDate to (currentMonth & "/" & currentDay & "/" & currentYear) as string
		else
			set itemDate to itemDay of currentItem
		end if
		set messageText to messageText & "Date: " & itemDate & "
Person: " & itemWho of currentItem & "
Type: " & itemType of currentItem & "
Description: " & itemDesc of currentItem & "
Secondary Description: " & itemDesc2 of currentItem & "
Amount: $" & itemAmount of currentItem & "
"
	end repeat
	
	set messageText to messageText & "
Original data: 
" & fileText
	
	tell application "Mail"
		set theMessage to make new outgoing message with properties {visible:true, subject:"Budget - Add Item Success", content:messageText}
		tell theMessage
			make new recipient at end of to recipients with properties {name:"Tim Wilson", address:"twilsonco@gmail.com"}
		end tell
		set sent to send theMessage
		if dontQuit is false then
			if sent is true then quit
		end if
	end tell
	
end successMessage
on insertItems(excelFile, sheetName, itemList)
	
	set itemNum to count of itemList
	set insertNum to 0
	set descMatch to false
	set userNumbers to {1, 2, 3}
	
	tell application "Microsoft Excel"
		
		open excelFile
		repeat with currentItem in itemList
			tell worksheet "Year"
				if itemWho of currentItem is not in userNumbers then
					quit saving no
					return {3, insertNum}
				end if
				set itemWho of currentItem to value of cell ("O" & (itemWho of currentItem) + 32)
				if itemType of currentItem is "Expense" then
					repeat with i from 32 to 44
						if itemDesc of currentItem contains value of cell ("B" & i) then
							set descMatch to true
							exit repeat
						end if
					end repeat
				else if itemType of currentItem is "Income" then
					repeat with i from 32 to 39
						if itemDesc of currentItem contains value of cell ("G" & i) then
							set descMatch to true
							exit repeat
						end if
					end repeat
				end if
			end tell
			if descMatch is false then
				set expList to {}
				set inList to {}
				tell worksheet "Year"
					repeat with i from 32 to 44
						if value of cell ("B" & i) is not "" then set end of expList to value of cell ("B" & i)
					end repeat
					repeat with i from 32 to 39
						if value of cell ("G" & i) is not "" then set end of inList to value of cell ("G" & i)
					end repeat
				end tell
				quit saving no
				return {2, insertNum, expList, inList}
			end if
			tell worksheet sheetName
				repeat with i from 5 to 104
					if value of cell ("D" & i) is "" then
						if itemDay of currentItem is not "" then set value of cell ("C" & i) to itemDay of currentItem
						set value of cell ("D" & i) to itemWho of currentItem
						set value of cell ("E" & i) to itemType of currentItem
						set value of cell ("F" & i) to itemDesc of currentItem
						set value of cell ("G" & i) to itemDesc2 of currentItem
						set value of cell ("H" & i) to itemAmount of currentItem
						set insertNum to insertNum + 1
						exit repeat
					end if
				end repeat
			end tell
		end repeat
		
		if i = 104 and insertNum < itemNum then
			quit saving no
			return {1, insertNum}
		else
			run VB macro "dateSort"
			tell active workbook
				save workbook as filename (excelFile as text) file format macro enabled XML file format with overwrite
			end tell
			quit
		end if
		
	end tell
	return {0, itemList}
	
end insertItems
on parseLine(theLine, delimiter)
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to {delimiter}
	set theTextItems to theLine's text items
	set AppleScript's text item delimiters to astid
	
	repeat with i from 1 to (count theTextItems)
		if (item i of theTextItems is "") then set item i of theTextItems to missing value
	end repeat
	
	return theTextItems's every text
end parseLine
on titleCase(_string)
	set _code to "import sys; print sys.argv[1].title()"
	set _script to "/usr/bin/python -c " & _code's quoted form & " " & _string's quoted form
	return do shell script _script
end titleCase
on appisrunning(appName)
	tell application "System Events" to (name of processes) contains appName
	-- returns true if excel is running
end appisrunning
Model: MBP-r
AppleScript: 2.2.4
Browser: Safari 536.25
Operating System: Mac OS X (10.8)


