Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| indigo_7.3_documentation:plugin_scripting_tutorial [2019/09/27 16:27] – [Shared Classes and Methods in Python Files (Python Modules)] jay | indigo_7.3_documentation:plugin_scripting_tutorial [2025/04/14 20:10] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== Indigo Scripting Tutorial ====== | ||
| + | ===== Talking to the Indigo Server ===== | ||
| + | |||
| + | The [[plugin_guide# | ||
| + | |||
| + | To launch the Terminal utility application (inside //''/ | ||
| + | |||
| + | ===== Example Code Snippets ===== | ||
| + | |||
| + | Below are some example you can copy/paste directly into the IPH window opened when following the directions 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> | ||
| + | |||
| + | ==== Device Examples ==== | ||
| + | |||
| + | ===Turn on the device " | ||
| + | < | ||
| + | indigo.device.turnOn(1234567890) # Preferred, where the number is the ID of " | ||
| + | # OR the less preferred way because it will break if you rename the device | ||
| + | indigo.device.turnOn(" | ||
| + | </ | ||
| + | ===Duplicate the device " | ||
| + | < | ||
| + | indigo.device.duplicate(1234567890, | ||
| + | </ | ||
| + | ===In 4 seconds turn on the device " | ||
| + | < | ||
| + | indigo.device.turnOn(1234567890, | ||
| + | </ | ||
| + | ===Turn off all devices:=== | ||
| + | < | ||
| + | indigo.device.allOff() | ||
| + | </ | ||
| + | |||
| + | ===Count the number of device modules:=== | ||
| + | < | ||
| + | indigo.devices.len() | ||
| + | </ | ||
| + | |||
| + | ===Count the number of dimmable device modules:=== | ||
| + | < | ||
| + | indigo.devices.len(filter=" | ||
| + | </ | ||
| + | |||
| + | ===Count the number of devices defined by all of our plugin types:=== | ||
| + | < | ||
| + | indigo.devices.len(filter=" | ||
| + | </ | ||
| + | |||
| + | ===Count the number of irBlaster type devices defined by our plugin:=== | ||
| + | < | ||
| + | indigo.devices.len(filter=" | ||
| + | </ | ||
| + | |||
| + | ===Get the on state of a device if it has the onState property (uses Python' | ||
| + | < | ||
| + | lamp = indigo.devices[1234567890] # where the number is the ID of " | ||
| + | if hasattr(lamp, | ||
| + | isOn = lamp.onState | ||
| + | </ | ||
| + | ===Get the class of a device (uses Python' | ||
| + | < | ||
| + | lamp = indigo.devices[1234567890] # where the number is the ID of " | ||
| + | if lamp.__class__ == indigo.DimmerDevice: | ||
| + | theBrightness = lamp.brightness | ||
| + | </ | ||
| + | ===Turn on a light only if it's been off for longer than 1 minute:=== | ||
| + | < | ||
| + | from datetime import datetime | ||
| + | lamp = indigo.devices[91776575] # ID of " | ||
| + | timeDelta = datetime.now() - lamp.lastChanged | ||
| + | if not lamp.onState and timeDelta.seconds > 60: | ||
| + | indigo.device.turnOn(91776575) # ID of " | ||
| + | </ | ||
| + | === Access a custom device state === | ||
| + | < | ||
| + | # get the device | ||
| + | dev = indigo.devices[23989834] | ||
| + | # access the state through the states property, use the key | ||
| + | # that's displayed in the Custom States tile on the main window | ||
| + | # when you have a custom device selected | ||
| + | print dev.states[" | ||
| + | |||
| + | # show all the device states (use unicode() to print special chars correctly): | ||
| + | print unicode(dev.states) | ||
| + | </ | ||
| + | ==== Variable Examples ==== | ||
| + | |||
| + | === Create a new Indigo variable named fooMonster, change its value multiple times, and delete it: === | ||
| + | < | ||
| + | newVar = indigo.variable.create(" | ||
| + | indigo.variable.updateValue(newVar, | ||
| + | indigo.variable.updateValue(newVar, | ||
| + | indigo.variable.delete(newVar) | ||
| + | </ | ||
| + | |||
| + | === Getting a variable object and using its value: === | ||
| + | < | ||
| + | if myVar.value == " | ||
| + | indigo.server.log(" | ||
| + | </ | ||
| + | |||
| + | |||
| + | === Duplicating a variable: === | ||
| + | < | ||
| + | indigo.variable.duplicate(" | ||
| + | </ | ||
| + | |||
| + | === Using a variable value in an HTTP GET === | ||
| + | |||
| + | < | ||
| + | myVar = indigo.variables[1893500335] # always use variable ID rather than name | ||
| + | import urllib, urllib2 | ||
| + | urllib2.urlopen(" | ||
| + | </ | ||
| + | === Setting a variable to Python types === | ||
| + | |||
| + | Since Indigo variable values are always unicode strings, you have to convert anything that's not a string. It's safest to use the unicode() method: | ||
| + | |||
| + | < | ||
| + | myAsciiString = "ASCII String" | ||
| + | indigo.variable.updateValue(1234567, | ||
| + | |||
| + | myUnicodeString = u" | ||
| + | indigo.variable.updateValue(1234567, | ||
| + | |||
| + | myInteger = 1 | ||
| + | indigo.variable.updateValue(1234567, | ||
| + | |||
| + | myFloat = 1.0 | ||
| + | indigo.variable.updateValue(1234567, | ||
| + | |||
| + | myList = [" | ||
| + | indigo.variable.updateValue(1234567, | ||
| + | |||
| + | myDictionary = {" | ||
| + | indigo.variable.updateValue(1234567, | ||
| + | </ | ||
| + | |||
| + | Any Python object that can be converted to a unicode string can then be inserted into an Indigo variable. <color red> | ||
| + | |||
| + | < | ||
| + | import simplejson as json | ||
| + | myList = [" | ||
| + | myDictionary = {" | ||
| + | indigo.variable.updateValue(1234567, | ||
| + | # string will be something like: ' | ||
| + | myNewDictionary = json.loads(indigo.variables[1234567].value) | ||
| + | # | ||
| + | myNewDictionary[" | ||
| + | # results in: [' | ||
| + | </ | ||
| + | |||
| + | Custom Python classes that you create can implement the %%__unicode__(self)%% method. Take this simple example: | ||
| + | |||
| + | < | ||
| + | class myCustomClass: | ||
| + | def __init__(self): | ||
| + | self.a=1 | ||
| + | self.b=2 | ||
| + | def __unicode__(self): | ||
| + | outputString = u" | ||
| + | return outputString | ||
| + | </ | ||
| + | |||
| + | If you have an instance of that class, then you can create a string representation of the class to insert into an Indigo variable: | ||
| + | |||
| + | < | ||
| + | cl = myCustomClass() | ||
| + | indigo.variable.updateValue(1234567, | ||
| + | # the value of the variable in Indigo will look like this: 'a:1, b:2' not including the quotes | ||
| + | </ | ||
| + | |||
| + | For further information on Python classes, [[http:// | ||
| + | ==== Date and Time Examples ==== | ||
| + | |||
| + | === Get the current server time: === | ||
| + | < | ||
| + | indigo.server.getTime() | ||
| + | </ | ||
| + | |||
| + | ===Calculate the sunset time in 1 week:=== | ||
| + | < | ||
| + | import datetime | ||
| + | one_week = indigo.server.getTime().date() + datetime.timedelta(days=7) | ||
| + | indigo.server.calculateSunset(one_week) | ||
| + | </ | ||
| + | |||
| + | ==== Action Group Examples ==== | ||
| + | |||
| + | ===Execute Action Group 12345678: | ||
| + | < | ||
| + | indigo.actionGroup.execute(12345678) | ||
| + | </ | ||
| + | |||
| + | |||
| + | ==== Log Examples ==== | ||
| + | |||
| + | ===Log to the Indigo Event Log window all the attributes/ | ||
| + | < | ||
| + | lamp = indigo.devices[" | ||
| + | indigo.server.log(" | ||
| + | </ | ||
| + | |||
| + | ===Print the last 5 Event Log entries:=== | ||
| + | < | ||
| + | logList = indigo.server.getEventLogList(lineCount=5) | ||
| + | print(logList) | ||
| + | </ | ||
| + | |||
| + | ==== Folder Examples ==== | ||
| + | |||
| + | ===Iterate over a list of all device folders=== | ||
| + | < | ||
| + | for folder in indigo.devices.folders: | ||
| + | print " | ||
| + | </ | ||
| + | |||
| + | ===Create a trigger folder named "My Triggers" | ||
| + | < | ||
| + | try: | ||
| + | myFolder = indigo.triggers.folder.create(" | ||
| + | except ValueError, e: | ||
| + | if e.message == " | ||
| + | # a folder with that name already exists so just get it | ||
| + | myFolder = indigo.triggers.folders[" | ||
| + | else: | ||
| + | # you'll probably want to do something else to make myFolder a valid folder | ||
| + | myFolder = None | ||
| + | </ | ||
| + | |||
| + | ===Make a folder visible in remote clients (IWS, Indigo Touch, etc.)=== | ||
| + | < | ||
| + | indigo.devices.folder.displayInRemoteUI(123, | ||
| + | </ | ||
| + | |||
| + | ===Getting the folder a device is in=== | ||
| + | < | ||
| + | lamp = indigo.devices[" | ||
| + | # 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 | ||
| + | </ | ||
| + | |||
| + | ==== Miscellaneous Examples ==== | ||
| + | |||
| + | ===Get a list of all serial ports, excluding any Bluetooth ports:=== | ||
| + | < | ||
| + | indigo.server.getSerialPorts(filter=" | ||
| + | </ | ||
| + | |||
| + | === Sending emails === | ||
| + | < | ||
| + | indigo.server.sendEmailTo(" | ||
| + | |||
| + | # Putting a variable' | ||
| + | theVar = indigo.variables[928734897] | ||
| + | theSubject = "The value of %s" % (theVar.name) | ||
| + | theBody = "The value of %s is now %s" % (theVar.name, | ||
| + | indigo.server.sendEmailTo(" | ||
| + | |||
| + | # Putting device data into the subject and body | ||
| + | theDevice = indigo.devices[980532604] | ||
| + | theSubject = " | ||
| + | theBody = "%s is %s\n%s is %s" % (" | ||
| + | indigo.server.sendEmailTo(" | ||
| + | </ | ||
| + | |||
| + | ===== Starting the host from an existing terminal window ===== | ||
| + | |||
| + | If you already have a Terminal shell running, you can launch it directly. To start the IPH in interactive mode just execute the following inside the Terminal: | ||
| + | |||
| + | < | ||
| + | |||
| + | {{pluginhost_prompt.png? | ||
| + | |||
| + | As shown, the IPH will automatically connect to the IndigoServer running on the same Mac and will show the server' | ||
| + | |||
| + | Next, let's tell the Indigo Server to log a message to the Event Log window (again, via the Terminal application): | ||
| + | < | ||
| + | |||
| + | {{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: | ||
| + | |||
| + | < | ||
| + | |||
| + | ===== What Else Can it Do? ===== | ||
| + | |||
| + | The IPH gives you full access to the [[object_model_reference|Indigo Object Model (IOM)]] providing access to create/ | ||
| + | |||
| + | ===== 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 " | ||
| + | |||
| + | < | ||
| + | |||
| + | Or to toggle the device " | ||
| + | |||
| + | < | ||
| + | indigo.device.toggle(" | ||
| + | indigo.device.toggle(" | ||
| + | indigo.device.toggle(" | ||
| + | '</ | ||
| + | |||
| + | Note when your commands are executed the indigo module is already loaded and connected and you can execute standard python code (loops, conditionals, | ||
| + | |||
| + | //Caveat:// Each call creates a new IPH (indigo-host) 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: | ||
| + | |||
| + | < | ||
| + | |||
| + | //Caveat:// Each call creates a new IPH (indigo-host) 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 AppleScript ===== | ||
| + | |||
| + | Often times, you may find yourself wanting to execute an AppleScript from Python. You want to send some parameters to the AppleScript and you want to get results back in some format. There' | ||
| + | |||
| + | This is a great pattern for calling AppleScripts from Python, and may help alleviate some of the pain of the Mac OS X “application isn't running” bug (error -600). | ||
| + | ===== Shared Classes and Methods in Python Files (Python Modules) ===== | ||
| + | |||
| + | You may install Python modules/ | ||
| + | |||
| + | /// | ||
| + | |||
| + | Of course, you can do the same thing with Python using a built-in mechanism that Python provides if you want the module/ | ||
| + | |||
| + | /// | ||
| + | |||
| + | Any Python files in this directory that define methods and/or classes will be available to any Python script whenever the interpreter is loaded (note that it's the Library directory at the top level of your boot disk, not the one in your user folder). These are referred to as Python modules. | ||
| + | |||
| + | So, you can create files of classes, functions, etc. you want to share between all Python scripts using these mechanisms. If you add/change something in that directory while the Indigo Server is running, you'll need to tell the server to reload - select the '' | ||
| + | |||
| + | Files added to the generic module location can also import the entire IOM - IF the script that's actually running is started by Indigo. Here's a simple script 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: | ||
| + | |||
| + | < | ||
| + | indigo_attachments.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 "The indigo module can only be used by scripts started from within Indigo" | ||
| + | raise ImportError | ||
| + | from datetime import datetime | ||
| + | |||
| + | def log(message, | ||
| + | # Create a log line with the date/ | ||
| + | log_line ="%s %s" % (datetime.today().strftime(" | ||
| + | # Write the log line with the label. If you didn't pass in a label | ||
| + | # then the default label will be used. | ||
| + | indigo.server.log(log_line, | ||
| + | </ | ||
| + | |||
| + | Then, when you want to use any of the classes/ | ||
| + | |||
| + | < | ||
| + | indigo_attachments.log(" | ||
| + | |||
| + | What you'll see in the Event Log: | ||
| + | |||
| + | < | ||
| + | |||
| + | If you try to run this script in a normal python session, you'll see the print statement followed by the ImportError: | ||
| + | |||
| + | < | ||
| + | The indigo module can only be used by scripts started from within Indigo | ||
| + | Traceback (most recent call last): | ||
| + | File " | ||
| + | import indigo_attachments | ||
| + | File "/ | ||
| + | raise ImportError | ||
| + | ImportError</ | ||
| + | ===== 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]] above 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: | ||
| + | |||
| + | < | ||
| + | iTunesPlugin = indigo.server.getPlugin(iTunesId)</ | ||
| + | |||
| + | This will **always** return to you a plugin object, defined with the following properties: | ||
| + | |||
| + | | pluginDisplayName | ||
| + | | pluginId | ||
| + | | pluginSupportURL | ||
| + | | pluginVersion | ||
| + | |||
| + | There are also a couple of methods defined by this object. | ||
| + | |||
| + | |< | ||
| + | |< | ||
| + | |< | ||
| + | |||
| + | Plugin developers should [[indigo_7.3_documentation: | ||
| + | |||
| + | ==== Examples ==== | ||
| + | |||
| + | === iTunes Toggle Play State === | ||
| + | |||
| + | < | ||
| + | itunesPlugin = indigo.server.getPlugin(itunesId) | ||
| + | if itunesPlugin.isEnabled(): | ||
| + | itunesPlugin.executeAction(" | ||
| + | |||
| + | === iTunes Set Volume to 50 === | ||
| + | |||
| + | < | ||
| + | itunesPlugin = indigo.server.getPlugin(itunesId) | ||
| + | if itunesPlugin.isEnabled(): | ||
| + | itunesPlugin.executeAction(" | ||
| + | |||
| + | === Pause iTunes and Speak NOAA Weather === | ||
| + | |||
| + | < | ||
| + | itunesPlugin = indigo.server.getPlugin(itunesId) | ||
| + | if itunesPlugin.isEnabled(): | ||
| + | myWeatherStation = indigo.devices[1798384204] | ||
| + | outsideTemp = myWeatherStation.states[' | ||
| + | currentCondition = myWeatherStation.states[' | ||
| + | spokenString = "The current temperature is %s degrees. Current condition is %s." % (outsideTemp, | ||
| + | itunesPlugin.executeAction(" | ||
| + | |||
| + | === Restart a Plugin === | ||
| + | |||
| + | There may be instances where you want to restart a plugin. This is how you do that: | ||
| + | |||
| + | < | ||
| + | itunesPlugin = indigo.server.getPlugin(itunesId) | ||
| + | if itunesPlugin.isEnabled(): | ||
| + | itunesPlugin.restart()</ | ||
| + | |||
| + | |||
| + | ===== ===== | ||
| + | |||
| + | Check the various plugin documentation for the necessary information and more examples. | ||