User Tools

Site Tools


Sidebar

tutorials:siri

Controlling XTension with Siri

There is now a full featured Home Kit Plugin!

The rest of this article is left up for those with a historical interest.


NOTE: As of High Sierra update 4 (10.13.4) Apple has removed the capability for messages to execute a script upon the reception of an incoming message. If you rely on this feature you may want to refrain from updating to that version for the time being.

This tutorial requires XTension build 914 or higher.

As of this writing Apple’s Home Kit is all the rage, but as far as I can tell there still isn’t a desktop application interface to it. Only iOS apps, devices and “hub” like external devices. There is the “homebridge” hacked API but setting it up isn’t simple and future support is not guaranteed. In the meantime it’s possible to use the scripting events in Messages to get a subset of the functionality right now. This script currently only runs the subset of scripts that you specifically enter into it’s list and will not directly control individual lights or devices. It will be expanded to do more later, but it will never be as flexible lexicographically as talking directly through Siri to a home kit system would potentially be. As soon as that support is officially available for desktop apps from Apple I will look into adding it properly.

Setup Messages on your XTension Machine

First you’re going to need a separate iCloud ID for your XTension machine to use to sign into Messages. It is only necessary to setup the new ID in Messages, you can leave all the other services on the machine logged into your regular iCloud account. You will need another unique email address in order to open the extra iCloud account for your XTension machine. Once you have Messages up and running with the new account create a Contacts entry for it in your iPhone’s address book so that you can send messages to it. That name will also be used by Siri when you want to send a message. All the regular abilities of Siri to learn a nickname for something will also work for this entry.

Setup the Messages AppleScript Event Script

There have been several changes to the script format necessary to do this over the various OS versions that we support. So rather than try to include individual scripts tailored to each OS version I’m just going to outline what you need to add to the default scripts to get it working.

Open the Preferences window for Messages and you’ll find a popup menu called “AppleScript Handler” Select the last item on the list which is to “Open Scripts Folder”

There are several example scripts, make a duplicate in the finder of the script named “Auto Accept.AppleScript” and rename your copy “XTension.applescript”

Add the script to accept the chat invitation

Double click that script to open it in the Script Editor. You’ll see many event handlers that all basically include just one line of code in them to “accept theChat” Find the handler called something similar to “on received text invitation theText from theBuddy for theChat” and change the code in that handler to this:

	on received text invitation theText from theBuddy for theChat
		(* this handler is called whenever someone wants to chat with you that isn't already chatting with you
		the ID of the buddy seems to be a unique Identifier string and a colon and then the telephone number or email address
		that started the messages chat. Send that to XTension for validation against the list of approved buddies.
		If XTension finds it in the list then accept the chat. Otherwise refuse the chat.
		*)
		set theBuddyID to (id of theBuddy)
		tell application "XTension"
			tell xGlobalScript “Messages Control" to validateBuddy( theBuddyID)
		end tell

		if (result of it) then
			accept theChat
		else
			decline theChat
		end if
	end received text invitation

this sends the buddy ID to XTension to be validated, if it’s on the list of allowed ID’s that you have setup XTension to talk to then the buddy will be allowed to chat with the XTension machine and have his commands handled. Otherwise the chat request will be denied.

Disable all other connection requests

Since this was the “auto accept” script it has it setup to accept all incoming connection requests and that is probably not what you want to do. you should now go down and comment out all the other “accept” commands. DO NOT comment out the entire handler, it must be present or Messages will throw an error and stop handling any more commands at all. The handler needs to be there, just without specifically accepting the connection. Commenting out the rest of the connection request handlers so that they look something like:

	on received audio invitation theText from theBuddy for theChat
		--accept theChat
	end received audio invitation

when that handler is called it will just do nothing which is what we want. You could also specifically change the code to “decline theChat” if that works better than having the window popup asking if you want to chat with someone else on the machine.

Add the Message Received Handler

Find the handler in the script called something like “on message received theMessage from theBuddy for theChat” in the example that handler is completely empty. Replace it with this code snippet:

	on message received theMessage from theBuddy for theChat

		set theBuddyID to (id of theBuddy)

		try
			tell application "XTension"
				tell xGlobalScript “Messages Control" to handleMessage(theMessage, theBuddyID)
			end tell
			set theResponse to the result of it
		on error
			set theResponse to "there was an error handling your request."
		end try

		send theResponse to theChat
		
	end message received

Save that script document and return to the Messages app and re-open it’s preferences window. Select the “XTension.applescript” from the popup of scripts and now all message events will be sent through that script to XTension.

NOTE: if there is an applescript error while you’re setting this up or working with it even clicking the “ignore” button on the Messages dialog does not seem to actually ignore the error. It will change this popup back to “none” and you’ll have to select it again. Additionally the script is only run if Messages is in the background. If Messages is in the foreground it thinks you’re using the app and will not run the script events. Keep some other app in the foreground, preferably XTension

Create the "Messages Control” global script in XTension

Create a new Global script in XTension and call it “Messages Control” (you can call it anything you like but you’ll need to edit the name in the 2 places were it’s referenced in the XTension.applescript script we just made for Messages)

