Control TV via IR with Arduino & AppleScript Part 3

[Part 1 Learn your IR codes] [Part 2 Control Arduino using AppleScript] [Part 3 Examples and Downloads]

UPDATE:

Works with OS X Yosemite.

 

Part 3

In Part 2 we learned how to control our Arduino using AppleScript, or more specifically, how to send Shell commands via AppleScript. In this next section we will look at a practical example, one which will take a User’s input and translate that into the appropriate IR commands for the appropriate digits on the remote. The script also sets the computer’s shutdown and boot up times.

The finished scripts can be found in the downloads box below, they will need a few slight adjustments for your system before they will run, but I’ll explain those as we run through them.

Downloads

ArduinoIRCommands.scpt (signalTV script)

SetWakeTime(db).scpt

Arduino Sketch for controlling 22LG3000 (can be adjusted for other TVs)

Don’t forget, you will still need to use Ken Shirriff’s IRremote library and Tod Kurt’s arduino-serial package. (Links in the ArduinoIRCommands script or in Part 2.)

Using the ArduinoIRCommands script, you can now create an unlimited number of scripts to control your TV in a number of ways only limited by the functions of your TV. For instance, the more eagle-eyed of you will have noticed that in Part 2 I left some subroutines in the signalTV routine that I haven’t yet talked about, namely “sendSLEEP“, “sendGET2WAKE“, “sendWAKE2SLEEP” and “sendCANCELSLEEP“. You’ll also find these in the Arduino sketch linked above. These subroutines send a series of IR commands that get to a specific point inside the TV’s system menus. For instance, the order of button presses on my TV remote to get to the sleep screen, set it for 120 minutes and then exit the menu is as follows:

Menu > Arrow Down > Arrow Down > Select > Arrow Down > Arrow Down > Select > Arrow Down > Arrow Down > Arrow Left > Exit.

Seeing as we’ve already made methods for all of these actions, it makes AppleScripting a lot easier if we simply create a new method to do specifically that, rather than script 11 different commands and send them via serial every time.

In the Arduino sketch you’ll see this method near the bottom:


void sendSLEEP() {

sendMENU();
sendADWN();
sendADWN();
sendSELECT();
sendADWN();
sendADWN();
sendSELECT();
sendADWN();
sendADWN();
sendALFT();
sendEXIT();
delay (10*100);

}

In the ArduinoIRCommands AppleScript, you see this in the commandlist:


set commandlist to { ... "sendSLEEP", ... }

And the corresponding letter “w” in the txlist.

In order to call that particular set of commands to set the TV sleep time to 120 minutes, you simply call:


commander's signalTV("sendSLEEP")

That sends 11 IR signals to the TV, but we’ve only sent 1 command to the Arduino.

Now, chances are that that particular set of button presses will do something different, or even nothing at all on your TV. All you have to do is change the series of button presses in the Arduino Sketch to reflect the actual process you need for your TV. For instance, it could be:

Menu > Arrow Down (3 times instead of 2) > Select > Arrow Down (Once instead of twice) > Arrow Right > Select > Arrow Down > Select > Exit.

In which case, the method in the Arduino sketch would look like this:


void sendSLEEP() {

sendMENU();
sendADWN();
sendADWN();
sendADWN();
sendSELECT();
sendADWN();
sendARGT();
sendSELECT();
sendADWN();
sendSELECT();
sendEXIT();
delay (10*100);
}

. . . or something.

Again it has sent 11 IR commands, but we’ve only sent 1 command to the Arduino, the letter “w“, rather than sending “i“, “e“, “e“, “e“, “h“, “e“, “g“, “h“, “e“, “h“, “k” one after the other. In essence, w = ieeeheghehk.

You can even re-write the entire method to do something completely different, but if you do that you’ll want to rename the method from “sendSLEEP” to “sendPARENTALCONTROLS” or something. If you do that, then you must rename the “sendSLEEP” references in both the void loop section in the Arduino sketch, and the ArduinoIRCommands AppleScript. Don’t move them, just rename them.

If you fancy going crazy and making loads of new methods but run out of letters to use, you can simply use the numbers and punctuation marks, for instance “!” = 33, “@” = 64 and “,” = 44.

