This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
indigo_5_documentation:plugin_extending_tutorial [2012/02/19 17:16] mattb |
indigo_5_documentation:plugin_extending_tutorial [2019/01/26 00:10] (current) |
||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Indigo Server Plugin Tutorial ====== | ||
+ | Note most of the code examples below are calling methods on the object //''self''//. In this context, //''self''// is meant to be the Plugin instance as defined inside the //''plugin.py''// file. To execute the sample code outside of Plugin instance methods use the //''indigo.activePlugin''// object instead. | ||
+ | |||
+ | //Important:// 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. | ||
+ | |||
+ | ===== Indigo Plugin SDK Source Code Examples ===== | ||
+ | |||
+ | The Indigo Plugin SDK ([[http://www.indigodomo.com/Indigo5SDK|available here]]) includes example plugins with full XML and python source. They are a great place to start when developing new plugins. | ||
+ | |||
+ | Included in the SDK are examples that create plugin based relay, dimmer, thermostat, and custom devices. Also included is an example showing basic Indigo database traversal, how to catch low-level X10/INSTEON messages, and how to create an Indigo telnet server using the python twisted framework. | ||
+ | |||
+ | We'll be adding additional example plugins in the future. | ||
+ | |||
+ | ===== Other Useful Plugin Source Code Examples ===== | ||
+ | |||
+ | Additionally, below is a table of common plugin tasks and built-in plugins that implement those tasks in some form or another (from simplest to most complex): | ||
+ | |||
+ | ^ Plugin task ^ Plugin that illustrates an approach ^ | ||
+ | | Parsing XML from an IP source | NOAA Weather, WeatherSnoop | | ||
+ | | Integrating with native Mac Apps | Airfoil | | ||
+ | | Sending RS232 (serial port & network serial port) Commands | EasyDAQ | | ||
+ | | Reading RS232 (serial & network serial port) Input | EasyDAQ | | ||
+ | | Interacting with an IMAP mail server | iCal Alarm Processor | | ||
+ | | Creating custom devices with states | Simple: NOAA Weather, WeatherSnoop - Complex: EasyDAQ, iTunes | | ||
+ | | Creating custom actions | Growl, Action Collection | | ||
+ | | Creating custom events | Airfoil | | ||
+ | |||
+ | Each of these plugins is installed by default with Indigo 5 - in the ''/Library/Application Support/Perceptive Automation/Indigo 5/Plugins (Disabled)/'' folder. To see the various XML and python source files, just right-click on it in the **''Finder''** and select ''**Show Package Contents**''. | ||
+ | |||
+ | Both the SDK example plugins and the plugins included with Indigo above are great places to see working examples of plugins and their source code. | ||
+ | |||
+ | ===== How to Read and Write Plugin Preferences ===== | ||
+ | |||
+ | FIXME (useful, but very rough notes below) | ||
+ | |||
+ | * Per plugin prefs file is automatically managed (created, loaded, updated). | ||
+ | * Pref values can be numbers, boolean, strings, indigo.Dict() or indigo.List(). | ||
+ | * Key values defined in PluginConfig.xml are automatically mapped into the plugin's prefs space which is available via:<code> | ||
+ | self.pluginPrefs["somePrefKey"] | ||
+ | </code> or, if you're not sure the key exists:<code>self.pluginPrefs.get("somePrefKey", "default value if key doesn't exist")</code> The latter will return the second parameter if they key doesn't exist in the dictionary - it's your responsibility to add it to the prefs dict if you want it to be stored permanently. | ||
+ | * Plugin can also insert other values into its pluginPrefs space (not just values shown in the plugin's config UI). | ||
+ | * To read a preference value access its key:<code> | ||
+ | someVal = self.pluginPrefs["somePrefKey"] | ||
+ | indigo.server.log("value is " + str(someVal)) | ||
+ | </code> | ||
+ | * To update a preference value assign it a new value:<code> | ||
+ | self.pluginPrefs["somePrefKey"] = 1234 | ||
+ | </code> | ||
+ | |||
+ | ===== How to Add Plugin Metadata to Devices, Trigger & Scheduled Events, Variables, etc. ===== | ||
+ | |||
+ | FIXME (useful, but very rough notes below) | ||
+ | |||
+ | * Most Indigo database objects support the addition of plugin specific metadata. | ||
+ | * Every plugin has its own name space accessed via the object instance //''.pluginProps''//. | ||
+ | * The pluginProps dictionary supports numbers, boolean, strings, indigo.Dict() or indigo.List(). | ||
+ | * Example adding new plugin metadata to the Device "den fixture":<code> | ||
+ | dev = indigo.devices["den fixture"] | ||
+ | newProps = dev.pluginProps | ||
+ | newProps["onCycles"] = 5 | ||
+ | newProps["moreData1"] = "abc" | ||
+ | newProps["moreData2"] = True | ||
+ | newProps["moreData3"] = 123.45 | ||
+ | dev.replacePluginPropsOnServer(newProps) | ||
+ | </code> | ||
+ | * Example reading the plugin specific property onCycles from the Device "den fixture":<code> | ||
+ | dev = indigo.devices["den fixture"] | ||
+ | onCycles = dev.pluginProps["onCycles"] | ||
+ | indigo.server.log("onCycles is " + str(onCycles)) | ||
+ | </code> | ||
+ | * Example incrementing by 1 the plugin specific property onCycles for the Device "den fixture":<code> | ||
+ | dev = indigo.devices["den fixture"] | ||
+ | newProps = dev.pluginProps | ||
+ | newProps["onCycles"] += 1 | ||
+ | dev.replacePluginPropsOnServer(newProps) | ||
+ | |||
+ | dev = indigo.devices["den fixture"] | ||
+ | onCycles = dev.pluginProps["onCycles"] | ||
+ | indigo.server.log("onCycles is now " + str(onCycles)) | ||
+ | </code> | ||
+ | * Plugins have read-only access to other plugin metadata via //''.globalProps''//. | ||
+ | * Plugins have read/write access to their own metadata space. | ||
+ | |||
+ | ===== Creating a Custom Plugin Device ===== | ||
+ | |||
+ | FIXME (useful, but very rough notes below) | ||
+ | |||
+ | * Plugin Device state and properties are defined in Devices.xml. | ||
+ | * Properties define the user configurable options for a device instance, and are specified in the //''<ConfigUI>''// XML node. Every field //''id''// is automatically mapped into the device instance //''.pluginProps''// metadata dictionary (described above) as a unique key. | ||
+ | * States are specified in the Devices.xml //''<States>''// XML node, and are used to define the transient state information for a device (ex: on/off setting, brightness, temperature, etc.). | ||
+ | * States defined in Devices.xml are automatically shows in the Trigger Event ''//Device State Changed//'' options when that plugin device type is selected, and are automatically shown in the Control Page editor when a control is created inspecting that plugin device. | ||
+ | * States are read-only for everyone except the plugin that defines the device's states. | ||
+ | * Plugins should update a device state after it has sent commands to hardware, or somehow received new state information from hardware. Example that increments the plugin defined state ''//heatSetPoint//'' by 1:<code> | ||
+ | dev = indigo.devices["Custom Plugin Thermostat"] | ||
+ | dev.updateStateOnServer("heatSetPoint", dev.states["heatSetPoint"] + 1) | ||
+ | </code> | ||
+ | * Plugins should subclass ''//deviceStartComm//'' and ''//deviceEndComm//'' to start/stop any hardware communication (normally via a new per-device thread):<code> | ||
+ | def deviceStartComm(self, dev): | ||
+ | self.easydaq.startCommThread(dev) | ||
+ | |||
+ | def deviceStopComm(self, dev): | ||
+ | self.easydaq.stopCommThread(dev) | ||
+ | </code> | ||
+ | * Calls to ''//deviceStartComm//'' and ''//deviceEndComm//'' are automatically managed by the Indigo Server and Indigo Plugin Host. When a plugin first connects all enabled device instances owned by the plugin will receive ''//deviceStartComm//'' calls. Likewise, ''//deviceStartComm//'' is called when a new plugin device is created or duplicated. ''//deviceEndComm//'' is called whenever a plugin is disabled, deleted, or when the plugin is shutting down. Therefore, these two functions should be the primary bottlenecks for starting/stopping device hardware or network connections. | ||
+ | |||
+ | ===== Creating a Custom Plugin Trigger Event ===== | ||
+ | |||
+ | FIXME Explain Events.xml. Show example plugin subclasses of functions: triggerStartProcessing, triggerStopProcessing, didTriggerProcessingPropertyChange. Note it works very similarly to devices. | ||
+ | |||
+ | ===== Monitoring Device State Changes from a Plugin ===== | ||
+ | |||
+ | FIXME (useful, but very rough notes below) | ||
+ | |||
+ | FIXME Give examples of using indigo.devices.subscribeToChanges() and plugin subclasses of functions: deviceCreated, deviceDeleted, deviceUpdated. | ||
+ | |||
+ | ===== Monitoring Changes to Variables, Triggered & Scheduled Events, Action Groups, and Control Pages ===== | ||
+ | |||
+ | FIXME (useful, but very rough notes below) | ||
+ | |||
+ | FIXME Give examples of using indigo.*.subscribeToChanges() and plugin subclasses of functions: variableCreated, variableDeleted, variableUpdated, triggerCreated, triggerDeleted, triggerUpdated, scheduleCreated, scheduleDeleted, scheduleUpdated, actionGroupCreated, actionGroupDeleted, actionGroupUpdated, controlPageCreated, controlPageDeleted, controlPageUpdated. |