Along with the TM Script language, TestManager also supports writing scripts in Python. This requires IronPython to be installed.
The Python environment within TestManager is much the same as that provided by IronPython, but with two important differences. Firstly, TestManager defines a global object tm to allow access to the TestManager API. Secondly, the import mechanism has been extended to allow scripts defined within TestManager to be imported as normal Python modules.
When Python scripts are run from within TestManager, certain internal aspects of the application are exposed via an API, for example to list set-top boxes (STBs), send IR signals etc.
Below are some examples of Python code in TestManager.
Importing a Script
If there is a script called Foo within a script folder called Python which contains the following code:
def Message(): print 'Hello, World!'
Message can then be called like so:
import scripts.Python.Foo as foo foo.Message()
Note that the names of the folders and scripts involved must be valid Python module names for the import to succeed.
Sending a Signal to a STB
To send an IR signal to a known STB, it first needs to be reserved. This is achieved by using the global ‘tm’ object and prevents other scripts from using it.
stbs = tm.reserve_stbs(['My STB']) tm.send('play', stbs)
Sending a Signal to All STBs Except One
To send a signal to all STBs except one, a list of STBs known about by TestManager is required.
all_stbs = tm.get_stb_names() my_stbs = [stb for stb in all_stbs if stb != 'STB-8'] stbs = tm.reserve_stbs(my_stbs) tm.send('play', stbs)
Sending a Signal to a Zone
rack1 = tm.reserve_zone('Rack 1') tm.send('play', rack1)
Sending a Signal Multiple Times
A signal can be sent multiple times using a For loop.
import time stb = tm.reserve_stbs(['my_stb']) for loopNum in range(0, 5): tm.send( ' Vol+ ', stb ) print 'Loop Number:', loopNum + 1 # Wait 1 second time.sleep( 1 )
Sending a Sequence of Signals
For actions which consist of multiple signals e.g. changing the channel number, a sequence of signals can be sent.
signals = ['1', '0', '1'] stb = tm.reserve_stbs(['my_stb']) for signal in signals: tm.send(signal, stb)
Using a Macro
A macro can be retrieved as follows:
all_macros = tm.get_macro_names() my_macro = [macro for macro in all_macros if macro == 'BBC1']
It can then be sent in a similar fashion to sending a signal.
rack1 = tm.reserve_zone('Rack 1') tm.send(my_macro, rack1)
Creating Automated Operations
The following code shows an example of how to create automated access to a TV’s Picture menu. First the necessary functions are created in a script called Utils.
class MenuAccess(): # Clear any menu currently accessed def __init__(self): tm.send('exit', stb); # Loop required in order to access menus by repeating signals (e.g. 'left', 'right') def MoveSelect(self, direction, amount): for i in range(0, amount): tm.send(direction, stb); # Access Main menu def MoveMenu(self, amount): tm.send('menu', stb); self.MoveSelect('right', amount); # Access Picture menu def GetPictureMenu(self): self.MoveMenu(1);
GetPictureMenu can then be called like so:
import scripts.Python.Utils as utils; # Allow access to 'tm' and 'stb' objects in 'Utils' utils.tm = tm; utils.stb = tm.reserve_stbs(['My TV']); # Access the Picture option in the TV menu utils.MenuAccess().GetPictureMenu();
Creating Logging Capabilities
The following code shows an example of how to create a logging file using a script called LogCreator.
import os; # Set the default directory path logDir = os.environ['HOMEPATH'] + '\Desktop\Logs'; class Logger(): def __init__(self): self.string = ''; # Create log directory if one does not exist if not os.path.isdir(logDir): os.makedirs(logDir); # Create log file if one does not exist if not os.path.exists(logDir + '\Last Session.txt'): fd = os.open(logDir + '\Last Session.txt', os.O_CREAT); os.close(fd); # Open file for writing only self.fd = os.open(logDir + '\Last Session.txt', os.O_WRONLY); # Set text to be written to file def Log(self, string): self.string = self.string + string + "\n"; # Write text and close file def End(self): os.write(self.fd, self.string); os.close(self.fd);
Log can then be called like so:
import time; import scripts.Python.LogCreator as logCreator; debug = logCreator.Logger(); # Give text to print to log file debug.Log("Hello world"); time.sleep(3); debug.Log("Goodbye world"); # Print to and Close log file debug.End();
Advanced: Writing Scripts for Concurrent Execution
The following code shows an example of how to send a sequence of signals on multiple threads using the .NET Task Parallel Library.
import clr clr.AddReference('System') clr.AddReference('System.Threading') clr.AddReference('System.Threading.Tasks') from System import * from System.Threading import * from System.Threading.Tasks import * import random import scripts.Python.TmTasks as tmtasks # Sends a signal and then waits a random period of time. def send(signal, stb): tm.send(signal, stb) Thread.Sleep(random.uniform(50, 2500)) # Sends a sequence of signals. def sendSeq(stb): tmtasks.printMessage("Starting output to " + stb.name) for count in range(0, 2): tmtasks.printMessage("Loop " + str(count) + " to " + stb.name) send('0', stb) send('1', stb) send('2', stb) send('3', stb) send('4', stb) tmtasks.printMessage("Output complete " + stb.name) # How to handle exceptions try: res = tm.get_stb_names() print res except Exception as inst: print type(inst) print inst # Reserve the STBs we want to use. stbs = tm.reserve_stbs(tm.get_stb_names()) tmtasks = tmtasks.TmTasks() # Create all our jobs and start them for i in range(len(stbs)): # Slightly strange syntax: 'x=param' is needed to capture the actual param instance. # See lambda function closures. tmtasks.startTask(lambda x=stbs[i]: sendSeq(x)) # Wait until all tasks finished. tmtasks.waitAll() print "Done!"
View the TestManager main page here