(* 
  Apple Messages to XTension Control Script v1.0
  james@sentman.com  http://MacHomeAutomation.com/

  AllowedBuddies is a list of phone numbers or email addresses that you want to allow to send commands to XTension
  After some experimentation it is a good idea to put both your phone number AND your other iCloud ID email address
  in here as commands may switch back and forth between where they are from even though they will go into the same
  window in Messages.
 
  AllowedScripts is a list of lists. Each list has 3 entries in it.
  The first entry is the phrase you want to use to activate the script from the message
  the second is the actual name of the global script. set an empty string "" to run within ThisScript
  the third is the handler in the global script you want to call. If empty then the script is run normally.
  
  example: {“do something”, “”, “doSomething”} when you send the command “do something” this will run 
  a handler called “on doSomething()” inside THIS script that you add at the bottom. This is because 
  the middle entry for script name is empty.
  
  example: {“goodnight”, “Bedtime script”, “”} will run a global script called “Bedtime script” when it receives
  the command “goodnight” Since no handler name is passed the script is just run normally.
  
  you can add as many of these records as you like.
  

  Whatever you return from the script or handler will be sent as the reply to the message.

  just say "help" to get a list of all the commands you've configured.

  NOTE: the messages script will not run if messages is in the foreground, do not leave
  messages in the foreground after you're done working with it or the script will not run.

*)


property AllowedBuddies : {“+123456789", “niceGuy@theBarLastNight.com"}

property AllowedScripts : {¬
	{"do something", "", "doSomething"}, ¬
	{"lab lights on", "SiriControlledScripts", "labLightsOn"}, ¬
	{"lab lights off", "SiriControlledScripts", "labLightsOff"}, ¬
	{"it's bedtime", "SiriControlledScripts", "bedtime"} ¬
		}


--just log what we are if someone runs the script from the script menu

write log "Apple Messages to XTension Control Script"
write log "version 1.0"




(*
   H A N D L E   M E S S A G E

  this is called from the messages script whenever a message is sent to the machine.
  This decides if the buddy is allowed and then if the command is valid and runs the script

*)


on handleMessage(TheMessage, theBuddy)
	
	--double check the buddy ID here too
	
	if not validateBuddy(theBuddy) then
		return "you are not known to me and so your commands have been rejected"
	end if
	
	
	
	--if the message is just help then send a list of all the commands
	
	if TheMessage is "help" then
		return sendHelp()
	end if
	
	
	
	
	-- walk the list of commands looking for a match
	
	repeat with thisInfo in AllowedScripts
		if item 1 of thisInfo is equal to TheMessage then
			write log "found global script match for " & TheMessage
			
			--if there is no script name then use thisScript			
			
			if (item 2 of thisInfo) is "" then
				set theScript to (thisScript)
			else
				set theScript to (item 2 of thisInfo)
			end if
			
			set myReply to (execute script theScript handler (item 3 of thisInfo))
			
			try
				return myReply
			on error
				return "command was executed"
			end try
			
		end if
		
	end repeat
	
	write log "no appropriate handler was found for the message: " & TheMessage & " from " & theBuddy color red
	return "no appropriate handler was found for your command, sorry."
	
end handleMessage

(*

V A L I D A T E    B U D D Y 

 theBuddyID seems to be a long string with a unique identifer as the first field, separated by a colon and then
 the telephone number or email address that started the chat. If we find that email or telephone number in the ID
 then we allow it to connect and run scripts.

This is called from the messages script to see if it shoudl accept a new chat invitation from a buddy that isn't already
involved in a conversation and also later when the buddy sends a command.

*)


on validateBuddy(theBuddyID)
	
	--split the string of ID on the colon that separates the opaque unique ID from the email or phone number
	set AppleScript's text item delimiters to {":"}
	set theID to text item 2 of theBuddyID
	set AppleScript's text item delimiters to {}
	
	-- see if the phone number or email address is in the allowed buddies list
	if AllowedBuddies does not contain theID then
		write log "messages connection refused from buddy: " & theID color red
		return false
	else
		--log who we are talking to with each command to know where it came from
		write log "messages connection allowed from buddy: " & theID
		return true
	end if
	
	
	
end validateBuddy

(* 

S E N D   H E L P 

sends all the valid commands as a comma delimited string so you can see what you've setup

*)

on sendHelp()
	
	--make a list of all the phrases you've setup
	
	set myPhrases to {}
	
	repeat with thisList in AllowedScripts
		copy item 1 of thisList to the end of myPhrases
	end repeat
	
	set AppleScript's text item delimiters to {", "}
	set myHelpText to myPhrases as text
	set AppleScript's text item delimiters to {}
	
	return "configured commands are: " & myHelpText
	
end sendHelp

(*

S E N D   M E S S A G E

You can also send a message to a buddy not as a response to a message they sent via these commands. 
Use from any other XTension script like:

tell xGlobalscript "messages control" to sendMessage( "The cat bowl is empty!", "john@something.com")

or whatever the buddy id is, it might be the iPhone address depending.

*)

on sendMessage(TheMessage, theBuddy)
	tell application "Messages" to send TheMessage to buddy theBuddy of (service 1 whose service type is iMessage)
	
	-- sending a message causes activate. If it's in the front then it won't run it's 
	-- messages received script. After sending the message we must either tell it to hide itself or in this case
	-- just re-activate XTension.
	
	activate
end sendMessage





-- you can add your handlers below here if you wish

on doSomething()
	write log "the doSomething script has been run"
	return “I AM doing something you lout"
end doSomething

Sending messages as alerts

The script above also has a generic sendMessage handler that you can use to send Messages to your phone or other machines in response to any events in XTension. Once it’s setup you can add something like:

  tell xGlobalScript “Messages Control” to sendMessage( “the catbox needs cleaning!”, “peter@bigFoundation.org”)

to send that message. The only downside with that is that Messages activates itself when it’s sending a message like this and so the script handler above contains a call to “Activate” to bring XTension back to the foreground. Otherwise the received message event script will not be used. This is no problem unless you’re sitting at the machine trying to work in an app and Messages and XTension are continually stealing your frontmost app focus.

tutorials/siri.txt · Last modified: 2019/11/18 12:23 by James Sentman