You can find these by loading up the Arduino sketch on your computer, opening up the serial monitor and sending the command but replacing the letters at the end with keys that aren’t currently assigned to methods in the sketch.

Practical Example

Now let’s look at the practical example. The SetWakeTime(db) AppleScript linked above is the finished script that I use everyday. In order to run it, I hit CMD+F15 on my keyboard (use BetterTouchTool to set that up).

Right at the top, the very first thing we do is load in the commander (the ArduinoIRCommands script). Place your file path for that script inside the quote marks to get that line to work (see Part 2 if you’re not sure how to do this).

There are 4 subroutines in this script, the first is “setup(defans)“, but that doesn’t run quite yet. Instead the script now moves down to the line found under the comment “–BEGIN“. Here the script sets the variables “a“, “b“, “c“, “d“, “waketime” to the result of the subroutine “setup(defans)“. However in order to get setup(defans) to run, we have to give it a variable for ‘defans‘. ‘defans‘ is short for ‘default answer‘, and is used in the ‘display dialog‘ part of the script. If you have a time that you would regularly set your TV to wake up at, then put that time in where I have set it to “1029” (10:30am). (What? I’m on my summer break!) If you don’t have a regular time, then you can set this to some text, for example “Insert a time here. . . “.

Once ‘defans‘ is set the subroutine will run. On run, it will immediately call another subroutine and parse ‘defans‘ into it, as this subroutine will ask the user for their input.

The ‘GetWakeTime(defans)‘ subroutine is quite simple and looks like this:


on GetWakeTime(defans)

try

display dialog "Use 24hr time." & return & "eg. 0929 for 09:29am" with title "Set TV Wake Time" default answer defans buttons {"OK", "Cancel"} default button 1

copy the result as list to {button_returned, text_returned}

set usertime to items of text_returned

on error

display notification "error -128 occurred"

set usertime to false

end try

end GetWakeTime

Running it gives you something like this:

GetWakeTimeDialog

The result of that box “1029” or “1141” or “0516” or whatever the user typed in is then sent back to ‘setup(defans)‘ as the variable ‘usertime‘.

Setup now takes that usertime and gives it to the ‘CheckUserTime(usertime)‘ subroutine whereupon it checks to see if the result given by the user was a valid response. If it is not valid ‘CheckUserTime(usertime)‘ re-runs setup(defans) only now with the ‘defans‘ changed from “1029” to “Use a Valid Time, e.g 1029“.  If all is well, then ‘CheckUserTime(usertime)‘ returns the user’s input to setup, and setup renames it ‘waketime‘. setup now checks to see if the user’s response is a valid time and whether or not there are any zeros in the response. This is necessary because the ‘send0‘ command is tenth in the list of numbers (shown later). If there are any zeros, they get changed to 10.

To us, ‘1029‘ looks exactly as it looks, but to the computer, ‘1029‘ looks like this ‘”1“,”10“, “2“, “9“‘. Later the script will use those variables to get the right numbers out of the number list.

If everything is all valid, then we finally get back to the first line of script below the “–BEGIN” comment. a, b, c & d become 1, 10, 2 & 9 and waketime becomes 1029.

Now that the computer knows what I want it to do, it starts to send the commands to the Arduino, which in turn signals the TV with the commands I have asked for.

The first command that is sent is ‘commander’s signalTV(“sendGET2WAKE)‘  which in actuality is simply the letter “x“. The Arduino reads an “x” as “120” and dutifully performs the following actions:


void sendGET2WAKE() {

sendMENU();
sendADWN();
sendADWN();
sendSELECT();
sendADWN();
sendADWN();
sendSELECT();
sendADWN();
sendSELECT();
sendADWN();
sendADWN();
sendADWN();
sendADWN();

}

These are the button presses necessary to locate the Power-On time in my TV’s system menus, but all it does is get there, it doesn’t input the numbers. To do that we need to translate the user’s response into IR codes.

To do that, we use the following lines:


delay 10

commander's signalTV(commander's getNUMBERS(a))

delay 0.5

commander's signalTV(commander's getNUMBERS(b))

delay 0.5

commander's signalTV(commander's getNUMBERS(c))

