How Do I Create an Applescript that executes curl commands w delays?

Hi All,
Apologies if this is super neophyte question. Total newbie with Applescript and still learning.
I’d like to create an Applescript that will send ECP commands to Roku box(es) on the local network - and do so at specified durations (set as delays) so for example:

The sequence (including the ECP commands) would go something like:

Wait 5 minutes (300 seconds)
curl -d “” “http://192.168.0.x:8060/keypress/home
Wait 10 seconds
curl -d “” “http://192.168.0.x:8060/launch/42088
Wait 15 seconds
curl -d “” “http://192.168.0.x:8060/keypress/right
Wait 5 seconds
curl -d “” “http://192.168.0.x:8060/keypress/select
Loop back to the top (Wait 300 seconds line)

I am able to manually execute the curl -d command lines directly from the Terminal, and they function as expected, I am just not sure how to do this from AppleScript - so that the process is automated.

Any insight or suggestions would be sincerely appreciated.

Kind Regards,

Ethan

repeat
delay 300
do shell script “curl -d "" "http://192.168.0.x:8060/keypress/home\”"
delay 10
do shell script “curl -d "" "http://192.168.0.x:8060/launch/42088\”"
delay 15
do shell script “curl -d "" "http://192.168.0.x:8060/keypress/right\”"
delay 5
do shell script “curl -d "" "http://192.168.0.x:8060/keypress/select\”"
end repeat

You’ll notice the Quotes in the curl statement are escaped with a backslash “/”

I’m wondering if do shell script waits for a return and if it matters.

Thank you robertfern!

Works perfectly. It was the “escaping” the quotes that was hanging me up - could not think of the word to even refer to the quotes within quotes! Preceding with a backslash does the trick.

And delay works perfectly following each do shell script.

Thank you so much for your guidance and support!

kind regards,

Ethan

actually the quotation including the “empty string” is not needed


repeat
	delay 300
	do shell script "curl -d [url=http://192.168.0.x:8060/keypress/home]http://192.168.0.x:8060/keypress/home"[/url]
	delay 10
	do shell script "curl -d  [url=http://192.168.0.x:8060/launch/42088]http://192.168.0.x:8060/launch/42088"[/url]
	delay 15
	do shell script "curl -d [url=http://192.168.0.x:8060/keypress/right]http://192.168.0.x:8060/keypress/right"[/url]
	delay 5
	do shell script "curl -d [url=http://192.168.0.x:8060/keypress/select]http://192.168.0.x:8060/keypress/select"[/url]
end repeat

Thank you for your insight. Much appreciated.

Question - is there a way to keep the AppleScript “persistent” - meaning, in the case that one of the curl line fails (as a result of no device at the specified IP for example) the script will ignore the error and keep running to the next line?

I noticed during some testing, test I had mistakenly entered the wrong IP, and when the script got to that line, the script stopped and showed me the error. Useful while debugging, but in practice for my implementation, I’d like to gave the script continue to the next line without regard for whether or not the present curl command succeeds. Does this make sense, and is there a way to implement this to make the AppleScript itself resilient or persistent (sorry if my wording is poor, not sure how else to describe what I am trying to accomplish.)

Does anyone have experience doing this?

Any suggestions would be greatly appreciated.

You can use a try block here, like so:

try
	-- any errors from code here will be ignored
	-- after an error script continues with statement after 'end try'
end try

You should put each curl command in its own try block

When you expect other errors that can be dealt with in whatever manner you can add an on error clause:

try
	-- any errors from code here will be dealt with in the 'on error' part
on error errorMessage number errorNumber
	-- add code here to deal with specified errors
end try

just put the curl line into a try block.
This version of the script uses a handler


repeat
	delayAndCurl(300, "keypress/home")
	delayAndCurl(10, "launch/42088")
	delayAndCurl(15, "keypress/right")
	delayAndCurl(5, "keypress/select")
end repeat

on delayAndCurl(theDelay, thePath)
	delay theDelay
	try
		do shell script "curl -d [url=http://192.168.0.x:8060/]http://192.168.0.x:8060/"[/url] & thePath
	end try
end delayAndCurl

Wow, thank you. You all are amazing. This makes perfect sense. Thank you. I will get to work on implementing Try now.

