Dynamic Lists and Filters¶
You may create dynamic menus and lists that are created at runtime. Indigo will supply some specific ones for you or your plugin may be called each time the UI is presented that will allow you to return anything you like.
To specify a dynamic list, you add some attributes to the List element and skip adding Option elements. To request any of the built-in lists, you specify a class attribute and (optionally) a filter attribute. For example, this field:
<Field id="insteonDimmers" type="menu">
<Label>Insteon Dimmers:</Label>
<List class="indigo.devices" filter="indigo.dimmer,indigo.insteon"/>
</Field>
would request a list of all Insteon dimmer devices. The list would be constructed with the device name as the menu or list item viewable by the user, and the value would be the device ID.
Attributes
| Attributes | ||
|---|---|---|
| Attribute | Required | Notes |
class |
Yes | A reference to a class value as described below. The class attribute must be set to one or more of the built-in classes (listed below), or self. |
filter |
No | A filter attribute is not required, but if one is included, it must be set to a valid filter (see below) or empty filter="". |
method |
No | A reference to a plugin.py method that returns the list elements as described below. If not included, class must refer to one of the built-in methods (class="self" by itself will not work). |
Built-in Classes
| Class | What the list contains |
|---|---|
indigo.devices |
devices. Accepts a filter. |
indigo.triggers |
triggers. Accepts a filter. |
indigo.variables |
variables. Accepts indigo.readWrite to get only the ones you can change. |
indigo.schedules |
schedules. The filter is ignored. |
indigo.actionGroups |
action groups. The filter is ignored. |
indigo.controlPages |
control pages. The filter is ignored. |
indigo.serialPorts |
available serial ports. Accepts indigo.ignoreBluetooth. |
Device and Trigger Filters
The filter attribute takes exactly the same strings as the IOM's iter(), keys() and len(),
and is parsed by the same code on the server. Rather than repeat the tables, the complete
reference lives with the IOM:
- Device filters — the protocol, class,
props.<key>, responder, controller and plugin groups, and how they combine. Tokens within a group are ORed and the groups are ANDed, sofilter="indigo.relay,indigo.dimmer"widens to relays or dimmers, whilefilter="indigo.zwave,indigo.relay"narrows to Z-Wave relays. - Trigger filters — every recognized token is ANDed, so only one class token will produce a useful list.
- Filter rules — separators, case sensitivity, and the warning that an unrecognized token is silently ignored rather than rejected, which makes a misspelled filter return more than you asked for.
Filters supplied to indigo.schedules, indigo.actionGroups and indigo.controlPages are parsed
by a list that recognizes no tokens. Supplying one is not an error; it simply has no effect.
Only variables have a read-only case worth mentioning: currently only isDaylight is defined as
read-only by the system, though it may become possible to create others in future versions of
the API.
Another special built-in list is serial ports — use indigo.serialPorts to get a list of
available serial ports, and what's returned to your plugin is the full path to the port (e.g.
/dev/tty*). This is suitable for using with PySerial, which we include with Indigo. Add
filter="indigo.ignoreBluetooth" to leave Bluetooth serial ports out of the list. See the
"serialport" field type below for an even better way of collecting serial communication
information.
You may also include custom dynamic lists that are constructed on-the-fly by your plugin. If you defined your list like this:
<Field id="insteonDimmers" type="menu">
<Label>Insteon Dimmers:</Label>
<List class="self" filter="stuff" method="myListGenerator"/>
</Field>
Then your plugin will have the method specified called with the filter. For the above example, you must define a method like this:
def myListGenerator(self, filter="", valuesDict=None, typeId="", targetId=0):
# From the example above, filter = "stuff"
# You can pass anything you want in the filter for any purpose
# Create a list where each entry is a list - the first item is
# the value attribute and last is the display string that will
# show up in the control. All parameters are read-only.
myArray = [("option1", "First Option"),("option2","Second Option")]
return myArray
Note
Both return tuple values should be strings. Specifically, items with None as the value (first item) will be skipped. Additionally, the first tuple value (option1 and option2 in the above example) must be a string that does not contain comma or semicolon characters.
The valuesDict parameter will contain the valuesDict for the object being edited - if it's a device config UI then it'll be the valuesDict from that device (just like what's passed in to validation methods), etc. Note: if it's a new object that hasn't been saved yet, valuesDict may be None or empty so test accordingly.
The typeId parameter will contain the type - for instance, if it's an event, it will be the event type id. The targetId is the ID of the object being edited. It will be 0 if it's a new object that hasn't been saved yet.
The field would then be created as if you had specified it statically like this:
<Field id="customList" type="menu">
<Label>Select an Option:</Label>
<List>
<Option value="option1">First Option</Option>
<Option value="option2">Second Option</Option>
</List>
</Field>
This mechanism allows you to create any number of lists: devices dynamically discovered through some discovery protocol, calendars available, email addresses, iTunes playlists, etc.
One further option to dynamic lists is the ability to have them reload whenever the config dialog returns from a button or menu callback. So, for instance, if you have a button that somehow changes the contents of the dynamic list, you can specify the list as dynamically reloaded:
<Field id="insteonDimmers" type="menu">
<Label>Insteon Dimmers:</Label>
<List class="self" filter="stuff" method="myListGenerator" dynamicReload="true"/>
</Field>
Note
This will cause an extra round-trip (client->Indigo server->plugin->Indigo server->client) so you should only use this when the list can change while the dialog is actually running.