| Both sides previous revision Previous revision Next revision | Previous revision |
| indigo_2024.1_documentation:plugin_guide [2024/10/05 13:21] – [Event and Message Flow] davel17 | indigo_2024.1_documentation:plugin_guide [2025/04/14 20:10] (current) – external edit 127.0.0.1 |
|---|
| 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: |
| 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. |
| * //''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's 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's 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. |
| 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. |
| 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. |
| === 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"> |
| === 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"> |
| === 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"> |
| === 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> |
| === 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"> |
| === 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"> |
| === Separator === | === Separator === |
| |
| {{ConfigUI_separator.png}} | {{ConfigUI_separator.png?nolink|Configuration UI Separator Image}} |
| |
| <code><Field id="simpleSeparator1" | <code><Field id="simpleSeparator1" |
| === Button === | === Button === |
| |
| {{ConfigUI_button.png}} | {{ConfigUI_button.png?nolink|Configuration UI Button Image}} |
| |
| <code><Field id="exampleButton" | <code><Field id="exampleButton" |
| === 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" |
| </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//''. |
| === Custom HTML Config Dialogs === | === Custom HTML Config Dialogs === |
| |
| You can also implement your own custom configuration in HTML if you prefer. Rather than adding lots of //''<Field>''// definitions, you simply specify a //''<URL>''// element. The URL specified can either be a fully specified URL (%%protocol://host/path%%) or it may be a relative URL (/some/relative/path). If it's the later then Indigo will attempt to guess the [[indigo_2022.2_documentation:server_commands&#get_web_server_url|best base URL]]. You would then handle those form requests using [[#processing_http_requests_in_your_plugin|the built-in request handling mechanism discussed below]]. See the **Example HTTP Responder** plugin in the [[https://github.com/IndigoDomotics/IndigoSDK/releases/tag/v2024.1|SDK]] for an example. | You can also implement your own custom configuration in HTML if you prefer. Rather than adding lots of //''<Field>''// definitions, you simply specify a //''<URL>''// element. The URL specified can either be a fully specified URL <nowiki>(%%protocol://host/path%%)</nowiki> or it may be a relative URL (/some/relative/path). If it's the later then Indigo will attempt to guess the [[indigo_2022.2_documentation:server_commands&#get_web_server_url|best base URL]]. You would then handle those form requests using [[#processing_http_requests_in_your_plugin|the built-in request handling mechanism discussed below]]. See the **Example HTTP Responder** plugin in the [[https://github.com/IndigoDomotics/IndigoSDK/releases/tag/v2024.1|SDK]] for an example. |
| ==== Devices.xml ==== | ==== Devices.xml ==== |
| |
| </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: |
| === 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: |
| === Custom HTML Menu Item Dialogs === | === Custom HTML Menu Item Dialogs === |
| |
| You can also implement your own custom menu item form in HTML if you prefer. Rather than adding //''<CallbackMethod>''// and //''<ConfigUI>''// definitions, you simply specify a //''<URL>''// element. The URL specified can either be a fully specified URL (%%protocol://host/path%%) or it may be a relative URL (/some/relative/path). If it's the latter, then Indigo will attempt to guess the [[indigo_2024.1_documentation:server_commands&#get_web_server_url|best base URL]]. You would then handle those form requests using [[#processing_http_requests_in_your_plugin|the built-in request handling mechanism discussed below]]. See the **Example HTTP Responder** plugin in the [[https://github.com/IndigoDomotics/IndigoSDK/releases/tag/v2024.1|SDK]] for an example. | You can also implement your own custom menu item form in HTML if you prefer. Rather than adding //''<CallbackMethod>''// and //''<ConfigUI>''// definitions, you simply specify a //''<URL>''// element. The URL specified can either be a fully specified URL <nowiki>(%%protocol://host/path%%)</nowiki> or it may be a relative URL (/some/relative/path). If it's the latter, then Indigo will attempt to guess the [[indigo_2024.1_documentation:server_commands&#get_web_server_url|best base URL]]. You would then handle those form requests using [[#processing_http_requests_in_your_plugin|the built-in request handling mechanism discussed below]]. See the **Example HTTP Responder** plugin in the [[https://github.com/IndigoDomotics/IndigoSDK/releases/tag/v2024.1|SDK]] for an example. |
| |
| ==== SupportURL Elements ==== | ==== SupportURL Elements ==== |
| 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''//.| |
| |<code>prepareToSleep(self)</code>| No |The default implementation of this method will call //''deviceStopComm()''// for each device instance and //''triggerStopProcessing()''// for each trigger instance provided by your plugin. You can of course override them to do anything you like. | | |<code>prepareToSleep(self)</code>| No |The default implementation of this method will call //''deviceStopComm()''// for each device instance and //''triggerStopProcessing()''// for each trigger instance provided by your plugin. You can of course override them to do anything you like. | |
| |<code>wakeUp(self)</code>| No |The default implementation of this method will call //''deviceStartComm()''// for each device instance and //''triggerStartProcessing()''// for each trigger instance provided by your plugin. You can of course override them to do anything you like. | | |<code>wakeUp(self)</code>| No |The default implementation of this method will call //''deviceStartComm()''// for each device instance and //''triggerStartProcessing()''// for each trigger instance provided by your plugin. You can of course override them to do anything you like. | |
| ^ Device Specific Methods ^^^ | |
| | === Device Specific Methods === |
| ^ Method definition ^ Required ^ Notes ^ | ^ Method definition ^ Required ^ Notes ^ |
| |<code>getDeviceStateList(self, | |<code>getDeviceStateList(self, |
| 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.| |
| |<code>deviceDeleted(self, | |<code>deviceDeleted(self, |
| dev)</code>| No |Complementary to the ''//deviceCreated() method//'' described above, but signals device deletes. The default implementation just checks to see if the device belongs to your plugin and if so calls the ''//deviceStopComm()//'' method. If you implement this method you'll need to call ''//deviceStopComm()//'' yourself or duplicate the functionality here.| | dev)</code>| No |Complementary to the ''//deviceCreated() method//'' described above, but signals device deletes. The default implementation just checks to see if the device belongs to your plugin and if so calls the ''//deviceStopComm()//'' method. If you implement this method you'll need to call ''//deviceStopComm()//'' yourself or duplicate the functionality here.| |
| ^ Trigger Specific Methods ^^^ | |
| | === Trigger Specific Methods === |
| ^ Method definition ^ Required ^ Notes ^ | ^ Method definition ^ Required ^ Notes ^ |
| |<code>triggerStartProcessing(self, | |<code>triggerStartProcessing(self, |
| |<code>triggerDeleted(self, | |<code>triggerDeleted(self, |
| trigger)</code>| No |Complementary to the ''//triggerCreated() method//'' described above, but signals trigger deletes. The default implementation just checks to see if the trigger belongs to your plugin and if so calls the ''//triggerStopProcessing()//'' method. If you implement this method you'll need to call ''//triggerStopProcessing()//'' yourself or duplicate the functionality here.| | trigger)</code>| No |Complementary to the ''//triggerCreated() method//'' described above, but signals trigger deletes. The default implementation just checks to see if the trigger belongs to your plugin and if so calls the ''//triggerStopProcessing()//'' method. If you implement this method you'll need to call ''//triggerStopProcessing()//'' yourself or duplicate the functionality here.| |
| ^ Schedule Specific Methods ^^^ | |
| | === Schedule Specific Methods === |
| ^ Method definition ^ Required ^ Notes ^ | ^ Method definition ^ Required ^ Notes ^ |
| |FIXME add schedule documentation when it is complete (not currently available)||| | |FIXME add schedule documentation when it is complete (not currently available)||| |
| ^ Action Group Specific Methods ^^^ | |
| | === Action Group Specific Methods === |
| ^ Name ^ Required ^ Notes ^ | ^ Name ^ Required ^ Notes ^ |
| |FIXME add action group documentation when it is complete (not currently available)||| | |FIXME add action group documentation when it is complete (not currently available)||| |
| ^ Variable Specific Methods ^^^ | |
| | === Variable Specific Methods === |
| ^ Name ^ Required ^ Notes ^ | ^ Name ^ Required ^ Notes ^ |
| |<code>variableCreated(self, var)</code>| No |This method will get called whenever a new variable is created. You can call the ''//indigo.variables.subscribeToChanges()//'' method to have the IndigoServer send all variable creation/update/deletion notifications. As with other change subscriptions, this should be used very sparingly since it's a lot of overhead both for your plugin and, more importantly, for the IndigoServer.| | |<code>variableCreated(self, var)</code>| No |This method will get called whenever a new variable is created. You can call the ''//indigo.variables.subscribeToChanges()//'' method to have the IndigoServer send all variable creation/update/deletion notifications. As with other change subscriptions, this should be used very sparingly since it's a lot of overhead both for your plugin and, more importantly, for the IndigoServer.| |
| 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. | |
| 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}} |
| |
| |
| 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}} |
| |
| |