Is there a way to also have the script write a simple Log file to the desktop, with a time/date stamp for each time the script runs the sequence of curl commands? And it would merely append to the same txt file?

Using the above Try logic, how could I differentiate in the output to the log.txt whether the curl command was successful?

Thank you very much for all of your input and support!

this script writes a log in failure case into the default log folder ~/Library/Logs.
You can change the name in the first line to the file name you want.
The log can be watched in Console.app

A log entry contains the time stamp, the path and the error reason


property logFileName : "myLog.txt"
property logFile : ""

set logFile to ((path to library folder from user domain as text) & "Logs:" & logFileName)

repeat
	delayAndCurl(300, "keypress/home")
	delayAndCurl(10, "launch/42088")
	delayAndCurl(15, "keypress/right")
	delayAndCurl(5, "keypress/select")
end repeat

on delayAndCurl(theDelay, thePath)
	delay theDelay
	try
		do shell script "curl -d [url=http://192.168.0.x:8060/]http://192.168.0.x:8060/"[/url] & thePath
	on error errorMessage
		writeToLog(thePath & " failed: " & errorMessage)
	end try
end delayAndCurl

on writeToLog(theMessage)
	tell (current date) to set timestamp to short date string & space & time string & ": "
	try
		set fileReference to open for access file logFile with write permission
		set logFileEof to get eof of the fileReference
		write timestamp & theMessage & return to fileReference starting at eof as «class utf8»
		close access fileReference
	on error
		try
			close access file logFile
		end try
	end try
end writeToLog


Beautiful! Thank you. So a question on structure if I would like to implement these commands across a series of devices at different IP addresses. (192.168.0.x, 192.168.1.x, 192.168.2.x, 192.168.3.x and so on)

Would it be better to place each one in it’s own TRY subroutine (is that the right word) or can they somehow be sequenced in the same TRY block? Or perhaps even place the IP addresses (URLs) in a variable? What I am trying to accomplish is to know which IP fails and on which CURL command.

How would that best be structured?

Your Expertise is Sincerely Appreciated.

if blocks of the IP addresses are consecutive like in your example I’d prefer this way


on delayAndCurl(theDelay, thePath)
	repeat with i from 0 to 3
		delay theDelay
		try
			do shell script "curl -d http://192.168. " & i & ".x:8060/" & thePath
		on error errorMessage
			writeToLog(thePath & " failed: " & errorMessage)
		end try
	end repeat
end delayAndCurl

Ok, indeed makes sense. However, the subnets are consecutive, but the actual IP addresses of the devices are not and sometimes new devices are added in a given subnet, so they may be:

192.168.0.23
192.168.1.5
192.168.3.54
192.168.2.236

and so on. Roku ECP has the ability to do “auto-discover” on a subnet to poll the IP of any Roku devices it finds, but that is getting way over my head at this stage. For now, I have allowed the respective DHCP server to assign the address to the Roku box when it brought online and then I go in a reserve that IP on the DHCP server (since there is no other way to assign a static IP on the Roku box itself.)

How would I create a table or input source of IPs in the script that can be either
a) manually adjusted or
b) read from an external txt file as an IP list? and if read from said list, to be read each time it runs the loop?

Manual input is ok, since I don’t think the IP list would change that often - as new devices may only be introduced once a week.

b)

create a plain text file and save the addresses one per line


.
set logFile to ((path to library folder from user domain as text) & "Logs:" & logFileName)

repeat
	set ipAddresses to paragraphs of (read file "MacHD:path:to:file.txt") -- change to proper path
	
	repeat with anAddress in ipAddresses
		delayAndCurl(300, "keypress/home", anAddress)
		delayAndCurl(10, "launch/42088", anAddress)
		delayAndCurl(15, "keypress/right", anAddress)
		delayAndCurl(5, "keypress/select", anAddress)
	end repeat
end repeat

on delayAndCurl(theDelay, thePath, ipAddress)
	delay theDelay
	try
		do shell script "curl -d http://" & ipAddress & ":8060/" & thePath
	on error errorMessage
		writeToLog(thePath & " failed: " & errorMessage)
	end try
end delayAndCurl
.

