Control Exposé via AppleScript and voice (macosxhints)

The following is a modified and improved version of the original script belonging to the hint Control Exposé via AppleScript and voice posted at Mac OS X Hints.

Some key differences are:
The key code belonging to a particular action will be read from the user’s preferences (including key modifiers), instead of just assuming default values.
The if statements have been replaced by a more flexible set of properties and repeat loops.
The SpeechRecognitionServer application should not need to be selected from a list of applications (please test).

Some problems might remain, including that all commands are required to be said with an American accent just to be recognised by the darn speech service. :wink:

Edit - key code with modifiers has actually been tested, so should work now.

----- Action Codes -----
property |All Windows| : 32
property |All Windows Slow| : 34
property |Application Windows| : 33
property |Application Windows Slow| : 35
property |Desktop| : 36
property |Desktop Slow| : 37
property |Dashboard| : 62
property |Dashboard Slow| : 63

----- Action Commands -----
property commandList : {{commands:{"Show All Windows", "Exposé 1"}, code:|All Windows|}, ¬
	{commands:{"Show All Windows Slow", "Exposé 1 Slow"}, code:|All Windows Slow|}, ¬
	{commands:{"Show Application Windows", "Exposé 2"}, code:|Application Windows|}, ¬
	{commands:{"Show Application Windows Slow", "Exposé 2 Slow"}, code:|Application Windows Slow|}, ¬
	{commands:{"Show Desktop", "Exposé 3"}, code:|Desktop|}, ¬
	{commands:{"Show Desktop Slow", "Exposé 3 Slow"}, code:|Desktop Slow|}, ¬
	{commands:{"Show Dashboard", "Dashboard"}, code:|Dashboard|}, ¬
	{commands:{"Show Dashboard Slow", "Dashboard Slow"}, code:|Dashboard Slow|}}
property timePeriod : 30


set speechCommands to {}
repeat with thisCommand in commandList
	set speechCommands to speechCommands & commands of thisCommand
end repeat


-- Try to get SpeechRecognitionServer to be recognised as an application
application ((path to library folder from system domain as Unicode text) & "Frameworks:Carbon.framework:Versions:A:Frameworks:SpeechRecognition.framework:Versions:A:Resources:SpeechRecognitionServer.app")

tell application "SpeechRecognitionServer"
	try
		set chosenCommand to listen for speechCommands with prompt "Choose an option [[emph +]]" giving up after timePeriod
		
		repeat with thisCommand in commandList
			if chosenCommand is in commands of thisCommand then
				my performActionForCode(code of thisCommand)
				exit repeat
			end if
		end repeat
	end try
end tell


on performActionForCode(actionCode)
	set keyProperties to getPropertiesForActionCode(|Dashboard|)
	
	if keyProperties is missing value then
		say "Key was not found for action!"
	else
		set keyCodeScript to {"tell application \"System Events\" to key code " & keyCode of keyProperties & " using {"}
		tell keyModifiers of keyProperties
			if its commandDown then set keyCodeScript to keyCodeScript & "command down"
			if its controlDown then set keyCodeScript to keyCodeScript & "control down"
			if its optionDown then set keyCodeScript to keyCodeScript & "option down"
			if its shiftDown then set keyCodeScript to keyCodeScript & "shift down"
		end tell
		set {tids, text item delimiters} to {text item delimiters, ", "}
		tell keyCodeScript to set keyCodeScript to beginning & (rest as string) & "}"
		set text item delimiters to tids
		
		run script keyCodeScript
	end if
end performActionForCode

on getPropertiesForActionCode(actionCode)
	tell (do shell script "defaults read com.apple.symbolichotkeys AppleSymbolicHotKeys | sed '/" & actionCode & "/!d; /enabled = 0/d; s/^.*parameters = (//; s/).*//; s/, /" & return & "/g;q'") to ¬
		if (count of it) is not 0 then return {keyCode:paragraph 2 as number, keyModifiers:my getKeyModifiersForCode(paragraph 3 as number)}
	
	return missing value
end getPropertiesForActionCode

on getKeyModifiersForCode(keyModifierCode)
	tell keyModifierCode div 131072 to ¬
		return {shiftDown:it mod 2 is 1, controlDown:it mod 4 div 2 is 1, optionDown:it mod 8 div 4 is 1, commandDown:it div 8 is 1}
end getKeyModifiersForCode