Plugin Formulas - A Gentle Tutorial
Not everyone aspires to be a Python programmer. If you don't know Python and have little interest in changing that, you're not completely out of luck. You just have accept that a certain amount of magic happens here, and your job is to come up with working incantations without getting burned. So to speak.
We will write examples like this:
What you put into the field
➼
What value the plugin uses for the field
If you want to follow along, you can use any dynamic field of any Cynical Plugin. The Cynical Explorer plugin has a field and button right in its configuration dialog - just type something and press the button. Or you can use the Message field of its Write Log
action.
You know what happens if you just type some text into a field - you get what you type:
Hello, there!
➼
Hello, there!
and that works just fine with a dynamic field, the kind that can contain a formula. To make the field do something surprising, you need to start with an equal sign "=":
The equal sign gets eaten, and the rest of the text gets taken to be the formula. Of course, 12345 is, well, 12345. Strings must be enclosed in single or double quotes:
='Just some text'
➼
Just some text
which produces exactly the same result as just typing in the string. This is also how you deal with an actual string that begins with an equal sign:
Did you ever try to put a
newline character into an Indigo field, though? With a dash of Python, it's easy:
='Here comes the end:\nVerily!
➼
Here comes the end:
Verily!
This is called a
string escape sequence, and it lets you put all kinds of funny characters into a string - even invisible binary ones.
Variables
Indigo has variables. Let's say you have a name
variable containing your name, Joe. How do you get a variable into a string? Like this:
Simple, no? If you change the value of
name
, the effective value of the field will magically change to match. Now
this is useful.
Of course, we can also do calculations:
Arithmetic is easy. So are string calculations:
="Hello, " + name + "!"
➼
Hello, Joe!
The "+" operator concatenates string values. It also adds up numbers; that's just how it works. So you need to pay attention to what kinds of values you work with. Python is apt to surprise you at times:
Multiplying a string with a number repeats it. Just be prepared to be amazed at times.
There are many other operators for calculations. If Python has it, you can use it. One useful one is the string formatting operator "%":
="Hello, %s! You have %d bottles." % (name, bottle_count)
➼
Hello, Joe! You have 7 bottles.
This is just a different way to put together string values. If you want to learn more about Python operators and how they work, check out the official
Python tutorial.
Functions
Python has functions, and they work just fine in plugin formulas:
calculates the length (in characters) of Joe. It can tell you how many things are contained in just about anything. Other functions are attached to things (they're called
methods when they do that), like
which puts Joe in ALL CAPS. To use those, you'll have to find out what they're called and what you put between those parentheses though, so you'll either have to look that up or ask a friend who knows Python. You can get pretty complicated, like so:
="high!" if temperature > 72 else "normal (%d)" % temperature
➼
normal (67)
and you can probably figure out what happens when it gets hot.
Objects
Don't get scared now. Objects don't bite. Much.
Playing with constants and variables is fun, but often you need the value of something inside Indigo itself:
=indigo.server.version
➼
5.1.2
In Python, you dig
inside things by writing their name, appending a dot and then saying what part of the object you mean. In this case, the name
indigo
contains stuff made by the Indigo programmers for us. Among that stuff is
indigo.server
, an object that represents Indigo itself to the plugin. And one of the attributes of
indigo.server
is its version. Check out the
Indigo scripting tutorial for some ideas of what you can use.
And we're just barely getting started here. The name plugin
refers to the plugin itself. It contains all kinds of useful things, like its version:
In fact,
plugin
is
the plugin object the Indigo documentation talks about. If the plugin has a preference field called
main_calendar
, then
=plugin.main_calendar
➼
Personal
will get it for you.
Devices
Let's say the plugin has a device called countdown, and that device has a state called count. (The Cynical Explorer plugin has such devices.) Then you can say
=devices.countdown.count
➼
56
and you'll get the value of the count of the countdown device. If the field you're typing into belongs to an action, and that action is for a particular device, then you can just say
device
for that, and you might fill in a
set counter action's value field with
to increment it by as much as you like.
The same approach also works if count
is a configuration field. And if that field is itself dynamic, we'll evaluate it for you. Neat, huh?
That "dot" naming only works if the device's name is a regular Python name with no funny characters like spaces. There's a different way to name recaltricant devices with odd names:
=devices["My Counter!!"].count
➼
56
Those square brackets let you use any value as the name. If you happen to know the
id number for a device, you can use it instead of the name. In fact, you can even calculate the name:
=devices[location + " counter"].count
➼
56
might get the
count
state or configuration field of the "Living Room counter" device - as long as it belongs to the plugin you're talking to. (Indigo won't let you mess with another plugin's devices without some special preparations.)
Modules
You can even talk directly to the operating system that Indigo is running on. Much of Python's power lies in its library of modules containing interesting things. In plugin formulas, you just start with the module name and keep going:
=time.asctime()
➼
Sat Jun 16 00:36:35 2012
to get the current time as a string. Or perhaps
=os.getenv("HOME")
➼
/Users/joe
to get the user's home directory. (That's the user running Indigo, not necessarily
you.)
Or even, somewhat precariously,
=open('/Users/joe/Desktop/testfile').read()
➼
This is what I put into my test file.
which produces the (entire) content of a text file on disk. The possibilities are endless; the limit is your imagination. That, and your patience in figuring out that Python thing...