Wow! Brilliant! Thank you!

And super clean. Will get this set up and implemented next.

Thank you again for all of the support.

What a great community!

Quick question if I may - so I understand the logic:

Based on the following, does this

  1. wait 300 seconds and then execute the CURL command for a single IP - then go through the four commands (CURL) then repeat with the next IP?
    OR
  2. does this wait 300 seconds and execute that CURL command for all of the IPs in the list of IPs before proceeding to the next delayAndCurl?
repeat
   set ipAddresses to paragraphs of (read file "MacHD:path:to:file.txt") -- change to proper path
   
   repeat with anAddress in ipAddresses
       delayAndCurl(300, "keypress/home", anAddress)
       delayAndCurl(10, "launch/42088", anAddress)
       delayAndCurl(15, "keypress/right", anAddress)
       delayAndCurl(5, "keypress/select", anAddress)
   end repeat
end repeat

The reason I am asking is to understand how to establish the timing(s) - ideally, it would wait 300 seconds, and then execute that “keypress/home” for each and every IP in the IP list, then proceed to the next delayAndCurl (10,

Sorry for my confusion, just want to see if I am understanding what is happening.

Your Input is Appreciated.

How would I structure it so that it would process all IPs for that command following the single delay value? The difference in timings becomes very large if it waits 300 seconds for one IP first command. Ideally, for timings, it would merely wait 300 seconds, then execute the CURL command for all IPs in the list, then proceed to the next delay/command. Such that the total delay is always about 300 seconds at the start whether 1 or 10 Roku devices.

Does this require a complete restructure?

Your Input and Expertise are Appreciated.

try this, it waits 5 minutes before going through the address list and one second before each keypress/home address


repeat
	set ipAddresses to paragraphs of (read file "MacHD:path:to:file.txt") -- change to proper path
	delay 300
	repeat with anAddress in ipAddresses
		delayAndCurl(1, "keypress/home", anAddress)
		delayAndCurl(10, "launch/42088", anAddress)
		delayAndCurl(15, "keypress/right", anAddress)
		delayAndCurl(5, "keypress/select", anAddress)
	end repeat
end repeat

Ok, I see. So there is still the cumulative effect of executing each command in sequence for each IP, but this could be manageable for a list of perhaps ten (10) devices - would total somewhere close to 5 minutes to work it’s way through the list.

How would you structure it so that it executes them more together with less delay between commands if we needed the devices to be triggered at about the same time(s)?

Sort of like this logic/flow:

repeat

delay 300
do shell script "curl -d [url=http://192.168.0.x:8060/keypress/home]http://192.168.0.x:8060/keypress/home"[/url]
do shell script "curl -d [url=http://192.168.2.x:8060/keypress/home]http://192.168.2.x:8060/keypress/home"[/url]
do shell script "curl -d [url=http://192.168.2.x:8060/keypress/home]http://192.168.2.x:8060/keypress/home"[/url]
do shell script "curl -d [url=http://192.168.2.x:8060/keypress/home]http://192.168.2.x:8060/keypress/home"[/url]
do shell script "curl -d [url=http://192.168.3.x:8060/keypress/home]http://192.168.3.x:8060/keypress/home"[/url]
do shell script "curl -d [url=http://192.168.3.x:8060/keypress/home]http://192.168.3.x:8060/keypress/home"[/url]

delay 10
do shell script "curl -d [url=http://192.168.0.x:8060/launch/42088]http://192.168.0.x:8060/launch/42088"[/url]
do shell script "curl -d [url=http://192.168.2.x:8060/launch/42088]http://192.168.2.x:8060/launch/42088"[/url]
do shell script "curl -d [url=http://192.168.2.x:8060/launch/42088]http://192.168.2.x:8060/launch/42088"[/url]
do shell script "curl -d [url=http://192.168.2.x:8060/launch/42088]http://192.168.2.x:8060/launch/42088"[/url]
do shell script "curl -d [url=http://192.168.3.x:8060/launch/42088]http://192.168.3.x:8060/launch/42088"[/url]
do shell script "curl -d [url=http://192.168.3.x:8060//launch/42088]http://192.168.3.x:8060//launch/42088"[/url]

.and so on.?