Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
indigo_5_documentation:plugin_scripting_tutorial [2012/02/03 00:25]
jay [Miscellaneous Examples]
indigo_5_documentation:plugin_scripting_tutorial [2019/01/26 00:10] (current)
Line 1: Line 1:
 +====== Indigo Scripting Tutorial ======
 +===== Talking to the Indigo Server =====
 +
 +The [[plugin_guide#​Indigo Plugin Host|Indigo Plugin Host (IPH)]] provides a python scripting and plugin environment,​ which makes controlling the Indigo Server easy via the high-level python language (the official python web site has [[http://​docs.python.org/​tutorial/​|great python tutorials]]). Every plugin automatically runs inside its own Indigo Plugin Host process, but you can also start the IPH in an interactive mode to directly communicate with the Indigo Server. It is a great way to experiment with Indigo'​s python environment,​ which is available to both python scripts run by Indigo and the plugin architecture.
 +
 +To launch the Terminal utility application (inside //''/​Applications/​Utilities/''//​) in an Indigo shell mode choose the **''​Plugins->​Open Scripting Shell''​** menu item.
 +
 +-- or --
 +
 +If you already have a Terminal shell running, you can launch it directly. The file path to the IPH executable is a bit deep, so create an alias to it inside your //''​.bashrc''//​ (or //''​.bash_profile''//​) file by copying/​pasting this into the Terminal (you only need to do this once!):
 +
 +<​code>​echo "alias indigohost='/​Library/​Application\ Support/​Perceptive\ Automation/​Indigo\ 5/​IndigoPluginHost.app/​Contents/​MacOS/​IndigoPluginHost'"​ >> ~/​.bashrc</​code>​
 +
 +Next, close the Terminal window and open a new one to load the shortcut alias to the IPH executable. To start the IPH in interactive mode we pass the //''​-i''//​ flag to it inside the Terminal:
 +
 +<​code>​indigohost -i</​code>​
 +
 +{{pluginhost_prompt.png|}}
 +
 +As shown, the IPH will automatically connect to the IndigoServer running on the same Mac and will show the server'​s version information. Additionally,​ you'll notice that Indigo Server logs the connection of the interactive shell plugin.
 +
 +Next, let's tell the Indigo Server to log a message to the Event Log window (again, via the Terminal application):​
 +<​code>​indigo.server.log("​Hello world!"​)</​code>​
 +
 +{{pluginhost_helloworld.png|}}
 +
 +===== Connecting Remotely over SSH =====
 +
 +If you have SSH configured so you can remotely connect to your Mac running the Indigo Server, then you can use SSH to start the IPH interactively anywhere using the syntax:
 +
 +<​code>​ssh username@indigo_mac_ipaddr -t "/​Library/​Application\ Support/​Perceptive\ Automation/​Indigo\ 5/​IndigoPluginHost.app/​Contents/​MacOS/​IndigoPluginHost"​ -i</​code>​
 +
 +===== What Else Can it Do? =====
 +
 +The IPH gives you full access to the [[object_model_reference|Indigo Object Model (IOM)]] providing access to create/​edit/​delete/​control Indigo objects, as well as several Indigo [[server_commands|utility functions]],​ [[insteon_specific_commands|INSTEON device commands]], and [[x10_specific_commands|X10 device commands]]. Access to all of this functionality is provided by the indigo module automatically loaded and included in any python scripts run by the IPH (including interactive IPH sessions like we are using here).
 +
 +===== Executing Indigo Commands Directly =====
 +
 +In addition to communicating interactively with Indigo via the shell, you can also send direct python commands to Indigo via the IPH. For example, to get the current brightness of the device "​office lamplinc":​
 +
 +<​code>​indigohost -e '​return indigo.devices["​office lamplinc"​].brightness'</​code>​
 +
 +Or to toggle the device "​office lamplinc"​ three times:
 +
 +<​code>​indigohost -e '
 +indigo.device.toggle("​office lamplinc"​)
 +indigo.device.toggle("​office lamplinc"​)
 +indigo.device.toggle("​office lamplinc"​)
 +'</​code>​
 +
 +Note when your commands are executed the indigo module is already loaded and connected and you can execute standard python code (loops, conditionals,​ etc.).
 +
 +//Caveat:// Each call creates a new IPH (indigohost) process which must establish a connection to the Indigo Server. Although this is relatively fast, calling it multiple times a second is not recommended.
 +
 +===== Executing Indigo Python Files =====
 +
 +The IPH can also be used to execute Indigo python (.py) files, like this:
 +
 +<​code>​indigohost -x /​SomeFolder/​indigo_script.py'</​code>​
 +
 +//Caveat:// Each call creates a new IPH (indigohost) process which must establish a connection to the Indigo Server. Although this is relatively fast, calling it multiple times a second is not recommended.
 +
 +===== Shared Classes and Methods in Python Files (Python Attachments) =====
 +
 +In Indigo, if you want to write AppleScript handlers that are shared by any script that Indigo executes, you can just put an AppleScript full of handlers in the ///​Library/​Application Support/​Perceptive Automation/​Indigo 5/​Scripts/​Attachments//​ folder. You can do the same thing with Python using a built-in mechanism that Mac OS X supports for Python. There is a folder in the Library folder at the top level of your boot disk that is very similar to the Attachments folder:
 +
 +///​Library/​Python/​2.5/​site-packages//​
 +
 +Any Python files in this directory that define methods and/or classes will be loaded by the Python interpreter whenever it's loaded (note that it's the Library directory at the top level of your boot disk, not the one in your user folder). So, you can create files of handlers you want to share between all Python scripts. Files here can also import the entire IOM - IF the script that's actually running is started by Indigo. Here's a simple shell that you can use that will safely import the IOM and will show a specific error when used from a Python script that's not started by Indigo:
 +
 +<​code>#​!/​usr/​bin/​env python2.5
 +"""​
 +indigoAttachments.py
 +
 +In this file you can insert any methods and classes that you define.
 +They will be shared by all Python scripts - you can even import the
 +IOM (as shown below) but if you do then you'll only be able to import
 +this script in Python processes started by Indigo. If you don't need
 +the IOM then skip the import and it'll work in any Python script
 +no matter where it's run from.
 +"""​
 +
 +try:
 +    import indigo
 +except ImportError:​
 +    print "​Attachments can only be used from within Indigo"​
 +    raise ImportError
 +
 +def specialLog(text):​
 +    indigo.server.log(text,​ type="​indigoAttachments"​)
 +</​code>​
 +
 +Then, when you want to use any of the classes/​methods in the file, just import the file and go:
 +
 +<​code>​import indigoAttachments
 +indigoAttachments.specialLog("​Here'​s a special sort of log message"​)</​code>​
 +
 +If you try to run this script in a normal python session, you'll see the print statement followed by the ImportError:​
 +
 +<​code>​FatMac:​testdir jay$ python2.5 testHandlerCall.py ​
 +Attachments can only be used from within Indigo
 +Traceback (most recent call last):
 +  File "​testCallingIndigo.py",​ line 3, in <​module>​
 +    import indigoAttachments
 +  File "/​Users/​jay/​Library/​Python/​2.5/​site-packages/​indigoAttachments.py",​ line 14, in <​module>​
 +    raise ImportError
 +ImportError</​code>​
 +
 +===== Scripting Indigo Plugins =====
 +
 +Indigo plugins are also scriptable. Because plugin-defined devices look almost identical to built-in devices, you can get (but not set) state information from them (see [[#device examples]] a bit further down for a lot of examples). You can also get some of the other properties for a plugin. Most importantly you can execute plugin-defined actions, which is how you'd set their state values.
 +
 +The first step is to get an instance of the plugin:
 +
 +<​code>​iTunesId = "​com.perceptiveautomation.indigoplugin.itunes"​ # supplied by the plugin'​s documentation
 +iTunesPlugin = indigo.server.getPlugin(iTunesId)</​code>​
 +
 +This will **always** return to you a plugin object, defined with the following properties:
 +
 +|  pluginDisplayName ​ | The name displayed to users in the clients ​ |
 +|  pluginId ​ | The ID of the plugin (same as was passed to the getPlugin() method above) ​ |
 +|  pluginSupportURL ​ | The URL supplied by the plugin that points to it's user documentation ​ |
 +|  pluginVersion ​ | The version number specified by the plugin ​ |
 +
 +There are also a couple of methods defined by this object.
 +
 +|<​code>​isEnabled()</​code>​| This method will return //''​True''//​ if the plugin is enabled (running), //''​False''//​ otherwise ​ |
 +|<​code>​executeAction(actionId,​ deviceId, props)</​code>​| This is the method that actually executes the action. //''​actionId''//​ is the unique action id. //''​deviceId''//​ is the id of the device that the action on which the action will perform (if it requires a device). //''​props''//​ is a dictionary of properties that the action needs to process correctly. See the plugin'​s documentation for a description of what you need to pass in to execute actions. ​ |
 +|<​code>​restart(waitUntilDone=True)</​code>​| This method will restart the plugin if it's already running. Pass the optional parameter ''//​waitUntilDone=False//''​ if you don't want to block while the plugin restarts (waitUntilDone defaults to ''//​True//''​ if it's no parameters are passed). If you're a plugin developer and you want to restart the plugin from within itself you should pass that parameter. ​ |
 +
 +Plugin developers should [[indigo_5_documentation:​plugin_guide#​actionsxml|test and document their plugin actions]] to make sure that the necessary information is available to scripters.
 +
 +==== Examples ====
 +
 +=== iTunes Toggle Play State ===
 +
 +<​code>​itunesId = "​com.perceptiveautomation.indigoplugin.itunes"​
 +itunesPlugin = indigo.server.getPlugin(itunesId)
 +if itunesPlugin.isEnabled():​
 + itunesPlugin.executeAction("​playPause",​ deviceId=135305663)</​code>​
 +
 +=== iTunes Set Volume to 50 ===
 +
 +<​code>​itunesId = "​com.perceptiveautomation.indigoplugin.itunes"​
 +itunesPlugin = indigo.server.getPlugin(itunesId)
 +if itunesPlugin.isEnabled():​
 + itunesPlugin.executeAction("​setVolume",​ deviceId=135305663,​ props={'​volume':​50})</​code>​
 +
 +=== Pause iTunes and Speak NOAA Weather ===
 +
 +<​code>​itunesId = "​com.perceptiveautomation.indigoplugin.itunes"​
 +itunesPlugin = indigo.server.getPlugin(itunesId)
 +if itunesPlugin.isEnabled():​
 + myWeatherStation = indigo.devices[1798384204]
 + outsideTemp = myWeatherStation.states['​temperatureF'​]
 + currentCondition = myWeatherStation.states['​currentCondition'​]
 + spokenString = "The current temperature is %s degrees. Current condition is %s." % (outsideTemp,​ currentCondition)
 + itunesPlugin.executeAction("​pauseAndSay",​ deviceId=135305663,​ props={'​message':​spokenString})</​code>​
 +
 +===== =====
 +
 +Check the various plugin documentation for the necessary information and more examples.
 +===== Example Code Snippets =====
 +
 +Below are some example you can copy/paste directly into the Terminal application (running the IPH via the //''​indigohost -i''//​ command explained above). Keep in mind these are only examples, so be sure and read over the full [[object_model_reference|Indigo Object Model (IOM) Reference]].
 +
 +<color red>​Note:</​color>​ For simplicity some of the samples below specify objects based on name (//''"​office desk lamp"''//​). However, the preferred lookup mechanism is to use the object'​s ID which can be retrieved by control-clicking on the object name in Indigo'​s Main Window. By using the ID you ensure the object will be found even if its name is changed.
 +
 +==== Device Examples ====
 +
 +===Turn on the device "​office desk lamp":​===
 +<​code>​
 +indigo.device.turnOn("​office desk lamp")
 +</​code>​
 +
 +===Duplicate the device "​office desk lamp":​===
 +<​code>​
 +indigo.device.duplicate("​office desk lamp", duplicateName="​office desk lamp2"​)
 +</​code>​
 +
 +===In 4 seconds turn on the device "​office desk lamp" for 2 seconds:===
 +<​code>​
 +indigo.device.turnOn("​office desk lamp", duration=2, delay=4)
 +</​code>​
 +
 +===Turn off all devices:===
 +<​code>​
 +indigo.device.allOff()
 +</​code>​
 +
 +===Count the number of device modules:===
 +<​code>​
 +indigo.devices.len()
 +</​code>​
 +
 +===Count the number of dimmable device modules:===
 +<​code>​
 +indigo.devices.len(filter="​indigo.dimmer"​)
 +</​code>​
 +
 +===Count the number of devices defined by all of our plugin types:===
 +<​code>​
 +indigo.devices.len(filter="​self"​)
 +</​code>​
 +
 +===Count the number of irBlaster type devices defined by our plugin:===
 +<​code>​
 +indigo.devices.len(filter="​self.irBlaster"​)
 +</​code>​
 +
 +===Get the on state of a device if it has the onState property (uses Python'​s hasattr() introspection method):===
 +<​code>​
 +lamp = indigo.devices["​office desk lamp"]
 +if hasattr(lamp,​ '​onState'​):​
 + isOn = lamp.onState
 +</​code>​
 +
 +===Get the class of a device (uses Python'​s %%__class__%% property):​===
 +<​code>​
 +lamp = indigo.devices["​office desk lamp"]
 +if lamp.__class__ == indigo.DimmerDevice:​
 + theBrightness = lamp.brightness
 +</​code>​
 +
 +==== Variable Examples ====
 +
 +=== Create a new Indigo variable named fooMonster, change its value multiple times, and delete it: ===
 +<​code>​
 +newVar = indigo.variable.create("​fooMonster",​ "​default value"​)
 +indigo.variable.updateValue(newVar,​ "​asleep"​)
 +indigo.variable.updateValue(newVar,​ "​awake"​)
 +indigo.variable.delete(newVar)
 +</​code>​
 +
 +=== Getting a variable object and using it's value: ===
 +<​code>​myVar = indigo.variables[123]
 +if myVar.value == "​true":​
 +    indigo.server.log("​The variable had a value of '​true'"​)
 +</​code>​
 +
 +
 +=== Duplicating a variable: ===
 +<​code>​
 +indigo.variable.duplicate("​fooMonster",​ duplicateName="​fooMonsterSister"​)
 +</​code>​
 +
 +==== Date and Time Examples ====
 +
 +=== Get the current server time: ===
 +<​code>​
 +indigo.server.getTime()
 +</​code>​
 +
 +===Calculate the sunset time in 1 week:===
 +<​code>​
 +import datetime
 +one_week = indigo.server.getTime().date() + datetime.timedelta(days=7)
 +indigo.server.calculateSunset(one_week)
 +</​code>​
 +
 +==== Action Group Examples ====
 +
 +===Execute Action Group 12345678:​===
 +<​code>​
 +indigo.actionGroup.execute(12345678)
 +</​code>​
 +
 +
 +==== Log Examples ====
 +
 +===Log to the Indigo Event Log window all of the attributes/​properties of the device "​office desk lamp":​===
 +<​code>​
 +lamp = indigo.devices["​office desk lamp"]
 +indigo.server.log(lamp.name + ": \n" + str(lamp))
 +</​code>​
 +
 +===Print the last 5 Event Log entries:===
 +<​code>​
 +logList = indigo.server.getEventLogList(lineCount=5)
 +print(logList)
 +</​code>​
 +
 +==== Folder Examples ====
 +
 +===Iterate over a list of all device folders===
 +<​code>​
 +for folder in indigo.devices.folders:​
 + print "​Folder id: %s name: %s, remoteDisplay:​ %s" % (folder.id,​folder.name,​folder.remoteDisplay)
 +</​code>​
 +
 +===Create a trigger folder named "My Triggers"​ and if it exists just return the existing one===
 +<​code>​
 +try:
 + myFolder = indigo.triggers.folder.create("​My Triggers"​)
 +except ValueError, e:
 + if e.message == "​NameNotUniqueError":​
 + # a folder with that name already exists so just get it
 + myFolder = indigo.triggers.folders["​My Triggers"​]
 + else:
 + # you'll probably want to do something else to make myFolder a valid folder
 + myFolder = None
 +</​code>​
 +
 +===Make a folder visible in remote clients (IWS, Indigo Touch, etc.)===
 +<​code>​
 +indigo.devices.folder.displayInRemoteUI(123,​ value=True)
 +</​code>​
 +
 +===Getting the folder a device is in===
 +<​code>​
 +lamp = indigo.devices["​office desk lamp"]
 +# An object that's not in a folder will have a folder id of 0, which isn't a valid folder
 +# so we need to make sure it's a valid folder ID first
 +if lamp.folderId != 0:
 + lampsFolder = indigo.devices.folders[lamp.folderId]
 +else:
 + lampsFolder = None
 +</​code>​
 +
 +==== Miscellaneous Examples ====
 +
 +===Get a list of all serial ports, excluding any Bluetooth ports:===
 +<​code>​
 +indigo.server.getSerialPorts(filter="​indigo.ignoreBluetooth"​)
 +</​code>​
 +
 +=== Sending emails ===
 +<​code>#​ Simple Example
 +indigo.server.sendEmailTo("​emailaddress@company.com",​ subject="​Subject Line Here", body="​Some longish text for the body of the email"​)
 +
 +# Putting a variable'​s data into the subject and body
 +theVar = indigo.variables[928734897]
 +theSubject = "The value of %s" % (theVar.name)
 +theBody = "The value of %s is now %s" % (theVar.name,​ theVar.value)
 +indigo.server.sendEmailTo("​emailaddress@company.com",​ subject=theSubject,​ body=theBody)
 +
 +# Putting device data into the subject and body
 +theDevice = indigo.devices[980532604]
 +theSubject = "​Summary of %s" % (theDevice.name)
 +theBody = "%s is %s\n%s is %s" % ("​onState",​ str(theDevice.onState),​ "​lastChanged",​ str(theDevice.lastChanged))
 +indigo.server.sendEmailTo("​emailaddress@company.com",​ subject=theSubject,​ body=theBody)
 +</​code>​
 +
 +
 +
 +FIXME Add examples for:
 +  * refreshing objects
 +  * replacing objects
 +  * enabling/​disabling objects
 +  * sprinkler control
 +  * etc.
  
 

© Perceptive Automation, LLC. · Privacy