delay 0.5

commander's signalTV(commander's getNUMBERS(d))

delay 0.5

The delays are very important here. It takes 10 seconds for all of the commands ‘signalTV(“sendGET2WAKE”)‘ is responsible for to occur.

You’ll remember from previously that we set a, b, c & d to 1, 10, 2 & 9. These refer to the places that the original numbers (1, 0, 2 & 9) occur in the number list shown below:


on getNUMBERS(x)

return item x of {"send1", "send2", "send3", "send4", "send5", "send6", "send7", "send8", "send9", "send0"}

end getNUMBERS

Notice ‘send0‘ in position 10 in the list, thus b is set to 10. If b was set to 0, the script would error out as there is no position 0 in the list.

Let’s look at one line in particular:


commander's signalTV(commander's getNUMBERS(a))

Here we are asking the script to perform one of commander’s subroutines, in this case ‘signalTV‘, but ‘signalTV‘ needs a variable before it will run. Problem is that we need a way of translating the user’s input into one of the ‘send‘ commands. To do that we tell the script that the variable it must use for signalTV is the result of the getNUMBERS subroutine.

First the script runs:


commander's getNUMBERS(a)

which results in “send1“. If we change ‘a‘ to ‘b‘, then it would result in “send0“, because b = 10, and send0 is tenth in the list.

Now the script takes that result and gives it to signalTV as the variable, so it would look like this:

commander's signalTV("send1")

So that one line:

commander's signalTV(commander's getNUMBERS(a))

Does two jobs. It works out which number the user asked for, and sends that command to the Arduino, which then signals the TV.

This command runs 4 times, with the ‘a‘ changing to ‘b‘ and so on. When that is complete, the TV’s Power-On function has been set.

signalTV(command)

Throughout all of this we have been using the ‘signalTV(command)‘ routine and as yet, I haven’t properly explained it.

The ‘signalTV(command)‘ subroutine is a powerful utility that allows you to perform (in it’s current form) up to 26 different and individual commands without having to have 26 different subroutines. (Although you could have many more than 26.)

It works like this:

You call it with ‘signalTV(“sendMUTE”)‘, or ‘(“send9”)‘, or ‘(“sendVOLUP”)‘ or anything that is in the commandlist.

It then takes that command e.g ‘send9‘ and finds its numerical position in the commandlist.

The commandlist and txlist are in sync (‘sendMUTE‘ = ‘a‘, ‘sendVOLUP‘ = ‘b‘, etc). It takes the numerical position of our command, in this case ‘send9‘ which is ‘21‘, and then re-sets the command from “send9” to the 21st item of the txlist which is “u“.

Now it performs the shell script which is set as ‘sc‘ with the addition of the command, which in our case is ‘u‘. This sends ‘u‘ to the Arduino, which reads that as 117, and thus sends the IR code for the button ‘9’ on the TV remote.

Quite simple really.

Optional

The next part of the script it specific to me. I use my computer as an Alarm clock, because I have another Jarvis-like script that runs on boot, which reads out the time, the weather, the news and my events for the day, and if a specific event occurs, the traffic news between here and there, but that script is for another time.

In order to use my computer as an alarm clock, I need to set the shutdown and boot times. To do that we run the ‘computerSetup(usertime)‘ subroutine. In short, this routine wakes up the computer 1 minute after the time the User set the TV to Power-on. It is necessary, however, to make sure that simply adding 1 minute to the usertime does indeed create a valid time, i.e, not 0761 or 2400. To deal with this there are some ‘if‘ statements.

After that we set the shutdown time to the time now + 110 minutes (taking into account the 10 minutes it takes for a scheduled shutdown to occur). This means the TV sleep timer of 120 minutes and the computer are now synced up and will both shutdown at more or less the same time.

In order for that to work, you must add your admin username and password in the areas that are clearly marked.

And that is it.

Chances are that my versions of the scripts and the sketch won’t work for you until you make some of the changes I have mentioned, but it’s a very good start if you’re new, or simply can’t be bothered to write it all out.

 

[Part 1 Learn your IR codes] [Part 2 Control Arduino using AppleScript] [Part 3 Examples and Downloads]

Author: Dan

Share This Post On