Differences

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

Link to this comparison view

Both sides previous revision Previous revision
indigo_2024.1_documentation:plugin_guide [2025/01/06 17:34] – [Custom HTML Menu Item Dialogs] davel17indigo_2024.1_documentation:plugin_guide [2025/04/14 20:10] (current) – external edit 127.0.0.1
Line 15: Line 15:
 Indigo's folder structure looks like this: Indigo's folder structure looks like this:
  
-{{folder_structure.png|}}+{{folder_structure.png?nolink|Folder Structure Image}}
  
 in this location: in this location:
Line 29: Line 29:
 We created a macOS Finder bundle type, the Indigo plugin bundle (//''.indigoPlugin''//), which has a very specific structure to encapsulate everything that a plugin needs to perform it's functions: We created a macOS Finder bundle type, the Indigo plugin bundle (//''.indigoPlugin''//), which has a very specific structure to encapsulate everything that a plugin needs to perform it's functions:
  
-{{bundle_layout.png|}}+{{bundle_layout.png?nolink|Bundle Layout Image}}
  
 The first thing you’ll notice is that this is actually a real Finder bundle - so it appears to be a single file called //''Example.indigoPlugin''//. It’s moved around and treated as a single file, and all the user has to do to install your solution is to double-click it in the Finder and Indigo will install and enable it for you. The first thing you’ll notice is that this is actually a real Finder bundle - so it appears to be a single file called //''Example.indigoPlugin''//. It’s moved around and treated as a single file, and all the user has to do to install your solution is to double-click it in the Finder and Indigo will install and enable it for you.
Line 79: Line 79:
   * //''CFBundleIdentifier''// (Bundle identifier) - this is another standard macOS key, and it represents a unique string that represents your plugin. This is used for namespacing where necessary in the code, so it is critical that it is unique. The standard reverse DNS naming scheme is what should be used, although if you aren’t a company you’ll need to figure something out (maybe your blog, etc.). You should limit your bundle id to standard alphanumerics as special/extended characters may cause problems. You should **not** use the ''//com.yourorgidentifier.*//'' namespace. This key is required.   * //''CFBundleIdentifier''// (Bundle identifier) - this is another standard macOS key, and it represents a unique string that represents your plugin. This is used for namespacing where necessary in the code, so it is critical that it is unique. The standard reverse DNS naming scheme is what should be used, although if you aren’t a company you’ll need to figure something out (maybe your blog, etc.). You should limit your bundle id to standard alphanumerics as special/extended characters may cause problems. You should **not** use the ''//com.yourorgidentifier.*//'' namespace. This key is required.
   * //''CFBundleVersion''// (Bundle version) - another standard macOS key, and it represents the layout of the bundle. This is controlled by us. This key is required.   * //''CFBundleVersion''// (Bundle version) - another standard macOS key, and it represents the layout of the bundle. This is controlled by us. This key is required.
-  * //''CFBundleURLTypes''// (URL types) - you must specify one URL that represents a web page where your user can get support. Your plugin will have a menu item called "About [PLUGIN NAME]" - when the user selects this menu item, the default browser will open to this URL. Note - this can be a link to your plugin'Github repo wiki if there is one, or could be a forum topic in the “User Contributions” section of our user forums if you don’t have any other place to host the support page. This key is required.+  * //''CFBundleURLTypes''// (URL types) - you must specify one URL that represents a web page where your user can get support. Your plugin will have a menu item called "About [PLUGIN NAME]" - when the user selects this menu item, the default browser will open to this URL. Note - this can be a link to your plugin'GitHub repo wiki if there is one, or could be a forum topic in the “User Contributions” section of our user forums if you don’t have any other place to host the support page. This key is required.
  
 We want the user experience to be very similar for plugins, at least until it comes to configuration and use of the plugin, so you should be careful to get the Info.plist correct. We want the user experience to be very similar for plugins, at least until it comes to configuration and use of the plugin, so you should be careful to get the Info.plist correct.
Line 160: Line 160:
 The structure of the //''Server Plugin''// directory in the plugin bundle is something like this: The structure of the //''Server Plugin''// directory in the plugin bundle is something like this:
  
-{{ServerPluginFolder.png}}+{{ServerPluginFolder.png?nolink|Server Plugin Folder Image}}
  
 Each of the XML files describes the components that your plugin provides. You must also have at least the //''plugin.py''// file which is the entry point into the Python code that executes your plugin. Beyond that, you may create any other structure you like inside the folder. We’ll go over each file in detail, but first we should discuss the general characteristics of the XML files. Each of the XML files describes the components that your plugin provides. You must also have at least the //''plugin.py''// file which is the entry point into the Python code that executes your plugin. Beyond that, you may create any other structure you like inside the folder. We’ll go over each file in detail, but first we should discuss the general characteristics of the XML files.
Line 221: Line 221:
 The ConfigUI definition will result in the following dialog being presented to the user: The ConfigUI definition will result in the following dialog being presented to the user:
  
-{{ConfigUIRendering.png}}+{{ConfigUIRendering.png?nolink|Configuration UI Rendering Image}}
  
 When each component type (device, event, action) needs a configuration user interface, and most will need some kind of configuration, it will have a ConfigUI element that describes the UI field elements along with a URL. When the user clicks on help button in the lower left corner (as shown above) their browser will be opened to the URL provided. If no URL is specified then the user will be directed to the main URL specified in the //''Info.plist''// file. When each component type (device, event, action) needs a configuration user interface, and most will need some kind of configuration, it will have a ConfigUI element that describes the UI field elements along with a URL. When the user clicks on help button in the lower left corner (as shown above) their browser will be opened to the URL provided. If no URL is specified then the user will be directed to the main URL specified in the //''Info.plist''// file.
Line 232: Line 232:
 === Text Field === === Text Field ===
  
-{{ConfigUI_textfield.png}}+{{ConfigUI_textfield.png?nolink|Configuration UI Textfield Image}}
  
 <code><Field id="simpleTextField" type="textfield" enabledBindingId="checkboxSample" defaultValue="Default Value"> <code><Field id="simpleTextField" type="textfield" enabledBindingId="checkboxSample" defaultValue="Default Value">
Line 259: Line 259:
 === Popup Menu === === Popup Menu ===
  
-{{ConfigUI_menu.png}}+{{ConfigUI_menu.png?nolink|Configuration UI Menu Image}}
  
 <code><Field type="menu" id="simplePopUpButton" defaultValue="item2"> <code><Field type="menu" id="simplePopUpButton" defaultValue="item2">
Line 314: Line 314:
 === List === === List ===
  
-{{ConfigUI_list.png}}+{{ConfigUI_list.png?nolink|COnfiguration UI List Image}}
  
 <code><Field type="list" id="listSample" defaultValue="item1,item3"> <code><Field type="list" id="listSample" defaultValue="item1,item3">
Line 453: Line 453:
 === Serial Port === === Serial Port ===
  
-{{:indigo_2024.1_documentation:configui_serialport_local.png}}+{{:indigo_2024.1_documentation:configui_serialport_local.png?nolink|Configuration UI Serial Port Local Image}}
  
-{{:indigo_2024.1_documentation:configui_serialport_socket.png}}+{{:indigo_2024.1_documentation:configui_serialport_socket.png?nolink|Configuration Serial Port Socket Image}}
  
-{{:indigo_2024.1_documentation:configui_serialport_rfc2217.png}}+{{:indigo_2024.1_documentation:configui_serialport_rfc2217.png?nolink|Configuration Serial Port RFC2217 Image}}
  
 <code><Field type="serialport" id="devicePortFieldId" /></code> <code><Field type="serialport" id="devicePortFieldId" /></code>
Line 468: Line 468:
 === Checkbox === === Checkbox ===
  
-{{ConfigUI_checkbox.png}}+{{ConfigUI_checkbox.png?nolink|Configuration UI Checkbox Image}}
  
 <code><Field type="checkbox" id="checkboxSample" defaultValue="true"> <code><Field type="checkbox" id="checkboxSample" defaultValue="true">
Line 510: Line 510:
 === Label === === Label ===
  
-{{ConfigUI_label.png}}+{{ConfigUI_label.png?nolink|Configuration UI Label Image}}
  
 <code><Field id="exampleLabel" type="label"> <code><Field id="exampleLabel" type="label">
Line 537: Line 537:
 === Separator === === Separator ===
  
-{{ConfigUI_separator.png}}+{{ConfigUI_separator.png?nolink|Configuration UI Separator Image}}
  
 <code><Field id="simpleSeparator1" <code><Field id="simpleSeparator1"
Line 560: Line 560:
 === Button === === Button ===
  
-{{ConfigUI_button.png}}+{{ConfigUI_button.png?nolink|Configuration UI Button Image}}
  
 <code><Field id="exampleButton" <code><Field id="exampleButton"
Line 621: Line 621:
 === Color Picker === === Color Picker ===
  
-{{color_picker_control.png}}+{{color_picker_control.png?nolink|Color Picker Control Image}}
  
 <code><Field id="exampleColorPicker" <code><Field id="exampleColorPicker"
Line 630: Line 630:
 </Field></code> </Field></code>
  
-Introduced with Indigo 7 and API version 2.0, Color Picker fields allow your user to choose a color value. For example, you might provide a method for a user to select a color for an RGB LED device. When the user clicks the color button, Indigo will open the standard MacOS color selector dialog. +Introduced with Indigo 7 and API version 2.0, Color Picker fields allow your user to choose a color value. For example, you might provide a method for a user to select a color for an RGB LED device. When the user clicks the color button, Indigo will open the standard macOS color selector dialog.
  
-{{color_selector_dialog.png}}+{{color_selector_dialog.png?nolink|Color Selector Dialog Image}}
  
 After the user selects a color, the color's value will be passed back to your plugin as a ''//values_dict//'' value when the user executes/closes the configuration dialog. Note that the value provided is a standard RGB value as a space-delimited string. You are responsible for converting that value into the format you need like ''//#8000FF//''. After the user selects a color, the color's value will be passed back to your plugin as a ''//values_dict//'' value when the user executes/closes the configuration dialog. Note that the value provided is a standard RGB value as a space-delimited string. You are responsible for converting that value into the format you need like ''//#8000FF//''.
Line 810: Line 810:
 </code> </code>
  
-{{ :indigo_2024.1_documentation:device_sub_type_example.png?nolink&600 |}}+{{ :indigo_2024.1_documentation:device_sub_type_example.png?nolink&600 |Device Subtype Example Image}}
  
 In Python: In Python:
Line 888: Line 888:
 === Device Factory === === Device Factory ===
  
-{{:indigo_2024.1_documentation:screenshot_2023-02-22_at_1.07.12_pm.png?600|}}+{{:indigo_2024.1_documentation:screenshot_2023-02-22_at_1.07.12_pm.png?nolink&600|Device Factory Edit Device Group Image}}
  
 One way to create "multifunction" devices is to use Indigo's Device Factory method. Device Factory devices are defined by using a special ''%%<DeviceFactory>%%'' node in ''Devices.xml''. A basic Device Factory device definition would look something like this: One way to create "multifunction" devices is to use Indigo's Device Factory method. Device Factory devices are defined by using a special ''%%<DeviceFactory>%%'' node in ''Devices.xml''. A basic Device Factory device definition would look something like this:
Line 1278: Line 1278:
     indigo.PluginBase.__del__(self)</code>|     indigo.PluginBase.__del__(self)</code>|
 |<code>startup(self)</code> No  |This method will get called after your plugin has been initialized. This is really the place where you want to make sure that everything your plugin needs to do gets set up correctly. It’s passed no parameters. If you’re storing a config parameter that’s not editable by the user, this is a good place to make sure it’s there and set to the right value. This is not, however, where you want to initialize devices and triggers that your plugin may provide - those are handled after this method completes (see the methods below). \\ \\ <code>def startup(self): |<code>startup(self)</code> No  |This method will get called after your plugin has been initialized. This is really the place where you want to make sure that everything your plugin needs to do gets set up correctly. It’s passed no parameters. If you’re storing a config parameter that’s not editable by the user, this is a good place to make sure it’s there and set to the right value. This is not, however, where you want to initialize devices and triggers that your plugin may provide - those are handled after this method completes (see the methods below). \\ \\ <code>def startup(self):
-    indigo.server.log(u"Startup called")</code>  The //''startup''// method supports a return value:<html><br></html>don't return anything (or return None explicitly) - your plugin will start up<html><br></html>- return True - your plugin will start up<html><br></html>- return False - your plugin stops with a default message <html><br></html>- return a string - your plugin stops with that string as the message<html><br></html>- return anything else - plugin stops with a default message|+    indigo.server.log(u"Startup called")</code>  The //''startup''// method supports a return value: don't return anything (or return None explicitly) - your plugin will start up<html><br></html>- return True - your plugin will start up<html><br></html>- return False - your plugin stops with a default message <html><br></html>- return a string - your plugin stops with that string as the message<html><br></html>- return anything else - plugin stops with a default message|
 |<code>shutdown(self)</code> No  |This method will get called when the IndigoServer wants your plugin to exit. If you define a global shutdown variable, this is the place to set it. Other things you might do in this method: if your plugin uses a single interface to talk to multiple devices, this is the place where you would want to shut down that interface (close the serial port or network connection, etc). Each device and trigger will already have had a chance to shutdown by the time this method is called (see the methods below). \\ \\ <code>def shutdown(self): |<code>shutdown(self)</code> No  |This method will get called when the IndigoServer wants your plugin to exit. If you define a global shutdown variable, this is the place to set it. Other things you might do in this method: if your plugin uses a single interface to talk to multiple devices, this is the place where you would want to shut down that interface (close the serial port or network connection, etc). Each device and trigger will already have had a chance to shutdown by the time this method is called (see the methods below). \\ \\ <code>def shutdown(self):
      # do any cleanup necessary before exiting</code> Note: //''shutdown''// will be called after //''runConcurrentThread''// (discussed next) so cleanup here will be after any changes that might result from a loop in //''runConcurrentThread''//.|      # do any cleanup necessary before exiting</code> Note: //''shutdown''// will be called after //''runConcurrentThread''// (discussed next) so cleanup here will be after any changes that might result from a loop in //''runConcurrentThread''//.|
Line 1301: Line 1301:
                         dev)</code> No  |If your plugin defines custom devices, this method will be called by the server to determine which device state ID to display in the device list UI state column. The default implementation just returns the <UiDisplayStateId> element in your Devices.xml file. You can, however, implement the method the plugin needs to dynamically determine the which state ID to display.|                         dev)</code> No  |If your plugin defines custom devices, this method will be called by the server to determine which device state ID to display in the device list UI state column. The default implementation just returns the <UiDisplayStateId> element in your Devices.xml file. You can, however, implement the method the plugin needs to dynamically determine the which state ID to display.|
 |<code>deviceStartComm(self, |<code>deviceStartComm(self,
-                dev)</code> No  |If your plugin defines devices, this is likely the place where you'll want to do the work of starting your device up. For instance, let's say that you have a device somewhere out on the network - the easiest way to "start" your device is to implement this method. You would open the network addrss:port (that's defined in ''//dev.pluginProps//''), get it's current state(s) and tell the IndigoServer to set those states (using the ''//dev.updateStateOnServer()//'' method).|+                dev)</code> No  |If your plugin defines devices, this is likely the place where you'll want to do the work of starting your device up. For instance, let's say that you have a device somewhere out on the network - the easiest way to "start" your device is to implement this method. You would open the network address:port (that's defined in ''//dev.pluginProps//''), get it's current state(s) and tell the IndigoServer to set those states (using the ''//dev.updateStateOnServer()//'' method).|
 |<code>deviceStopComm(self, |<code>deviceStopComm(self,
                dev)</code> No  |This is the complementary method to ''//deviceStartComm()//'' - it gets called when the device should no longer be active/enabled. For instance, when the user disables or deletes a device, this method gets called.|                dev)</code> No  |This is the complementary method to ''//deviceStartComm()//'' - it gets called when the device should no longer be active/enabled. For instance, when the user disables or deletes a device, this method gets called.|
Line 1364: Line 1364:
          parity, stopbits, timeout,          parity, stopbits, timeout,
          xonxoff, rtscts, writeTimeout,          xonxoff, rtscts, writeTimeout,
-         dsrdtr, interCharTimeout)</code> | ''//ownerName//'' - the name of the device or plugin that owns this serial port (used for error logging) - make sure that it's ASCII text with no unicode characters \\ ''//other args//'' - all other arguments are passed directly to the [[http://pyserial.sourceforge.net/pyserial_api.html#classes | pySerial's Serial contructor ]] | serial.Serial instance | This method is identical to creating a new [[http://pyserial.sourceforge.net/pyserial_api.html#classes | pySerial Serial object]] except that it never throws an exception. If the serial connection cannot be openned then None is returned and an error will be automatically logged to the Indigo Server event log. |+         dsrdtr, interCharTimeout)</code> | ''//ownerName//'' - the name of the device or plugin that owns this serial port (used for error logging) - make sure that it's ASCII text with no unicode characters \\ ''//other args//'' - all other arguments are passed directly to the [[http://pyserial.sourceforge.net/pyserial_api.html#classes | pySerial's Serial contructor ]] | serial.Serial instance | This method is identical to creating a new [[http://pyserial.sourceforge.net/pyserial_api.html#classes | pySerial Serial object]] except that it never throws an exception. If the serial connection cannot be opened then None is returned and an error will be automatically logged to the Indigo Server event log. |
 | <code>sleep(self, | <code>sleep(self,
          seconds)</code> | ''//seconds//'' - the sleep duration as a real number | None | This method should be called from within your plugin's //''runConcurrentThread()''// defined method, if it is defined. It will automatically raise the //''StopThread''// exception when the Indigo Server is trying to shutdown or restart the plugin. See //''runConcurrentThread''// documentation above for more details. |          seconds)</code> | ''//seconds//'' - the sleep duration as a real number | None | This method should be called from within your plugin's //''runConcurrentThread()''// defined method, if it is defined. It will automatically raise the //''StopThread''// exception when the Indigo Server is trying to shutdown or restart the plugin. See //''runConcurrentThread''// documentation above for more details. |
Line 1417: Line 1417:
 The ''self.indigo_log_handler'' is an instance of a custom [[https://docs.python.org/3.10/library/logging.html#handler-objects|Handler]] object that will write (or emit in Python Handler speak) your log messages into the Indigo Event Log. The message type (the left part in the Event Log) is modified so that it reflects the level of the log message (with the exception of the `info` level). For convenience, the various log levels are also represented in color. Here's an example of each level: The ''self.indigo_log_handler'' is an instance of a custom [[https://docs.python.org/3.10/library/logging.html#handler-objects|Handler]] object that will write (or emit in Python Handler speak) your log messages into the Indigo Event Log. The message type (the left part in the Event Log) is modified so that it reflects the level of the log message (with the exception of the `info` level). For convenience, the various log levels are also represented in color. Here's an example of each level:
  
-{{indigo_2024.1_documentation:embedded_script_logging.png?nolink&600}}+{{indigo_2024.1_documentation:embedded_script_logging.png?nolink&600|Embedded Script Logging Image}}
  
  
Line 1546: Line 1546:
 and then logging messages will appear with the colors above. Result: and then logging messages will appear with the colors above. Result:
  
-{{indigo_2024.1_documentation:embedded_script_logging.png?nolink&600}}+{{indigo_2024.1_documentation:embedded_script_logging.png?nolink&600|Embedded Script Logging Image}}
  
  
  • /www/perceptive/wiki/data/attic/indigo_2024.1_documentation/plugin_guide.1736184855.txt.gz
  • Last modified: 2025/01/06 17:34
  • by davel17