--- Integration APIs (https://docs.indigodomo.com/2025.2/api/) --- # Integration APIs This article is about the two APIs in IWS that developers can use to integrate Indigo with external services: - [WebSocket API](websocket.md) – (which the new Indigo Touch Web UI uses), and - [HTTP API](http.md) – shares as much of the messaging construction with the WebSocket interface as is practical. Both of these APIs are authenticated with HTTP Digest, API Keys, and [local secrets](../user/remote-access/web-server.md#authentication) (either as a query string or preferably an Authorization header) depending on how the user configures it in the Start Local Server dialog. !!! warning "Warning" - **HTTP Basic authentication** has been deprecated due to its insecure nature. - The old REST API has been deprecated in favor of the [HTTP API](http.md). ## Versioning A quick note on versioning: all APIs will be versioned under the following scheme: - `/v2/` - this is the top level version number and will change as necessary ### Python vs JavaScript In these APIs, we’re using [JSON](https://www.json.org/) (JavaScript Object Notation) as the message format for communicating between the WebSocket and HTTP APIs and IWS. In JavaScript, an “object” definition looks (almost) exactly like a Python dictionary (and vice versa). So we may refer to an object or dictionary (dict): for the purposes of this document, they refer to the same JSON construct. For example, we may call this an object or a dict: ```json { "key1": "value 1", "key2": 2 } ``` We expect there will be both Python and JavaScript users integrating our APIs, so we wanted to explicitly call this out. As a primarily Python organization, you may notice a bias towards “dict”. Python developers will notice the use of `null` in the message descriptions. This corresponds to the Python `None` object. Also of note are the booleans `true` and `false`, which are capitalized in Python but not in JSON. Here’s a handy cheat sheet: | Python | JSON Equivalent | |--------|-----------------| | True | true | | False | false | | float | Number | | int | Number | | None | null | | dict | Object | | list | Array | | tuple | Array | | str | String | If you are new to JSON, you may want to use the [JSON Validator website](https://jsonlint.com) to validate that the JSON message you are sending is valid JSON. --- HTTP API (https://docs.indigodomo.com/2025.2/api/http/) --- # HTTP API !!! abstract "In this guide" This API is meant for use with standard HTTP as the communication mechanism. HTTP **GET** requests to get Indigo object instances in JSON format, and **POST** requests to send commands to the Indigo Server. ## HTTP API Endpoints The following is a summary of endpoints (URLs that you will need to use the API) that are available (detail on each is further down): - `/v2/api/indigo.devices` - endpoint to get a list of devices - `/v2/api/indigo.devices/123456789` - endpoint to get a specific device instance (See [Device Objects](messages.md#device-objects) below) - `/v2/api/indigo.variables` - endpoint to get a list of variables - `/v2/api/indigo.variables/123456789` - endpoint to specific variable object (See [Variable Objects](messages.md#variable-objects) below) - `/v2/api/indigo.actionGroups` - endpoint to get a list of action groups - `/v2/api/indigo.actionGroups/123456789` - endpoint to get a specific action group (See [Action Group Objects](messages.md#action-group-objects) below) - `/v2/api/command` - endpoint to send Indigo a command (device control, variable update, action execution). You’ll receive full JSON objects which represent either a list of all instances of the object type requested (for example, a list of all action groups) or an individual Indigo object instance (a single device, variable, etc.) Each individual object will be different based on the object type, class and its definition (a custom device, for example). See the [Indigo Object Model](../scripting/iom-concepts.md) docs for details about each object type. ## Authentication HTTP API requests must be authenticated using an **API Key**. You can manage API Keys in the [Authorizations section of your Indigo Account](https://www.indigodomo.com/account/authorizations). Using keys instead of your Indigo Server username/password has several advantages: you can, at any time, revoke an API key, and it will immediately cause anything using it to fail. This will not affect anything else using your username/password or another API key, so your server protections against intrusions are much more granular. Also, if someone does manage to get your API Key, they can control devices, but they cannot modify your database (add/delete devices, etc.) - that is reserved for Indigo clients using the username/password. The best way to use an API Key is to include it in an **Authorization** header on your HTTP request. All the examples below show this approach. If you are using a system which does not allow you to set headers for your HTTP request, you can include the API Key as a query argument with the URL: `https://YOUR-REFLECTOR-NAME.indigodomo.net/v2/api/indigo.devices/123456789?api-key=YOUR-API-KEY` When using HTTPS, such as when you are using your Indigo Reflector, then your API Key (in both instances) is protected by the TLS security used by the HTTPS protocol. You may use the API locally (or thorough your own router port forwarding), but those connections will be HTTP and **will not be secure**. It is highly recommended that you always use your Indigo Reflector because it provides a very simple and **secure** solution for accessing your system. !!! warning Don't share your API keys with anyone who is not authorized to use them — especially in posts to the user forums. ## Getting Device Objects The HTTP API includes a couple of methods for getting device instances as JSON objects. ### Getting All Device Objects [Device Objects](messages.md#device-objects) are JSON representations of an Indigo device instance. This JSON object will contain all information about the object, which you can use in your solutions. By using the endpoint without specifying a specific device id, you can get the complete list of all devices in your Indigo database: `/v2/api/indigo.devices` Here are some examples in different languages/technologies that illustrate how to get all devices. #### Pure Python 3 (no additional libraries) - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. ```python # This is a pure python example - no additional libraries needed from urllib.request import Request, urlopen import json REFLECTORNAME = "YOUR-REFLECTOR-NAME" APIKEY = "YOUR-API-KEY" req = Request(f"https://{REFLECTORNAME}.indigodomo.net/v2/api/indigo.devices") req.add_header('Authorization', f"Bearer {APIKEY}") with urlopen(req) as request: device_list = json.load(request) print(device_list) ``` #### JavaScript run from nodejs (no additional libraries) - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. ```javascript // Things that are specific to your environment const REFLECTORNAME = "YOUR-REFLECTOR-NAME" const APIKEY = "YOUR-API-KEY" // Get the http module, and tell it that you're using HTTPS const http = require("https") // These are options that you'll pass to the get call const options = { hostname: `${REFLECTORNAME}.indigodomo.net`, path: `/v2/api/indigo.devices`, headers: { Authorization: `Bearer ${APIKEY}` } } // Get the device JSON from Indigo, parse it into an object, and log it to the console http.get(options, (response) => { let result = "" response.on("data", chunk => { result += chunk; }) response.on("end", () => { const deviceList = JSON.parse(result); console.log(deviceList); }) }) ``` #### Using curl from the command line - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. ```bash curl -H "Authorization: Bearer YOUR-API-KEY" https://YOUR-REFLECTOR-NAME.indigodomo.net/v2/api/indigo.devices ``` ### Getting a Single Device Object Here are a few examples in different languages that illustrate how to get device objects. #### Pure Python 3 (no additional libraries) { #getting-a-single-device-object-pure-python-3-no-additional-libraries } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. - Replace `*123456789*` with a valid Indigo device ID. ```python # This is a pure python example - no additional libraries needed from urllib.request import Request, urlopen import json REFLECTORNAME = "YOUR-REFLECTOR-NAME" APIKEY = "YOUR-API-KEY" DEVICEID = 123456789 req = Request(f"https://{REFLECTORNAME}.indigodomo.net/v2/api/indigo.devices/{DEVICEID}") req.add_header('Authorization', f"Bearer {APIKEY}") with urlopen(req) as request: device_instance = json.load(request) print(device_instance) ``` #### JavaScript run from nodejs (no additional libraries) { #getting-a-single-device-object-javascript-run-from-nodejs-no-additional-libraries } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. - Replace `*123456789*` with a valid Indigo device ID. ```javascript // Things that are specific to your environment const REFLECTORNAME = "YOUR-REFLECTOR-NAME" const APIKEY = "YOUR-API-KEY" const DEVICEID = 123456789 // Get the http module, and tell it that you're using HTTPS const http = require("https") // These are options that you'll pass to the get call const options = { hostname: `${REFLECTORNAME}.indigodomo.net`, path: `/v2/api/indigo.devices/${DEVICEID}`, headers: { Authorization: `Bearer ${APIKEY}` } } // Get the device JSON from Indigo, parse it into an object, and log it to the console http.get(options, (response) => { let result = "" response.on("data", chunk => { result += chunk; }) response.on("end", () => { const deviceInstance = JSON.parse(result); console.log(deviceInstance); }) }) ``` #### Using curl from the command line { #getting-a-single-device-object-using-curl-from-the-command-line } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. ```bash curl -H "Authorization: Bearer YOUR-API-KEY" https://YOUR-REFLECTOR-NAME.indigodomo.net/v2/api/indigo.devices/123456789 ``` ### Controlling an Indigo Device You can control Indigo devices by sending commands through the API that instruct Indigo how to control the device. Indigo devices come in a variety of types, and each type has its own command set. See the [Device Command Messages](messages.md#device-command-messages) below for a description of all messages. For now, here are some simple examples to toggle devices: #### Pure Python 3 (no additional libraries) { #controlling-an-indigo-device-pure-python-3-no-additional-libraries } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. - Replace `*123456789*` with a valid Indigo device ID. ```python # This is a pure python example - no additional libraries needed from urllib.request import Request, urlopen import json REFLECTORNAME = "YOUR-REFLECTOR-NAME" APIKEY = "YOUR-API-KEY" DEVICEID = 123456789 # The message to send to the Indigo Server message = json.dumps({ "id": "optional-custom-user-message", "message": "indigo.device.toggle", "objectId": DEVICEID }).encode("utf8") req = Request(f"https://{REFLECTORNAME}.indigodomo.net/v2/api/command", data=message) req.add_header('Authorization', f"Bearer {APIKEY}") with urlopen(req) as request: reply = json.load(request) print(reply) ``` #### JavaScript toggle from nodejs (no additional libraries) - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. - Replace `*123456789*` with a valid Indigo device ID. ```javascript // Things that are specific to your environment const REFLECTORNAME = "YOUR-REFLECTOR-NAME" const APIKEY = "YOUR-API-KEY" const DEVICEID = 123456789 // Get the http module, and tell it that you're using HTTPS const http = require("https") // The message to send to the Indigo Server const message = JSON.stringify({ "id": "optional-custom-user-message", "message": "indigo.device.toggle", "objectId": DEVICEID }) // These are options that you'll pass to the get call const options = { hostname: `${REFLECTORNAME}.indigodomo.net`, path: "/v2/api/command", method: "POST", headers: { Authorization: `Bearer ${APIKEY}`, "Content-Length": message.length } } // Get the device JSON from Indigo, parse it into an object, and log it to the console const req = http.request(options, (response) => { let result = "" response.on("data", chunk => { result += chunk; }) response.on("end", () => { const deviceInstance = JSON.parse(result); console.log(deviceInstance); }) }) req.write(message) req.end() ``` #### Using curl from the command line { #controlling-an-indigo-device-using-curl-from-the-command-line } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. - Replace `*123456789*` with a valid Indigo device ID. ```bash curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message": "indigo.device.toggle", "objectId": 123456789}' https://YOUR-REFLECTOR-NAME.indigodomo.net/v2/api/command ``` Using these examples, you can now construct any command listed below for any device type. ### Controlling an Indigo Device with Parameters As noted above, Indigo devices come in a variety of types, and each type has its own command set. As a part of this command set, some devices require specific parameters in order for Indigo to be able to execute them (some devices accept optional parameters, and others do not require any parameters at all). See the [Device Command Messages](messages.md#device-command-messages) below for a description of all messages. The following examples use the `indigo.dimmer.setBrightness` command and demonstrate how to include parameters. #### Pure Python 3 (no additional libraries) { #controlling-an-indigo-device-with-parameters-pure-python-3-no-additional-libraries } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. - Replace `*123456789*` with a valid Indigo device ID. ```python # This is a pure python example - no additional libraries needed from urllib.request import Request, urlopen import json REFLECTORNAME = "YOUR-REFLECTOR-NAME" APIKEY = "YOUR-API-KEY" DEVICEID = 123456789 # The message to send to the Indigo Server message = json.dumps({ "id": "optional-custom-user-message", "message": "indigo.dimmer.setBrightness", "objectId": DEVICEID, "parameters": { "value": 50, "delay": 10 } }).encode("utf8") req = Request(f"https://{REFLECTORNAME}.indigodomo.net/v2/api/command", data=message) req.add_header('Authorization', f"Bearer {APIKEY}") with urlopen(req) as request: reply = json.load(request) print(reply) ``` #### JavaScript run from nodejs (no additional libraries) { #controlling-an-indigo-device-with-parameters-javascript-run-from-nodejs-no-additional-libraries } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. - Replace `*123456789*` with a valid Indigo device ID. ```javascript // Things that are specific to your environment const REFLECTORNAME = "YOUR-REFLECTOR-NAME" const APIKEY = "YOUR-API-KEY" const DEVICEID = 123456789 // Get the http module, and tell it that you're using HTTPS const http = require("https") // The message to send to the Indigo Server const message = JSON.stringify({ "id": "optional-custom-user-message", "message": "indigo.dimmer.setBrightness", "objectId": DEVICEID, "parameters": { "value": 50, "delay": 10 } }) // These are options that you'll pass to the get call const options = { hostname: `${REFLECTORNAME}.indigodomo.net`, path: "/v2/api/command", method: "POST", headers: { Authorization: `Bearer ${APIKEY}`, "Content-Length": message.length } } // Get the device JSON from Indigo, parse it into an object, and log it to the console const req = http.request(options, (response) => { let result = "" response.on("data", chunk => { result += chunk; }) response.on("end", () => { const deviceInstance = JSON.parse(result); console.log(deviceInstance); }) }) req.write(message) req.end() ``` #### Using curl from the command line { #controlling-an-indigo-device-with-parameters-using-curl-from-the-command-line } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. - Replace `*123456789*` with a valid Indigo device ID. ```bash curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message": "indigo.dimmer.setBrightness", "objectId": 123456789, "parameters": {"value": 50, "delay": 10}}' https://YOUR-REFLECTOR-NAME.indigodomo.net/v2/api/command ``` Using these examples, you can now construct any command listed below for any device type. ## Getting Variable Objects ### Getting All Variable Objects [Variable Objects](messages.md#variable-objects) are JSON representations of an Indigo variable instance. This JSON object will contain all information about the object, which you can use in your solutions. By using the endpoint without specifying a specific variable id, you can get the complete list of all variables in your Indigo database: `/v2/api/indigo.variables` Here are some examples in different languages/technologies that illustrate how to get all variables. #### Pure Python 3 (no additional libraries) { #getting-all-variable-objects-pure-python-3-no-additional-libraries } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. ```python # This is a pure python example - no additional libraries needed from urllib.request import Request, urlopen import json REFLECTORNAME = "YOUR-REFLECTOR-NAME" APIKEY = "YOUR-API-KEY" req = Request(f"https://{REFLECTORNAME}.indigodomo.net/v2/api/indigo.variables") req.add_header('Authorization', f"Bearer {APIKEY}") with urlopen(req) as request: variable_list = json.load(request) print(variable_list) ``` #### JavaScript run from nodejs (no additional libraries) { #getting-all-variable-objects-javascript-run-from-nodejs-no-additional-libraries } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. ```javascript // Things that are specific to your environment const REFLECTORNAME = "YOUR-REFLECTOR-NAME" const APIKEY = "YOUR-API-KEY" // Get the http module, and tell it that you're using HTTPS const http = require("https") // These are options that you'll pass to the get call const options = { hostname: `${REFLECTORNAME}.indigodomo.net`, path: `/v2/api/indigo.variables`, headers: { Authorization: `Bearer ${APIKEY}` } } // Get the variable JSON from Indigo, parse it into an object, and log it to the console http.get(options, (response) => { let result = "" response.on("data", chunk => { result += chunk; }) response.on("end", () => { const VariableList = JSON.parse(result); console.log(VariableList); }) }) ``` #### Using curl from the command line { #getting-all-variable-objects-using-curl-from-the-command-line } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. ```bash curl -H "Authorization: Bearer YOUR-API-KEY" https://YOUR-REFLECTOR-NAME.indigodomo.net/v2/api/indigo.variables ``` ### Getting a Single Variable Object Here are a few examples in different languages that illustrate how to get variable objects. #### Pure Python 3 (no additional libraries) { #getting-a-single-variable-object-pure-python-3-no-additional-libraries } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. - Replace `*123456789*` with a valid Indigo variable ID. ```python # This is a pure python example - no additional libraries needed from urllib.request import Request, urlopen import json REFLECTORNAME = "YOUR-REFLECTOR-NAME" APIKEY = "YOUR-API-KEY" VARID = 123456789 # Indigo variable object id req = Request(f"https://{REFLECTORNAME}.indigodomo.net/v2/api/indigo.variables/{VARID}") req.add_header('Authorization', f"Bearer {APIKEY}") with urlopen(req) as request: var_instance = json.load(request) print(var_instance) ``` #### JavaScript run from nodejs (no additional libraries) { #getting-a-single-variable-object-javascript-run-from-nodejs-no-additional-libraries } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. - Replace `*123456789*` with a valid Indigo variable ID. ```javascript // Things that are specific to your environment const REFLECTORNAME = "YOUR-REFLECTOR-NAME" const APIKEY = "YOUR-API-KEY" const VARID = 123456789 // Get the http module, and tell it that you're using HTTPS const http = require("https") // These are options that you'll pass to the get call const options = { hostname: `${REFLECTORNAME}.indigodomo.net`, path: `/v2/api/indigo.variables/${VARID}`, headers: { Authorization: `Bearer ${APIKEY}` } } // Get the variable object JSON from Indigo, parse it into an object, and log it to the console http.get(options, (response) => { let result = "" response.on("data", chunk => { result += chunk; }) response.on("end", () => { const varInstance = JSON.parse(result); console.log(varInstance); }) }) ``` #### Using curl from the command line { #getting-a-single-variable-object-using-curl-from-the-command-line } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. - Replace `*123456789*` with a valid Indigo variable ID. ```bash curl -H "Authorization: Bearer YOUR-API-KEY" https://YOUR-REFLECTOR-NAME.indigodomo.net/v2/api/indigo.variables/123456789 ``` ### Updating a Variable's Value You can interact with Indigo variables by sending commands through the API that instruct Indigo what to do. There is only one type of Indigo variable, and the variable type has its own command set. See the [Variable Command Messages](messages.md#variable-command-messages) below for a description of all messages. For now, here is an example of how to set the value of a variable: #### Pure Python 3 (no additional libraries) { #updating-a-variables-value-pure-python-3-no-additional-libraries } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. - Replace `*123456789*` with a valid Indigo variable ID. ```python # This is a pure python example - no additional libraries needed from urllib.request import Request, urlopen import json REFLECTORNAME = "YOUR-REFLECTOR-NAME" APIKEY = "YOUR-API-KEY" VARIABLEID = 123456789 # The message to send to the Indigo Server message = json.dumps({ "id": "optional-custom-user-message", "message": "indigo.variable.updateValue", "objectId": VARIABLEID, "parameters": { "value": "Some string value" } }).encode("utf8") req = Request(f"https://{REFLECTORNAME}.indigodomo.net/v2/api/command", data=message) req.add_header('Authorization', f"Bearer {APIKEY}") with urlopen(req) as request: reply = json.load(request) print(reply) ``` #### JavaScript run from nodejs (no additional libraries) { #updating-a-variables-value-javascript-run-from-nodejs-no-additional-libraries } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. - Replace `*123456789*` with a valid Indigo variable ID. ```javascript // Things that are specific to your environment const REFLECTORNAME = "YOUR-REFLECTOR-NAME" const APIKEY = "YOUR-API-KEY" const VARIABLEID = 123456789 // Get the http module, and tell it that you're using HTTPS const http = require("https") // The message to send to the Indigo Server const message = JSON.stringify({ "id": "optional-custom-user-message", "message": "indigo.variable.updateValue", "objectId": VARIABLEID, "parameters": { "value": "Some string value" } }) // These are options that you'll pass to the get call const options = { hostname: `${REFLECTORNAME}.indigodomo.net`, path: "/v2/api/command", method: "POST", headers: { Authorization: `Bearer ${APIKEY}`, "Content-Length": message.length } } // Get the variable JSON from Indigo, parse it into an object, and log it to the console const req = http.request(options, (response) => { let result = "" response.on("data", chunk => { result += chunk; }) response.on("end", () => { const VariableInstance = JSON.parse(result); console.log(VariableInstance); }) }) req.write(message) req.end() ``` #### Using curl from the command line { #updating-a-variables-value-using-curl-from-the-command-line } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. - Replace `*123456789*` with a valid Indigo variable ID. ```bash curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message": "indigo.variable.updateValue", "objectId": 123456789, "parameters": {"value": "Some string value"}}' https://YOUR-REFLECTOR-NAME.indigodomo.net/v2/api/command ``` Using these examples, you can now construct any command listed below for any variable type. ## Getting Action Group Objects ### Getting All Action Group Objects [Action Group Objects](messages.md#action-group-objects) are JSON representations of an Indigo action group instance. This JSON object will contain all information about the object, which you can use in your solutions. By using the endpoint without specifying a specific action group id, you can get the complete list of all action groups in your Indigo database: `/v2/api/indigo.actionGroups` Here are some examples in different languages/technologies that illustrate how to get all action groups. #### Pure Python 3 (no additional libraries) { #getting-all-action-group-objects-pure-python-3-no-additional-libraries } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. ```python # This is a pure python example - no additional libraries needed from urllib.request import Request, urlopen import json REFLECTORNAME = "YOUR-REFLECTOR-NAME" APIKEY = "YOUR-API-KEY" req = Request(f"https://{REFLECTORNAME}.indigodomo.net/v2/api/indigo.actionGroups") req.add_header('Authorization', f"Bearer {APIKEY}") with urlopen(req) as request: action_group_list = json.load(request) print(action_group_list) ``` #### JavaScript run from nodejs (no additional libraries) { #getting-all-action-group-objects-javascript-run-from-nodejs-no-additional-libraries } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. ```javascript // Things that are specific to your environment const REFLECTORNAME = "YOUR-REFLECTOR-NAME" const APIKEY = "YOUR-API-KEY" // Get the http module, and tell it that you're using HTTPS const http = require("https") // These are options that you'll pass to the get call const options = { hostname: `${REFLECTORNAME}.indigodomo.net`, path: `/v2/api/indigo.actionGroups`, headers: { Authorization: `Bearer ${APIKEY}` } } // Get the action group JSON from Indigo, parse it into an object, and log it to the console http.get(options, (response) => { let result = "" response.on("data", chunk => { result += chunk; }) response.on("end", () => { const actionGroupList = JSON.parse(result); console.log(actionGroupList); }) }) ``` #### Using curl from the command line { #getting-all-action-group-objects-using-curl-from-the-command-line } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. ```bash curl -H "Authorization: Bearer YOUR-API-KEY" https://YOUR-REFLECTOR-NAME.indigodomo.net/v2/api/indigo.actionGroups ``` ### Executing an Action Group You can control Indigo action groups by sending commands through the API that instruct Indigo how to execute the action group. There is only one type of Indigo action group, and the action group type has its own command set. See the [Action Group Command Messages](messages.md#action-group-command-messages) below for a description of all messages. For now, here is a simple example of how to execute an action group: #### Pure Python 3 (no additional libraries) { #executing-an-action-group-pure-python-3-no-additional-libraries } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. - Replace `*123456789*` with a valid Indigo action group ID. ```python # This is a pure python example - no additional libraries needed from urllib.request import Request, urlopen import json REFLECTORNAME = "YOUR-REFLECTOR-NAME" APIKEY = "YOUR-API-KEY" ACTIONGROUPID = 123456789 # The message to send to the Indigo Server message = json.dumps({ "id": "optional-custom-user-message", "message": "indigo.actionGroup.execute", "objectId": ACTIONGROUPID }).encode("utf8") req = Request(f"https://{REFLECTORNAME}.indigodomo.net/v2/api/command", data=message) req.add_header('Authorization', f"Bearer {APIKEY}") with urlopen(req) as request: reply = json.load(request) print(reply) ``` #### JavaScript run from nodejs (no additional libraries) { #executing-an-action-group-javascript-run-from-nodejs-no-additional-libraries } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. - Replace `*123456789*` with a valid Indigo action group ID. ```javascript // Things that are specific to your environment const REFLECTORNAME = "YOUR-REFLECTOR-NAME" const APIKEY = "YOUR-API-KEY" const ACTIONGROUPID = 123456789 // Get the http module, and tell it that you're using HTTPS const http = require("https") // The message to send to the Indigo Server const message = JSON.stringify({ "id": "optional-custom-user-message", "message": "indigo.actionGroup.execute", "objectId": ACTIONGROUPID }) // These are options that you'll pass to the get call const options = { hostname: `${REFLECTORNAME}.indigodomo.net`, path: "/v2/api/command", method: "POST", headers: { Authorization: `Bearer ${APIKEY}`, "Content-Length": message.length } } // Get the action group JSON from Indigo, parse it into an object, and log it to the console const req = http.request(options, (response) => { let result = "" response.on("data", chunk => { result += chunk; }) response.on("end", () => { const actionGroupInstance = JSON.parse(result); console.log(actionGroupInstance); }) }) req.write(message) req.end() ``` #### Using curl from the command line { #executing-an-action-group-using-curl-from-the-command-line } - Replace `*YOUR-API-KEY*` with a valid key from your Indigo account. - Replace `*YOUR-REFLECTOR-NAME*` with the reflector name for your Indigo server. - Replace `*123456789*` with a valid Indigo action group ID. ```bash curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message": "indigo.actionGroup.execute", "objectId": 123456789}' https://YOUR-REFLECTOR-NAME.indigodomo.net/v2/api/command ``` Using these examples, you can now construct any command listed below for any action group type. --- API Messages (https://docs.indigodomo.com/2025.2/api/messages/) --- # API Messages As mentioned earlier, both the WebSocket and HTTP APIs use JSON as the message format. We have exposed JSON versions of main Indigo objects: devices, variables, action groups, and control pages. We are also exposing command messages, which parallel the IOM command namespaces (where appropriate) for each of these object types (i.e. [indigo.device](../scripting/reference/devices/base-class.md#commands-indigodevice), [indigo.variable](../scripting/reference/variables.md#commands-indigovariable), etc.), which will allow you to command devices, set variable values, execute action groups, etc. There are also two other message types: event log messages, which represent each message that flow into the Event Log window, and error messages, which is a standardized way to communicate error conditions with each API. In this section, we will give examples of each message type and describe their use. If you are new to JSON, you might want to use the [JSON Validator website](https://jsonlint.com) to validate that your JSON messages are valid JSON. ## Device Messaging This section describes the messages that you'll use when implementing either the WebSocket or HTTP APIs. They fall into two categories: device objects and device commands. ### Device Objects You’ll receive full device JSON objects which represent an Indigo device instance. Each device will be slightly different based on the device class and the definition (if a custom device). See the [Indigo Object Model](../scripting/iom-concepts.md) docs for device details. #### Example device object This is an example of an Indigo device as a JSON object. Of course, to accommodate the wide variety of supported device types, each one will be somewhat different. This one represents an Insteon Dimmer. ```json { "class": "indigo.DimmerDevice", "address": "3B.04.7A", "batteryLevel": null, "blueLevel": null, "brightness": 0, "buttonConfiguredCount": 0, "buttonGroupCount": 1, "configured": true, "defaultBrightness": 100, "description": "- sample device -", "deviceTypeId": "", "displayStateId": "brightnessLevel", "displayStateImageSel": "indigo.kStateImageSel.DimmerOff", "displayStateValRaw": 0, "displayStateValUi": "0", "enabled": true, "energyAccumBaseTime": null, "energyAccumTimeDelta": null, "energyAccumTotal": null, "energyCurLevel": null, "errorState": "", "folderId": 1552926800, "folder": { "class": "indigo.Folder", "id": 1552926800, "name": "Insteon", "remoteDisplay": true }, "globalProps": {}, "greenLevel": null, "id": 1508839119, "lastChanged": "2023-02-01T11:39:58", "lastSuccessfulComm": "2023-02-01T11:39:58", "ledStates": [], "model": "LampLinc (dual-band)", "name": "Insteon Dimmer", "onBrightensToDefaultToggle": true, "onBrightensToLast": false, "onState": false, "ownerProps": {}, "pluginId": "", "pluginProps": {}, "protocol": "indigo.kProtocol.Insteon", "redLevel": null, "remoteDisplay": false, "sharedProps": {}, "states": { "brightnessLevel": 0, "onOffState": false }, "subModel": "Plug-In", "subType": "Plug-In", "supportsAllLightsOnOff": true, "supportsAllOff": true, "supportsColor": false, "supportsOnState": true, "supportsRGB": false, "supportsRGBandWhiteSimultaneously": false, "supportsStatusRequest": true, "supportsTwoWhiteLevels": false, "supportsTwoWhiteLevelsSimultaneously": false, "supportsWhite": false, "supportsWhiteTemperature": false, "version": 67, "whiteLevel": null, "whiteLevel2": null, "whiteTemperature": null } ``` Message that contain device objects generate those objects by first converting the device to a python dictionary and then converting the python dictionary to JSON. See the [Generating a Dictionary for a device](../scripting/reference/devices/dictionary.md#generating-a-dictionary-for-a-device) section of the IOM Reference for details. !!! note The `folder` element is only available in the HTTP API. Folders are handled differently in the WebSocket API. #### Device Command Messages One thing you will notice as you are looking through these examples, is that they closely mirror the Python-based [IOM commands for controlling devices](../scripting/iom-concepts.md). This was intentional to make learning one API a stepping stone to another. The HTTP API messages and the WebSocket API messages are identical, and are very clearly a JSON-rendered version of the associated IOM command. The `id` key is optional, but may contain a user generated ID that will be logged and returned to help match up with message requests. Custom `id` values should be strings. #### indigo.device [indigo.device](../scripting/reference/devices/base-class.md#commands-indigodevice) command messages can be used for several Indigo device types - [indigo.RelayDevice](../scripting/reference/device-subclasses/relay.md#relaydevice), [indigo.DimmerDevice](../scripting/reference/device-subclasses/dimmer.md#dimmerdevice), [indigo.SpeedControl](../scripting/reference/device-subclasses/speedcontrol.md#speedcontroldevice) (fans), and some [indigo.SensorDevice](../scripting/reference/device-subclasses/sensor.md#sensordevice) instances. **status request** `[indigo.device.statusRequest(123456789)](../scripting/reference/devices/base-class.md#status-request)` ```json { "id": "optional-custom-user-message", "message": "indigo.device.statusRequest", "objectId": 123456789 } ``` **Note:** This message will work on any Indigo device type, though the device that it targets may not respond to status request messages in which case it will do nothing. This won't necessarily cause a device update message - if the device didn't have any changes after the status request, there will be no updates to the device in the server, so no update message will be sent out the websocket. **toggle** `[indigo.device.toggle(123456789, delay=5, duration=10)](../scripting/reference/devices/base-class.md#toggle)` ```json { "id": "optional-custom-user-message", "message": "indigo.device.toggle", "objectId": 123456789, "parameters": { "delay": 5, "duration": 10 } } ``` `objectId` is the id of the device. The `parameters` dictionary is optional. **turn off** `[indigo.device.turnOff(123456789, delay=5, duration=10)](../scripting/reference/devices/base-class.md#turn-off)` ```json { "id": "optional-custom-user-message", "message": "indigo.device.turnOff", "objectId": 123456789, "parameters": { "delay": 5, "duration": 10 } } ``` `objectId` is the id of the device. The `parameters` dictionary is optional. **turn on** `[indigo.device.turnOn(123456789, delay=5, duration=10)](../scripting/reference/devices/base-class.md#turn-on)` ```json { "id": "optional-custom-user-message", "message": "indigo.device.turnOn", "objectId": 123456789, "parameters": { "delay": 5, "duration": 10 } } ``` `objectId` is the id of the device. The `parameters` dictionary is optional. **lock** `[indigo.device.lock(123456789, delay=5, duration=10)](../scripting/reference/devices/base-class.md#lock)` ```json { "id": "optional-custom-user-message", "message": "indigo.device.lock", "objectId": 123456789, "parameters": { "delay": 5, "duration": 10 } } ``` `objectId` is the id of the device. The `parameters` dictionary is optional. **unlock** `[indigo.device.unlock(123456789, delay=5, duration=10)](../scripting/reference/devices/base-class.md#unlock)` ```json { "id": "optional-custom-user-message", "message": "indigo.device.unlock", "objectId": 123456789, "parameters": { "delay": 5, "duration": 10 } } ``` `objectId` is the id of the device. The `parameters` dictionary is optional. **enable/disable** `[indigo.device.enable(123456789, value=True)](../scripting/reference/devices/base-class.md#enable-disable)` ```json { "id": "optional-custom-user-message", "message": "indigo.device.enable", "objectId": 123456789, "parameters": { "value": True, } } ``` `objectId` is the id of the device. The `value` parameter is optional (`True` to enable, `False` to disable). #### indigo.dimmer [indigo.dimmer](../scripting/reference/devices/base-class.md#commands-indigodevice) command messages can be used for [indigo.DimmerDevice](../scripting/reference/device-subclasses/dimmer.md#dimmerdevice) instances. **brighten** `[indigo.dimmer.brighten(123456789, delay=5, duration=10)](../scripting/reference/device-subclasses/dimmer.md#brighten)` ```json { "id": "optional-custom-user-message", "message": "indigo.dimmer.brighten", "objectId": 123456789, "parameters": { "by": 5, "delay": 10 } } ``` `objectId` is the id of the device. The `parameters` dictionary is optional. **dim** `[indigo.dimmer.dim(123456789, delay=5, duration=10)](../scripting/reference/device-subclasses/dimmer.md#dim)` ```json { "id": "optional-custom-user-message", "message": "indigo.dimmer.dim", "objectId": 123456789, "parameters": { "by": 5, "delay": 10 } } ``` `objectId` is the id of the device. The `parameters` dictionary is optional. **set brightness** `[indigo.dimmer.setBrightness(123456789, value=50, delay=5)](../scripting/reference/device-subclasses/dimmer.md#set-brightness)` ```json { "id": "optional-custom-user-message", "message": "indigo.dimmer.setBrightness", "objectId": 123456789, "parameters": { "value": 50, "delay": 10 } } ``` `objectId` is the id of the device. The `value` parameter is required and the `delay` parameter is optional. #### indigo.iodevice [indigo.iodevice](../scripting/reference/devices/base-class.md#commands-indigodevice) command messages can be used for [indigo.MultiIODevice](../scripting/reference/device-subclasses/dimmer.md#dimmerdevice) instances. **set binary output** `[indigo.iodevice.setBinaryOutput(123456789, index=2, value=True)](../scripting/reference/device-subclasses/multiio.md#set-binary-output)` ```json { "id": "optional-custom-user-message", "message": "indigo.iodevice.setBinaryOutput", "objectId": 123456789, "parameters": { "index": 5, "value": true } } ``` `objectId` is the id of the device. The `index` and `value` parameters are required. #### indigo.sensor [indigo.sensor](../scripting/reference/device-subclasses/sensor.md#commands-indigosensor) command messages can be used for [indigo.sensorDevice](../scripting/reference/device-subclasses/sensor.md#sensordevice) instances. **set on state** `[indigo.sensor.setOnState(123456789, value=True)](../scripting/reference/device-subclasses/sensor.md#set-on-state)` ```json { "id": "optional-custom-user-message", "message": "indigo.sensor.setOnState", "objectId": 123456789, "parameters": { "value": true } } ``` `objectId` is the id of the device. The `value` parameter is required. #### indigo.speedcontrol [indigo.speedcontrol](../scripting/reference/device-subclasses/speedcontrol.md#commands-indigospeedcontrol) command messages can be used for [indigo.speedcontrol](../scripting/reference/device-subclasses/speedcontrol.md#speedcontroldevice) instances. **decrease speed index** `[indigo.speedcontrol.decreaseSpeedIndex(123456789, by=2, delay=5)](../scripting/reference/device-subclasses/speedcontrol.md#decrease-speed-index)` ```json { "id": "optional-custom-user-message", "message": "indigo.speedcontrol.decreaseSpeedIndex", "objectId": 123456789, "parameters": { "by": 2, "delay": 5 } } ``` `objectId` is the id of the device. The `by` and `delay` parameters are optional. **increase speed index** `[indigo.speedcontrol.increaseSpeedIndex(123456789, by=2, delay=5)](../scripting/reference/device-subclasses/speedcontrol.md#increase-speed-index)` ```json { "id": "optional-custom-user-message", "message": "indigo.speedcontrol.increaseSpeedIndex", "objectId": 123456789, "parameters": { "by": 2, "delay": 5 } } ``` `objectId` is the id of the device. The `by` and `delay` parameters are optional. **set speed index** `[indigo.speedcontrol.setSpeedIndex(123456789, value=2, delay=5)](../scripting/reference/device-subclasses/speedcontrol.md#set-speed-index)` ```json { "id": "optional-custom-user-message", "message": "indigo.speedcontrol.setSpeedIndex", "objectId": 123456789, "parameters": { "value": 2, "delay": 5 } } ``` `objectId` is the id of the device. The `value` parameter is required and the `delay` parameter is optional. **set speed level** `[indigo.speedcontrol.setSpeedLevel(123456789, value=50, delay=5)](../scripting/reference/device-subclasses/speedcontrol.md#set-speed-level)` ```json { "id": "optional-custom-user-message", "message": "indigo.speedcontrol.setSpeedLevel", "objectId": 123456789, "parameters": { "value": 50, "delay": 5 } } ``` `objectId` is the id of the device. The `value` parameter is required and the `delay` parameter is optional. #### indigo.sprinkler [indigo.sprinkler](../scripting/reference/device-subclasses/sprinkler.md#commands-indigosprinkler) command messages can be used for [indigo.sprinkler](../scripting/reference/device-subclasses/sprinkler.md#sprinklerdevice) instances. **next zone** `[indigo.sprinkler.nextZone(123456789)](../scripting/reference/device-subclasses/sprinkler.md#next-zone)` ```json { "id": "optional-custom-user-message", "message": "indigo.sprinkler.nextZone", "objectId": 123456789 } ``` `objectId` is the id of the device. The `next zone` command does not have any additional parameters. **pause schedule** `[indigo.sprinkler.pause(123456789)](../scripting/reference/device-subclasses/sprinkler.md#pause-schedule)` ```json { "id": "optional-custom-user-message", "message": "indigo.sprinkler.pause", "objectId": 123456789 } ``` `objectId` is the id of the device. The `next zone` command does not have any additional parameters. **previous zone** `[indigo.sprinkler.previousZone(123456789)](../scripting/reference/device-subclasses/sprinkler.md#previous-zone)` ```json { "id": "optional-custom-user-message", "message": "indigo.sprinkler.previousZone", "objectId": 123456789 } ``` `objectId` is the id of the device. The `previous zone` command does not have any additional parameters. **resume schedule** `[indigo.sprinkler.resume(123456789)](../scripting/reference/device-subclasses/sprinkler.md#resume-schedule)` ```json { "id": "optional-custom-user-message", "message": "indigo.sprinkler.resume", "objectId": 123456789 } ``` `objectId` is the id of the device. The `resume schedule` command does not have any additional parameters. **run schedule** `[indigo.sprinkler.run(123456789, schedule=[10, 15, 8, 0, 0, 0, 0, 0])](../scripting/reference/device-subclasses/sprinkler.md#run-schedule)` ```json { "id": "optional-custom-user-message", "message": "indigo.sprinkler.run", "objectId": 123456789, "parameters": { "schedule": [10, 15, 8, 0, 0, 0, 0, 0] } } ``` `objectId` is the id of the device. The `schedule` parameter is required. **stop schedule** `[indigo.sprinkler.stop(123456789)](../scripting/reference/device-subclasses/sprinkler.md#stop-schedule)` ```json { "id": "optional-custom-user-message", "message": "indigo.sprinkler.stop", "objectId": 123456789 } ``` `objectId` is the id of the device. The `resume schedule` command does not have any additional parameters. **set active zone** `[indigo.sprinkler.setActiveZone(123456789, index=2)](../scripting/reference/device-subclasses/sprinkler.md#set-active-zone)` ```json { "id": "optional-custom-user-message", "message": "indigo.sprinkler.setActiveZone", "objectId": 123456789, "parameters": { "index": 2 } } ``` `objectId` is the id of the device. The `index` parameter is required. #### indigo.thermostat [indigo.thermostatdevice](../scripting/reference/device-subclasses/thermostat.md#commands-indigothermostat) command messages can be used for [indigo.thermostat](../scripting/reference/device-subclasses/thermostat.md#thermostatdevice) instances. **decrease cool setpoint** `[indigo.thermostat.decreaseCoolSetpoint(123456789, delta=2)](../scripting/reference/device-subclasses/thermostat.md#decrease-cool-setpoint)` ```json { "id": "optional-custom-user-message", "message": "indigo.thermostat.decreaseCoolSetpoint", "objectId": 123456789, "parameters": { "delta": 2 } } ``` `objectId` is the id of the device. The `value` parameter is optional. **decrease heat setpoint** `[indigo.thermostat.decreaseHeatSetpoint(123456789, delta=2)](../scripting/reference/device-subclasses/thermostat.md#decrease-heat-setpoint)` ```json { "id": "optional-custom-user-message", "message": "indigo.thermostat.decreaseHeatSetpoint", "objectId": 123456789, "parameters": { "delta": 2 } } ``` `objectId` is the id of the device. The `value` parameter is optional. **increase cool setpoint** `[indigo.thermostat.increaseCoolSetpoint(123456789, delta=2)](../scripting/reference/device-subclasses/thermostat.md#increase-cool-setpoint)` ```json { "id": "optional-custom-user-message", "message": "indigo.thermostat.increaseCoolSetpoint", "objectId": 123456789, "parameters": { "delta": 2 } } ``` `objectId` is the id of the device. The `value` parameter is optional. **increase heat setpoint** `[indigo.thermostat.increaseHeatSetpoint(123456789, delta=2)](../scripting/reference/device-subclasses/thermostat.md#increase-heat-setpoint)` ```json { "id": "optional-custom-user-message", "message": "indigo.thermostat.increaseHeatSetpoint", "objectId": 123456789, "parameters": { "delta": 2 } } ``` `objectId` is the id of the device. The `value` parameter is optional. **set cool setpoint** `[indigo.thermostat.setCoolSetpoint(123456789, value=76)](../scripting/reference/device-subclasses/thermostat.md#set-cool-setpoint)` ```json { "id": "optional-custom-user-message", "message": "indigo.thermostat.setCoolSetpoint", "objectId": 123456789, "parameters": { "value": 76 } } ``` `objectId` is the id of the device. The `value` parameters is required. **set fan mode** `[indigo.thermostat.setFanMode(123456789, value=indigo.kFanMode.AlwaysOn)](../scripting/reference/device-subclasses/thermostat.md#set-fan-mode)` ```json { "id": "optional-custom-user-message", "message": "indigo.thermostat.setFanMode", "objectId": 123456789, "parameters": { "value": "indigo.kFanMode.AlwaysOn" } } ``` `objectId` is the id of the device. The `value` parameter is required. **set heat setpoint** `[indigo.thermostat.setHeatSetpoint(123456789, value=76)](../scripting/reference/device-subclasses/thermostat.md#set-heat-setpoint)` ```json { "id": "optional-custom-user-message", "message": "indigo.thermostat.setHeatSetpoint", "objectId": 123456789, "parameters": { "value": 76 } } ``` `objectId` is the id of the device. The `value` parameter is required. **set hvac mode** `[indigo.thermostat.setHvacMode(123456789, value=indigo.kHvacMode.HeatCool)](../scripting/reference/device-subclasses/thermostat.md#set-hvac-mode)` ```json { "id": "optional-custom-user-message", "message": "indigo.thermostat.setHvacMode", "objectId": 123456789, "parameters": { "value": "indigo.kHvacMode.HeatCool" } } ``` `objectId` is the id of the device. The `value` parameter is required. ### Variable Messaging This section describes the messages that you'll use when implementing either the WebSocket or HTTP APIs when dealing with Indigo devices. They fall into two categories: device objects and device command messages. #### Variable Objects You’ll receive full variable JSON objects which represent an Indigo variable instance. See the [Indigo Object Model](../scripting/iom-concepts.md) docs for device details. ##### Example variable object ```json { "class": "indigo.Variable", "description": "", "folderId": 0, "folder": {}, "globalProps": { "com.indigodomo.indigoserver": {} }, "id": 345633244, "name": "house_status", "pluginProps": {}, "readOnly": false, "remoteDisplay": true, "sharedProps": {}, "value": "home" } ``` Messages that contain variable objects generate those objects by first converting the variable to a python dictionary and then converting the python dictionary to JSON. See the [Generating a Dictionary for a variable](../scripting/reference/variables.md) section of the IOM Reference for details. !!! note The `folder` element is only available in the HTTP API. Folders are handled differently in the WebSocket API. #### Variable Command Messages The only action that can currently be performed on a variable is to update the value. The `id` key is optional, but may contain a user generated ID that will be logged and returned to help match up with message requests. Custom `id` values should be strings. **updateValue** `[indigo.variable.updateValue(123456789, value="Some string value")](../scripting/reference/variables.md#update-value)` ```json { "id": "optional-custom-user-message", "message": "indigo.variable.updateValue", "objectId": 123456789, "parameters": { "value": "Some string value" } } ``` `objectId` is the id of the variable. The `value` parameter is required and must be a string. Pass an empty string ("") to clear the variable value. ### Action Group Messaging #### Action Group Objects You’ll receive full variable JSON objects which represent an Indigo action group instance. See the [Indigo Object Model](../scripting/iom-concepts.md) docs for action group details. ##### Example action group object ```json { "class": "indigo.ActionGroup", "description": "", "folderId": 532526508, "folder": { "class": "indigo.Folder", "id": 532526508, "name": "Mood Scenes", "remoteDisplay": true }, "globalProps": { "com.indigodomo.indigoserver": { "speakDelayTime": "5", "speakTextVariable": "speech_string" } }, "id": 94914463, "name": "Movie Night", "pluginProps": {}, "remoteDisplay": true, "sharedProps": { "speakDelayTime": "5", "speakTextVariable": "speech_string" } } ``` !!! note The `folder` element is only available in the HTTP API. Folders are handled differently in the WebSocket API. #### Action Group Command Messages There is only a single action group command, and that's to execute it. The `id` key is optional, but may contain a user generated ID that will be logged and returned to help match up with message requests. Custom `id` values should be strings. ** execute ** `[indigo.actionGroup.execute(123456789)](../scripting/reference/action-groups.md#execute)` ```json { "id": "optional-custom-user-message", "message": "indigo.actionGroup.execute", "objectId": 123456789 } ``` `objectId` is the id of the action group. ### Schedule Messaging #### Schedule Objects You’ll receive full variable JSON objects which represent an Indigo schedule instance. See the [Indigo Object Model](../scripting/iom-concepts.md) docs for schedule details. ```json { "class": "indigo.Schedule", "absoluteDate": null, "absoluteDateTime": null, "absoluteTime": null, "autoDelete": false, "configured": true, "dateType": 0, "description": "", "enabled": false, "folderId": 0, "globalProps": {}, "id": 12345678, "name": "My Schedule Name", "nextExecution": null, "pluginProps": {}, "randomizeBy": 0, "remoteDisplay": true, "sharedProps": {}, "sunDelta": 0, "suppressLogging": false, "timeType": 3 } ``` #### Schedule Command Messages These are the available schedule commands. The `id` key is optional, but may contain a user generated ID that will be logged and returned to help match up with message requests. Custom `id` values should be strings. ** execute ** `[indigo.schedule.execute(123456789)](../scripting/reference/schedules.md#execute)` ```json { "id": "optional-custom-user-message", "message": "indigo.schedule.execute", "objectId": 12345678, } ``` `objectId` is the id of the schedule. ** enable ** `[indigo.schedule.enable(123456789)](../scripting/reference/schedules.md#enable)` ```json { "id": "optional-custom-user-message", "message": "indigo.schedule.enable", "objectId": 12345678, "parameters": {"value": false} } ``` ### Trigger Messaging #### Trigger Objects You’ll receive full variable JSON objects which represent an Indigo trigger instance. See the [Indigo Object Model](../scripting/iom-concepts.md) docs for trigger details. ```json { "class": "indigo.Trigger", "configured": true, "description": "A trigger description.", "enabled": true, "folderId": 12345678, "globalProps": { "com.indigodomo.indigoserver": {}, "com.indigodomo.webserver": { "httpMethod": "GET", "postProcessing": "JSON", "webhookId": "8aa4b5140c5c473ea170465239143839" }, "emptyDict": {} }, "id": 12345678, "name": "My Trigger Name", "pluginProps": {}, "remoteDisplay": true, "sharedProps": {}, "suppressLogging": false } ``` #### Trigger Command Messages These are the available trigger commands. The `id` key is optional, but may contain a user generated ID that will be logged and returned to help match up with message requests. Custom `id` values should be strings. ** execute ** `[indigo.trigger.execute(123456789)](../scripting/reference/triggers.md#execute)` ```json { "id": "optional-custom-user-message", "message": "indigo.trigger.execute", "objectId": 12345678, } ``` `objectId` is the id of the trigger. ** enable ** `[indigo.trigger.enable(123456789)](../scripting/reference/triggers.md#enable)` ```json { "id": "optional-custom-user-message", "message": "indigo.trigger.enable", "objectId": 12345678, "parameters": {"value": false} } ``` ### Log Messages We convert the event Indigo dictionary into a python dictionary in the `event_log_line_received` plugin method. #### Example Log Message ```json { "message": "Stopping plugin \"Web Server 2025.1.0\" (pid 1020)", "timeStamp": "2022-12-01T12:03:27.759000", "typeStr": "Application", "typeVal": 0 "objectType": "indigo.LogEvent" } ``` Note that `typeVal` will be one of the following values: ```python EVENT_TYPES = { Application = 0, Error = 1, Error_Client = 2, Warning = 3, Warning_Client = 4, Debug_Server = 5, Debug_Client = 6, Debug_Plugin = 7, Custom = 8, } ``` You can use the values above to help determine any kind of decoration you want to use when displaying or otherwise interpreting the log event. #### Log Command Message In some situations, you might want to send a log message to the server and have that message appear in the Indigo Event Log. This is done using the *`indigo.server.log`* command namespace and the *`command`* API endpoint. Here is full python example with a log message payload: ```python import requests REFLECTOR = "MY_REFLECTOR_NAME" API_KEY = "MY_API_KEY" url = f"https://{REFLECTOR}.indigodomo.net/v2/api/command" headers = {'Authorization': f'Bearer {API_KEY}'} message = { "id": "optional-user-generated-id", "messageText": "Some important log message goes here.", "message": "indigo.server.log", } response = requests.post(url, headers=headers, json=message) if response.status_code == 200: device_list = response.json() print(device_list) else: print(f"Failed to fetch data. Status code: {response.status_code} - {response.text}") ``` ## Plugin Messaging There are two plugin-based command messages in the HTTP API: `*plugin.restart*` and `*plugin.executeAction*`. ### Plugin Command Messages **restart plugin ** When the `*plugin.restart*` command message is sent to the HTTP API, Indigo will restart the target plugin **only if it is installed and enabled.** ```python { "id": "some-optional-message-text", "message": "plugin.restart", "pluginId": "com.some.indigo.plugin" # the plugin's bundle identifier } ``` ** Execute Plugin Action ** When the `*plugin.executeAction*` command message is sent to the HTTP API, Indigo will attempt to fire the plugin action **as long as the plugin is installed and enabled.** Many plugin actions require additional properties to complete (such as plugin Action configuration settings) and those are included as `*props*`. The `*props*` payload must be valid JSON. ```python { "id": "some-optional-message-text", "message": "plugin.executeAction", "pluginId": "com.some.indigo.plugin", # the plugin's bundle identifier "actionId": "some_plugin_action", # the ID of the plugin action found in the plugin's Actions.xml file [string] "deviceId": 12345678, # the device ID targeted by the plugin action (not all actions will require a device ID). [int] "props": {"prop1": "foo", "prop2": "bar"}, # required if the plugin action requires props to fire the action [valid JSON] "waitUntilDone": True # optional [True/False] } ``` ## Error Messaging Error messages will be returned to API clients in the event that something didn’t go as planned. The structure of messages returned will depend on what went wrong and how the message was sent. **Note that folder messages are added to the feed by the server; there are no folder endpoints to manage folders from a client at this time.** A generic example message is provided in JSON format: ### Generic Error Message ```json { "error": "Some error description", "id": "the id sent from the client in the message, or null if there wasn't one", "validationErrors": { "field1": "some error that occurred in the message with the key field1" } } ``` where the value of the JSON name (or key) `validationErrors` is a dictionary with a field name and a description of the error in that field that came from the client. For instance, if you pass a float `value` to the `indigo.variable.updateValue` message: #### Example API Call ```json { "id": "a-random-id-for-this-message", "message": "indigo.variable.updateValue", "objectId": 123456789, "parameters": { "value": 1234.56 } } ``` You will receive the following error response: ##### Resulting Error Message ```json { "validationErrors": { "value": "variable values must be strings" }, "error": "invalid command payload received, id: my-set-var-command", "id": "a-random-id-for-this-message" } ``` The `error` key is the indicator that the response is some kind of error. `validationErrors` is a dict which contains all the validation errors for the message, in this case the `value` that was passed in was not a string (it was the float `1234.56`). The possible keys in the error message reply `validationErrors` could be: 1. `message`, 1. `objectId`, 1. `parameters`, and 1. `value` based on the `indigo.variable.updateValue` message format (we do **no validation** on the `id` value passed in, we pass it through, and if your message doesn’t contain one then the value will be `null`). Only the keys from your message that have errors will be returned. So in the example error above, only the `value` key had a validation error (because we passed in a float) so that was the only key returned. `id` is the ID you (may have) passed in when you sent the command message. #### Invalid JSON One other type of error that you may receive would be if you POST a string (or something else, like XML, etc.) that’s not JSON. This will result in the following message return: ##### Example Invalid JSON Message ```json { "request_body": "this is not valid JSON", "error": "invalid JSON" } ``` We will return the entire request body since it isn’t valid JSON, and we don’t know what else to do with it. #### Web Server Warnings Occasionally, you might see a warning from the Web Server written to the Indigo Event Log that might look something like this: *`Web Server Warning HTTP 400 error for request /v2/api/command/ from 123.45.678.90`* The codes are standard HTTP response codes and may help you to determine what happened. The most common codes you might encounter are: | Code | Condition | Description | |------|-----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 200 | OK or Success | Indicates that the request has succeeded. | | 400 | Bad Request | Indicates that the server cannot or will not process the request due to something that is perceived to be a client error (for example, malformed request syntax, invalid request message framing, or deceptive request routing) | | 401 | Unauthorized | Indicates that the client request has not been completed because it lacks valid authentication credentials for the requested resource. | | 404 | Not Found | Indicates that the server cannot find the requested resource. | | 500 | Internal Server Error | Indicates that the server encountered an unexpected condition that prevented it from fulfilling the request. | You might see another code from time to time, and a complete list of codes and their meanings can be found on the [Mozilla web docs](https://developer.mozilla.org/en-US/docs/Web/HTTP) site under "HTTP response status codes". #### Other Errors and Warnings From time to time, you might see other errors and warnings as you use the APIs. These messages can appear in the Indigo Events Log, the Web Server plugin log, and in your client's console. Sometimes, these messages can be somewhat generic (it's not possible to catch every conceivable error) so it can be helpful to know where to look for answers. A likely place where errors can creep into your API scripting involves the payloads that are sent to and from the Indigo Web Server. For example, JSON payloads may be converted to XML behind the scenes, so bear in mind the XML conventions found in the [Plugin Developer's Guide](../plugin-dev/guide.md#indigo-plugin-xml-conventions). --- Migrating from the REST API (https://docs.indigodomo.com/2025.2/api/rest-migration/) --- # REST API Conversion Examples !!! abstract "In this guide" This page provides side-by-side examples for converting legacy REST API calls to the newer HTTP API format. Familiarity with [HTTP API authentication](http.md#authentication) and [JSON message formats](messages.md) is recommended before working through the examples. If you're using the old REST API (which has been deprecated), here are some examples of how you might convert your REST usages to the HTTP API. The first thing you'll want to understand is how to use [authentication with the HTTP API](http.md#authentication). The examples below use both headers and query args for authentication. You can use whichever works for you. You should replace `YOUR-API-KEY` with [your actual API Key](https://www.indigodomo.com/account/authorizations). Second, all replies to the API will be [JSON messages](messages.md). In the following examples, we'll be using a mix of authentication headers and the API Key as a query arg. You can use either. We mark the examples **REST** (old REST API) and **HTTP** (newer HTTP API). ## Device Access { .ref-head-no-code } ### Getting Devices { .ref-head-no-code } #### get device list { .ca } **REST** ```bash http://username:password@127.0.0.1:8176/devices.json ``` **HTTP** ```bash http://127.0.0.1:8176/v2/api/indigo.devices?api-key=YOUR-API-KEY ``` This will return a JSON list of [Device Objects](messages.md#device-objects). #### get single device { .ca } **REST** ```bash http://username:password@127.0.0.1:8176/devices/office-lamp.json ``` **HTTP** ```bash http://127.0.0.1:8176/v2/api/indigo.devices/123456789?api-key=YOUR-API-KEY ``` Insert the ID of the `office-lamp` device rather than the name. Note you can quickly copy the device ID to the clipboard by right-clicking on the device in Indigo app's main window and choosing the `Copy ID` context menu. This will return a single [Device Object](messages.md#device-objects). ### Device Commands { .ref-head-no-code } Sending commands to devices requires that you POST a JSON message to the `/v2/api/command` URL. #### set brightness { .ca } **REST** ```bash curl -X PUT -u user:password --digest -d brightness=27 http://127.0.0.1:8176/devices/office-lamp ``` **HTTP** ```bash curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.device.setBrightness","objectId":123456789,"parameters":{"value":27}}' http://127.0.0.1:8176/v2/api/command ``` Insert the ID of the `office-lamp` device as the objectId. #### turn on/turn off { .ca } **REST** ```bash curl -X PUT -u user:password --digest -d isOn=1 http://127.0.0.1:8176/devices/office-lamp curl -X PUT -u user:password --digest -d isOn=0 http://127.0.0.1:8176/devices/office-lamp ``` **HTTP** ```bash curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.device.turnOn","objectId":123456789}' http://127.0.0.1:8176/v2/api/command curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.device.turnOff","objectId":123456789}' http://127.0.0.1:8176/v2/api/command ``` Insert the ID of the `office-lamp` device as the objectId. #### toggle { .ca } **REST** ```bash curl -X PUT -u user:password --digest -d toggle=1 http://127.0.0.1:8176/devices/office-lamp ``` **HTTP** ```bash curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.device.toggle","objectId":123456789}' http://127.0.0.1:8176/v2/api/command ``` Insert the ID of the `office-lamp` device as the objectId. #### change speed index (fan) { .ca } These examples will set device `office-ceiling-fan` to 0 (off), then set to 3 (high), then decrease back to 0 (off). **REST** ```bash curl -X PUT -u user:password --digest -d speedIndex=0 http://127.0.0.1:8176/devices/office-ceiling-fan curl -X PUT -u user:password --digest -d speedIndex=3 http://127.0.0.1:8176/devices/office-ceiling-fan curl -X PUT -u user:password --digest -d speedIndex=dn http://127.0.0.1:8176/devices/office-ceiling-fan curl -X PUT -u user:password --digest -d speedIndex=dn http://127.0.0.1:8176/devices/office-ceiling-fan curl -X PUT -u user:password --digest -d speedIndex=dn http://127.0.0.1:8176/devices/office-ceiling-fan ``` **HTTP** ```bash curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.speedcontrol.setSpeedIndex","objectId":123456789,"parameters":{"value":0}}' http://127.0.0.1:8176/v2/api/command curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.speedcontrol.setSpeedIndex","objectId":123456789,"parameters":{"value":3}}' http://127.0.0.1:8176/v2/api/command curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.speedcontrol.decreaseSpeedIndex","objectId":123456789}' http://127.0.0.1:8176/v2/api/command curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.speedcontrol.decreaseSpeedIndex","objectId":123456789}' http://127.0.0.1:8176/v2/api/command curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.speedcontrol.decreaseSpeedIndex","objectId":123456789}' http://127.0.0.1:8176/v2/api/command ``` Insert the ID of the `office-ceiling-fan` device as the objectId. #### change sprinkler zones { .ca } These examples will change device `irrmaster-pro` active sprinkler zone to 3 and all off. **REST** ```bash curl -X PUT -u user:password --digest -d activeZone=3 http://127.0.0.1:8176/devices/irrmaster-pro curl -X PUT -u user:password --digest -d activeZone=0 http://127.0.0.1:8176/devices/irrmaster-pro ``` **HTTP** ```bash curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.sprinkler.setActiveZone","objectId":123456789,"parameters":{"index":3}}' http://127.0.0.1:8176/v2/api/command curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.sprinkler.setActiveZone","objectId":123456789,"parameters":{"index":0}}' http://127.0.0.1:8176/v2/api/command ``` Insert the ID of the `irrmaster-pro` device as the objectId. #### set thermostat setpoints { .ca } These examples will set device `thermostat`'s heat and cool setpoints **REST** ```bash curl -X PUT -u user:password --digest -d setpointCool=76 http://127.0.0.1:8176/devices/thermostat curl -X PUT -u user:password --digest -d setpointHeat=70 http://127.0.0.1:8176/devices/thermostat ``` **Increase Heat Setpoint** ```bash curl -X PUT -u user:password --digest -d setpointHeat=up http://127.0.0.1:8176/devices/thermostat ``` **HTTP** ```bash curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.thermostat.setCoolSetpoint","objectId":123456789,"parameters":{"value":76}}' http://127.0.0.1:8176/v2/api/command curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.thermostat.setHeatSetpoint","objectId":123456789,"parameters":{"value":70}}' http://127.0.0.1:8176/v2/api/command ``` **Increase Heat Setpoint** ```bash curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.thermostat.increaseHeatSetpoint","objectId":123456789}' http://127.0.0.1:8176/v2/api/command ``` Insert the ID of the `thermostat` device as the objectId. #### set thermostat mode { .ca } These examples will set device `thermostat`'s mode to "cool on" and "auto on" **REST** ```bash curl -X PUT -u user:password --digest -d hvacCurrentMode="cool on" http://127.0.0.1:8176/devices/thermostat curl -X PUT -u user:password --digest -d hvacCurrentMode="auto on" http://127.0.0.1:8176/devices/thermostat ``` **HTTP** ```bash curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.thermostat.setHvacMode","objectId":123456789,"parameters":{"value":"indigo.kHvacMode.Cool"}}' http://127.0.0.1:8176/v2/api/command curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.thermostat.setHvacMode","objectId":123456789,"parameters":{"value":"indigo.kHvacMode.HeatCool"}}' http://127.0.0.1:8176/v2/api/command ``` Insert the ID of the `thermostat` device as the objectId. #### set thermostat fan mode { .ca } These examples will set device `thermostat`'s fan mode to "always on" and "auto on" **REST** ```bash curl -X PUT -u user:password --digest -d hvacFanMode="always on" http://127.0.0.1:8176/devices/thermostat curl -X PUT -u user:password --digest -d hvacFanMode="auto on" http://127.0.0.1:8176/devices/thermostat ``` **HTTP** ```bash curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.thermostat.setFanMode","objectId":123456789,"parameters":{"value":"indigo.kFanMode.AlwaysOn"}}' http://127.0.0.1:8176/v2/api/command curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.thermostat.setFanMode","objectId":123456789,"parameters":{"value":"indigo.kFanMode.Auto"}}' http://127.0.0.1:8176/v2/api/command ``` Insert the ID of the `thermostat` device as the objectId. ## Variable Access { .ref-head-no-code } ### Getting Variables { .ref-head-no-code } #### get variable list { .ca } **REST** ```bash http://username:password@127.0.0.1:8176/variables.json ``` **HTTP** ```bash http://127.0.0.1:8176/v2/api/indigo.variables?api-key=YOUR-API-KEY ``` This will return a JSON list of [Variable Objects](messages.md#variable-objects). #### get single variable { .ca } **REST** ```bash http://username:password@127.0.0.1:8176/variables/sprinklerDurationMultiplier.json ``` **HTTP** ```bash http://127.0.0.1:8176/v2/api/indigo.variables/123456789?api-key=YOUR-API-KEY ``` Insert the ID of the `sprinklerDurationMultiplier` variable rather than the name. This will return a single [Variable Object](messages.md#variable-objects). ### Variable Commands { .ref-head-no-code } Sending commands to the server requires that you POST a JSON message to the `/v2/api/command` URL. #### update variable value { .ca } **REST** ```bash curl -X PUT -u user:password --digest -d value=1.23 http://127.0.0.1:8176/variables/sprinklerDurationMultiplier ``` **HTTP** ```bash curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.variable.updateValue","objectId":123456789,"parameters":{"value":"1.23"}}' http://127.0.0.1:8176/v2/api/command ``` Insert the ID of the `sprinklerDurationMultiplier` variable as the objectId. **Note**: variable values are always strings to make sure to enclose the value in quotes. ## Action Group Access { .ref-head-no-code } ### Getting Action Groups { .ref-head-no-code } #### get action group list { .ca } **REST** ```bash http://username:password@127.0.0.1:8176/actions.json ``` **HTTP** ```bash http://127.0.0.1:8176/v2/api/indigo.actionGroups?api-key=YOUR-API-KEY ``` This will return a JSON list of [Action Group Objects](messages.md#action-group-objects). #### get single action group { .ca } **REST** ```bash http://username:password@127.0.0.1:8176/actions/party%20scene.json ``` **HTTP** ```bash http://127.0.0.1:8176/v2/api/indigo.actionGroups/123456789?api-key=YOUR-API-KEY ``` Insert the ID of the `party scene` action group rather than the name. This will return a single [Action Group Object](messages.md#action-group-objects). ### Action Group Commands { .ref-head-no-code } Sending commands to the server requires that you POST a JSON message to the `/v2/api/command` URL. #### execute action group { .ca } **REST** ```bash curl -X EXECUTE -u user:password --digest -d value=1.23 http://127.0.0.1:8176/actions/party%20scene ``` **HTTP** ```bash curl -X POST -H "Authorization: Bearer YOUR-API-KEY" -d '{"message":"indigo.actionGroup.execute","objectId":123456789}' http://127.0.0.1:8176/v2/api/command ``` Insert the ID of the `party scene` action group as the objectId. --- Webhooks (https://docs.indigodomo.com/2025.2/api/webhooks/) --- # Webhooks !!! abstract "In this guide" How to configure Indigo to receive webhooks from external services using the Webhook trigger type. Covers the three webhook data formats (JSON POST, FORM POST, GET), the URL pattern for calling a webhook, API key authentication, and how received data is passed through to action scripts via the event data mechanism. Another feature that we started (before going down the event data path) is to make it easier for users to capture webhooks. As you may know, these are quite popular with internet services, be it doorbells, network equipment, location apps, etc. These services can sometimes be used with the HTTP API, but there are limitations to that which cause pain. Without the event data mechanism, webhooks might still be somewhat useful, but to make them really useful we needed to pass the data through to actions. That was the final motivation to create the mechanism discussed above. The new **Webhook** trigger event type (under the **Web Server** trigger section) allows the user to specify an ID (we prepopulate it with a random UUID but the user can override that if they like) and how the data will come in. There are 3 options for receiving webhook events: 1. JSON POST - this type of webhook will accept a POST and interpret the payload as JSON. The `event_data` dict will contain two keys: `http-post-content` which will be `"JSON"` and `data` which will be the full JSON payload as a dict or list, whatever the caller passes in. 1. FORM POST - this type of webhook will accept a POST with optional form data, which will be converted into a dict of name value pairs and passed through as `data`. The `http-post-content` key will be `"FORM"` in this case. 1. GET - this type of webhook will accept a GET and will pass through any query arguments as the `data` element. Calling a webhook is a very straight-forward URL pattern: ```text https://myreflector.indigodomo.net/webhook/IDFROMCONFIG ``` As with the other APIs, the caller can either send a `Authorization` header of `Bearer APIKEYHERE` or can pass it as a query arg to the url: ```text https://myreflector.indigodomo.net/webhook/IDFROMCONFIG?api-key=APIKEYHERE ``` We believe this covers the vast majority of webhook options that are in use at the moment (let us know if you have an example of one which wouldn't work correctly and we'll look into it). ## Broadcast Webhooks will also use the broadcast function to send out the entire `event_data` dict to any plugins that subscribe to events from the web server: ```python indigo.server.broadcastToSubscribers("webhook-received", message) ``` Subscribers can: ```python iws_id = "com.indigodomo.webserver" indigo.server.subscribeToBroadcast(iws_id, "webhook-received", "webhook_received_method") ``` ## Locative Webhook Examples The [Locative geolocation app](https://www.locative.app) provides location notifications on iOS. It has very good support for webhook events and therefore is a great example for our webhook implementation. Here's a rough summary of how locative works: users can define a geofence (or beacon) as a location. Here's an example of two locations we'll use in this example: ![](../images/locative-main.jpeg){ width=300 } We've defined (completely made up) two geofences, one that represents a home and another representing a work location. Whenever a location is entered or exited, Locative can send webhook requests that Indigo can catch and act upon: ![](../images/locative-home.jpeg){ width=300 } Here's what each of those fields represent: - LOCATION ID - this is an ID that will be sent to the webhooks endpoint whenever the webhooks are sent so if so desired the user can use a single endpoint to handle all locations. We describe this approach in the advanced below. - WEBHOOK ON ARRIVAL - this is the URL that will be used when the geofence is entered. You also specify the request type (Indigo handles both POST and GET requests). - WEBHOOK ON DEPARTURE - this is the URL that will be used when the geofence is exited. You also specify the request type (Indigo handles both POST and GET requests). - WEBHOOK AUTHENTICATION - you will be using an API key specified on the URL line, so this setting should always be Disabled. - CUSTOM REQUEST-DESIGN - Locative allows you to specify which data will be sent to the webhook. We'll describe below two different payloads (one JSON and the other FORM POST) that we defined for this though you can create your own. ### Payloads As mentioned above, we define two payloads. The first payload uses JSON format while the second is a FORM POST which will also double as GET query string parameters. First, we define the "JSON" payload like this: ![](../images/locative-json-payload.jpeg){ width=300 } The Content Type is set to application/json and for completeness we're including all the fields that Locative supports (as of this writing). The resulting JSON looks something like this: ```json { "device": "A-UUID-THAT-REPRESENTS-YOUR-IOS-DEVICE", "device_model": "iPhone15,2", "device_type": "iOS", "id": "home", "latitude": 29.12345, "longitude": -96.12345, "timestamp": 1759348496.39196, "trigger": "enter" } ``` The other payload is "FORM POST", which is similar but instead of JSON we specify it as //`application/x-www-form-urlencoded; charset=utf-8`//: ![](../images/locative-form-get-payload.jpeg){ width=300 } We also specify all the available fields here like with the JSON payload. One more setting in the Locative app (select the Settings tab at the bottom) - you should turn on the Send Payload as URL-Query when using GET-Method setting so that if you select GET you will get all the values as a query string which Indigo will also make available. ### Update a Variable When Entering / Leaving Home For our first example, we will create a simple JSON webhook that updates a variable when entering or leaving home. In other words, we're going to configure a webhook for a geofence that we will configure as "home-json". This webhook will catch the webhook enter and exit messages from Locative and update a variable named home. The value will come directly from the JSON payload described earlier. #### Indigo configuration steps On the Indigo side, we're going to create a variable called home and then add a webhook trigger that will catch the webhook from Locative and change the variable value to the value received in the trigger field. Here are the steps: 1. Create a variable and name it "home" (or whatever you want) 1. Configure a new Webhook Trigger action (select Web Server Event from Type and Webhook from Event). ![](../images/locative-home-json-trigger.jpeg){ width=500 } The Webhook ID field specifies the name of the webhook that you'll use on your URL. The Method is POST and the processing is JSON. Next, switch to the Actions tab and select *`Variable Actions->Insert Event Data into Variable`* from the Type popup, which will bring up the config dialog for that action: ![](../images/locative-home-json-trigger-actions.jpeg){ width=500 } Select the Variable you created in step 1 above, then enter *`data.trigger`* in the Path specifier field. A brief reminder: this is what the event_data contains from a webhook trigger event: ```text event_data = { 'data': { 'device': 'YOUR-DEVICES-UUID', 'device_model': 'iPhone15,2', 'device_type': 'iOS', 'id': 'home', 'latitude': 30.25458515413769, 'longitude': -97.76605401300928, 'timestamp': 1759348496.39196, 'trigger': 'enter' }, 'event-indigo-id': 264490832, 'event-plugin-event-id': 'simpleWebhook', 'event-plugin-id': 'com.indigodomo.webserver', 'event-plugin-name': 'Web Server', 'event-type': 'PluginEventTrigger', 'http-method': 'POST', 'http-post-content': 'JSON', 'request-url': 'http://localhost:8176/webhook/home-json', 'source': 'python', 'status-code': 200, 'timestamp': '2025-10-01T14:54:56', 'webhook-id': 'home-json' } ``` Note that the JSON data from Locative is stored in the *`data`* part of the event data dictionary, and the trigger event is in the *`trigger`* field, so we specify that as *`data.trigger`*. #### Locative configuration steps Create a new Geofence that represents your home (or whatever). Here's how the webhooks are configured in Locative: ![](../images/locative-home.jpeg){ width=300 } You set the URL in both arrival and departure to this: ```text https://REFLECTORNAMEHERE.indigodomo.net/webhook/home-json?api-key=APIKEYHERE ``` Change to your reflector name and add an API key at the end. Note the webhook ID that you set in the trigger definition is used as the last part of the path specifier (`*home-json*` in this case). Then select POST for both, then JSON (or whatever you named your definition). #### Test your setup We recommend enabling debug logging for the webserver (Indigo XXXX.Y->Advanced Web Server Settings... then click on the Debug Logging option). This will show you the traffic between Locative and Indigo. Then in the Locative app's Locations list, long press on your home location and select *`Trigger "Enter" Event`* and watch your Event Log window in Indigo. You should see something like this: ```text Web Server Debug caught webhook with ID: 'home-json' Web Server Debug received webhook 'home-json-locative' with JSON data: {'longitude': XXXXX, 'device_type': 'iOS', 'id': 'home', 'latitude': XXXXXX, 'timestamp': 1759522339.223068, 'device': 'YOUR-DEVICES-UUID', 'device_model': 'iPhone15,2', 'trigger': 'enter'} Web Server Debug webhook 'home-json-locative' message sent to subscribers: {'webhook-id': 'home-json', 'status-code': 200, 'http-method': 'POST', 'request-url': 'http://localhost:8176/webhook/home-json', 'http-post-content': 'JSON', 'data': {'longitude': XXXXXX, 'device_type': 'iOS', 'id': 'home', 'latitude': XXXXXX, 'timestamp': 1759522339.223068, 'device': 'YOUR-DEVICES-UUID', 'device_model': 'iPhone15,2', 'trigger': 'enter'}} ``` Hopefully you'll see this and you'll see the value of the *`home`* Indigo variable set to *`enter`*. You can test the exit as well to make sure that your variable is getting updated correctly. You can disable debug logging once you've confirmed it's working correctly. If you see an error about the webhook being undefined, or you see a Not Found (404) error, you may need to restart Indigo. #### Summary In summary, we created a geofence in Locative that represents your home and configured it to send a JSON webhook to Indigo, where we catch the webhook and update a variable with the *`trigger`* value, either *`enter`* or *`exit`* which represents your iOS device arriving home or leaving. ### Update a Variable When Entering / Leaving Work For our second example, simple FORM POST and GET webhooks that update a variable when entering or leaving work Next, we are going to create a similar solution for work, but instead of using JSON and a single webhook to catch both enter and exit calls, we'll create two separate webhooks. One will catch a FORM POST, and the other will catch a GET. Under most circumstances, you wouldn't need to do this, but for the purposes of this example we wanted to show you how to do both of these. Using the first example as a starting point, we'll first create the Indigo elements. 1. Create a "work" variable. 1. Create a webhook trigger we'll call *`work-form-post`* which will have a webhook ID of *`work-form`* and which uses *`POST`* and *`HTTP`* Form for method and processing respectively. For the action we will configure another insert event data action for your new *`work`* variable, but this time you'll use the following path specifier: *`data.trigger[0]`*. Now, you're probably wondering why the *`[0]`* is appended to the end: this is because FORM POST (as well as GET query) args are always a list because you can have multiple duplicate field names with different values in those methods. But we know that Locative only sends a single value so we just get the one at the 0 index (the first one, Python lists are 0-based, so the first element is 0, second is 1, etc.) 1. Just like above we'll create a webhook trigger we'll call *`work-get`* which will have a webhook ID of *`work-form`* and which uses *`GET`* for the method. Configure an identical action as above to insert the value into the variable you created in step 1. Next, in Locative, create a location just as before (except for your work). For this one we'll have two different URLs and methods defined for the Arrival and Departure webhooks. For Arrival, you'll want to set it to POST and use the following URL (with appropriate substitutions): ```text https://REFLECTORNAMEHERE.indigodomo.net/webhook/work-form?api-key=APIKEYHERE ``` For Departure, select GET and use the following URL (with appropriate substitutions): ```text https://REFLECTORNAMEHERE.indigodomo.net/webhook/work-get?api-key=APIKEYHERE ``` Select your FORM POST custom design for the custom request field as illustrated above. Test and debug your solution to make sure that your work location is updating correctly when entered/exited. #### Summary { #update-a-variable-when-entering-leaving-work-summary } For the work location, we used two different webhooks (a GET and a FORM POST) to catch data from Locative. Both webhooks update the same variable with exit/enter just like the first example. We artificially created these scenarios to illustrate how GET and FORM POST works in case you have a data provider that uses those methods. ### Advanced Scripting Example As you can see from the two examples above, Indigo is quite flexible when it comes to configuring webhooks and using the data provided by them. But there are other ways to customize your solution to fit your needs. Let's say what you really want Indigo to know is when you (for this example, we'll call you Joe) are at *`home`*, at *`work`*, or some other place (which we'll just call *`away`*). For this example, we're just going to have a single webhook which will catch all the webhooks and have a script that uses the data from the webhook to determine where you are. We'll then change the value of a variable to *`home`*, *`work`*, or *`away`* First, let's create the Indigo webhook. Here's the trigger definition: ![](../images/locative-joe-trigger.jpeg){ width=500 } It's a straight-forward JSON POST webhook named *`joe-location`*. The action is where the real work is. Select the Server Actions->Script and File Actions->Execute Script, with the following script: ```python # Get the location ID and the triggering event from the # locative webhook data location_id = event_data["data"]["id"] location_trigger = event_data["data"]["trigger"] # The default value will be "away" joe_location = "away" if location_id == "joe-home": if location_trigger == "enter": # The location was home and enter, so joe is # home joe_location = "home" else: # We only have 2 locations that execute this webhook # so it must be the work location if location_trigger == "enter": # The location was joe-work and enter, so joe is # at work joe_location = "work" # Update the variable with one of ["home", "work", "away"] # Substitute the ID of the variable that will hold your location indigo.variable.updateValue(IDOFVARIABLE, value=joe_location) ``` In Locative, create two different locations, one with an id of "joe-home" and another with an id of "joe-work". For each, configure all the webhooks as POST JSON using the following url: ```text https://REFLECTORNAMEHERE.indigodomo.net/webhook/joe-location?api-key=APIKEYHERE ``` All webhooks for both locations will go to the same trigger in Indigo and the script will process it based on the data present in the webhook JSON. Give it a try and debug as necessary. #### Summary { #substitute-the-id-of-the-variable-that-will-hold-your-location-summary } We created a single Indigo webhook that catches a JSON payload and in an embedded script it uses data from the payloads to determine where Joe is. We also created two locations in Locative that represent home and work, and configured the webhooks for both locations to hit our single webhook trigger and pass through their data. ## Synology NAS Webhook Example Synology DSM supports webhook events which work great with Indigo's Webhook Events. This document describes how to set up a Synology DSM webhook event (sender) and Indigo webhook event (receiver). It assumes a certain level of familiarity with both Synology DSM Notifications, Indigo Webhook events and Indigo authentication. There are many configurations that are possible and this article describes a very basic example. ### Setting Up Indigo Webhooks 1. In Indigo, create a new Trigger. 1. Under Type, select Web Server Event. 1. Under Event, select Webhook. 1. Under the Configure Webhook dialog, copy the Webhook ID and paste it somewhere safe. If you want, you can change the ID to something else -- it must be unique. 1. Under Webhook Method, ensure it's set to POST. 1. Under Processing, ensure it's set to JSON. 1. Set up the Action for your Indigo Trigger (send an email, write to the event log, etc.) 1. Save your Trigger. ![Webhook Trigger](../images/synology_nas_webhook_trigger.png){ width=600 } ![Webhook Trigger Config](../images/synology_nas_webhook_config_post.png){ width=600 } ![Webhook Action](../images/synology_nas_webhook_action.png){ width=600 } ### Setting Up Synology DSM Webhooks 1. Log into the Synology NAS administrative dashboard. 1. Open the Control Panel app. 1. Select Notification. 1. Select Webhooks 1. Click the "Add" button to add a new webhook. 1. Choose a Notification Rule, enter a Provider Name, and Subject 1. Enter the configured webhook URL which should have a form similar to this: `https://MY_REFLECTOR_NAME.indigodomo.net/webhook/MY_INDIGO_WEBHOOK_ID?api-key=MY_INDIGO_API_KEYwhere you replace `MY_REFLECTOR_NAME`, `MY_INDIGO_WEBHOOK_ID`, and `MY_INDIGO_API_KEY with values from your Indigo setup. Use the webhook ID you got from the Indigo Webhook Trigger. For the API key, you can also use a local secret. 1. Click Next (or switch to the HTTP Request tab). 1. Adjust the request as needed -- can leave at defaults for your first webhook. 1. Save your webhook. ![Webhook Provider](../images/synology_nas_webhook_provider.png){ width=600 } ![Webhook Request](../images/synology_nas_webhook_request.png){ width=600 } ### Test Your Webhook 1. Select your new Synology web hook if it's not already selected. 1. Press the "Send Test Message" button. 1. Confirm the webhook worked (depends on the Action options you chose above.) An example event log success message: ```bash Trigger Synology Webhook Event Email+ sending email 'Synology Webhook Event' to 'me@me.com' using Email+ SMTP Server ``` And the notification! ![Webhook Email](../images/synology_nas_webhook_email.png){ width=600 } ### Customize Your Webhook Event Now that you've confirmed that your Indigo Webhook event is working properly, you can customize it to make it even more useful. Because we've selected a POST event, the Synology DSM webhook will pass DSM event data to Indigo. You can use that payload to take different actions. When an Indigo POST web hook is triggered, it includes the payload that was POSTed. You can access this payload with a Python script. The payload is sent as JSON, so you can access the DSM event information by working with the `event_data` payload that's sent to the trigger. See the [Webhooks Page](webhooks.md) for more information. --- WebSocket API (https://docs.indigodomo.com/2025.2/api/websocket/) --- # WebSocket API The Indigo WebSocket API is a persistent connection, meaning that your app/integration will open the connection and keep it open until your app/integration quits. If you are looking for a transactional API, try our [HTTP API](http.md). WebSockets are bidirectional TCP connections, which clients can read from and write to – much like you can a socket or serial connection. This initial version of this API is meant to support functionality like that provided by the Indigo Touch and Domotics Pat clients and does not provide all the necessary bits to support a full configuration clients like the Mac Client. In other words, there are things that the Indigo client can do that are not supported in the WebSockets API at this time. There are 7 WebSocket feeds that are available: 1. `/v2/api/ws/device-feed` - WebSocket to do all communication about devices 1. `/v2/api/ws/variable-feed` - WebSocket to do all communication about variables 1. `/v2/api/ws/action-feed` - WebSocket to do all communication about action groups 1. `/v2/api/ws/schedule-feed` - WebSocket to do all communication about schedules 1. `/v2/api/ws/trigger-feed` - WebSocket to do all communication about triggers 1. `/v2/api/ws/page-feed` - WebSocket to do all communication about control pages 1. `/v2/api/ws/log-feed` - WebSocket to do all communication about logs Each feed (except log feed) sends the following server messages: - **add** (when a new Indigo object is added) - **patch** (when an Indigo object changes - you would apply the patch to an existing object) - **delete** (when the Indigo object is deleted) - **refresh** (when you request a fresh copy of a device or the entire list of devices) ## WebSocket Lifecycle The flow of how you will interact with any of the WebSockets above will generally be: 1. Setup: - Open the websocket connection, and - Send a refresh message to obtain all of the existing instances of that Indigo type in your database. 1. Routine operation (asynchronously processing for the lifetime of your app/integration): - Read incoming messages from the server (add/patch/delete/refresh) and handle those as appropriate, and - Write messages to the server to instruct the server to do something (commands like statusRequest, turnOn, or refresh). 1. Close the connection when your app/integration quits. The **log-feed** is different in that it will only send **add** messages with the appropriate log event object defined below. ## Authentication WebSocket API requests must be authenticated using an **API Key**. You can manage API Keys in the [Authorizations section of your Indigo Account](https://www.indigodomo.com/account/authorizations). Alternatively, you can create "[local secrets](../user/remote-access/web-server.md#authentication)" -- a special kind of API key that doesn't pass through the Indigo reflector -- or a combination of both types of keys. Using keys instead of your Indigo Server username/password has several advantages: you can, at any time, revoke an API key, and it will immediately cause anything using it to fail. This will not affect anything else using a username/password or another API key, so your server protections against intrusions are much more granular. Also, if someone does manage to get your API Key, they can control devices, but they cannot modify your database (add/delete devices, etc.) - that is reserved for Indigo clients using the username/password. The best way to use an API Key is to include it in an **Authorization** header on your HTTP request. If you are using a system which does not allow you to set headers for your HTTP request, you can include the API Key as a query argument with the URL: `*wss://YOUR-REFLECTOR-NAME.indigodomo.net/v2/api/ws/device-feed?api-key=YOUR-API-KEY*` Note the protocol: `wss`. WebSockets actually begin their life as HTTP/HTTPS connections, which the WebSocket client then requests the server to upgrade to a WebSocket. In this respect, you can think of WSS as HTTPS (protected) and WS as HTTP (unprotected). When using WSS, such as when you are using your Indigo Reflector, then your API Key (in both instances) is protected by the TLS security used by the HTTPS protocol. You may use the API locally (or thorough your own router port forwarding), but those connections will be WS and **will not be secure**. It is highly recommended that you always use your Indigo Reflector because it provides a very simple and **secure** solution for accessing your system. !!! warning In order to use API Key authentication, you MUST have enabled *`Enable OAuth and API Key authentication`* in the Indigo ["Start Local Server"](../user/getting-started/installation.md#starting-indigo-server) dialog box. Don't share your API keys with anyone who is not authorized to use them — especially in posts to the Indigo user forums. Note that disabling this feature disables both API Keys and secrets. ## Examples The WebSocket and HTTP APIs are designed to be familiar to those that have been using the legacy RESTful API. However, anyone with knowledge of Python, JavaScript or similar languages should be able to pick up the structure of the new APIs very quickly. To help those making the transition as well as those learning to use API calls for the first time, we've laid out several examples to show how API calls are made, as well as all the current API hooks available. Here's a simple Python script to open the device-feed and print out any messages it receives (you'll need to `pip3 install websockets` before running it if it isn't already installed - Indigo installs it on the Indigo Server Mac so you won't need to install there). ### Python Receiver Example This example opens a websocket connection and prints all messages received to the console. It runs until you stop it. ```python import asyncio import websockets import json API_KEY = 'YOUR-API-KEY' async def receiver(): try: headers = {"Authorization": f"Bearer {API_KEY}"} # Note the update to Python 3.13 changed the `websockets.connect()` attribute `additional_headers` to `additional_headers`. async with websockets.connect("ws://localhost:8176/v2/api/ws/device-feed", additional_headers=headers) as websocket: while True: message = await websocket.recv() print(json.dumps(json.loads(message), indent=2)) except Exception as exc: print(f"Exception:\n{exc}") asyncio.run(receiver()) ``` ### Python Receiver/Sender Example This example opens a websocket connection and shows how messages can be received and sent from the same websocket connection. It runs until the loop counters run out. ```python import asyncio import json from websockets import connect API_KEY = 'YOUR_API_KEY' # YOUR API KEY HERE DEVICE_ID = 12345678 # YOUR DEVICE ID HERE HEADERS = {"Authorization": f"Bearer {API_KEY}"} URI = "ws://127.0.0.1:8176/v2/api/ws/device-feed" # wss:// if using the reflector async def receiver(ws): try: print("Starting receiver task") # Ask for a refresh of device with ID 12345678 refresh_message = { "id": "initial-device-refresh", "message": "refresh", "objectType": "indigo.Device", "objectId": DEVICE_ID } await ws.send(json.dumps(refresh_message)) counter = 0 device = {} while counter < 20: # just keep looping waiting for a message to come log_string = "ignoring message" message_json = await ws.recv() message = json.loads(message_json) message_type = message['message'] if message_type == "refresh": # This is the response to the refresh message we sent above. It gets us a full copy of the device. device = message["objectDict"] log_string = f"receiver: device refresh message: '{device.get('name', 'unknown')}'" elif message_type == "patch": # There's been a change, so confirm it's the device we want then log the change if message["objectId"] == device["id"]: log_string = f"'receiver: device patch message: {device.get('name', 'unknown')}' update: \n{message_json}" print(f"receiver: loop count: {counter}") print(f"receiver: {log_string}") counter += 1 except Exception as exc: print(f"Exception:\n{exc}") async def sender(ws, count): try: print("Starting sender task") message = { "id": "initial-device-refresh", "message": "refresh", "objectType": "indigo.Device", "objectId": DEVICE_ID } for count in range(10): print(f"sender: loop count: {count}") msg = json.dumps(message) print(f"sender: sending message: {msg}") await ws.send(json.dumps(msg)) await asyncio.sleep(5) except Exception as exc: print(f"Exception:\n{exc}") async def main(): try: # Note the update to Python 3.13 changed the `websockets.connect()` attribute `additional_headers` to `additional_headers`. async with connect(URI, additional_headers=HEADERS) as websocket: await asyncio.gather(receiver(websocket), sender(websocket, 10)) except Exception as exc: print(f"Exception:\n{exc}") asyncio.run(main()) ``` ### JavaScript Example Here's a simple Node.js JavaScript to open the device-feed and print out any messages it receives (you'll need to `npm install websocket` before running it). ```javascript const APIKEY = "YOUR-API-KEY"; const W3CWebSocket = require("websocket").w3cwebsocket; console.log("Creating websocket"); const client = new W3CWebSocket( `ws://localhost:8176/v2/api/ws/device-feed?api-key=${APIKEY}` ); client.onmessage = function (message) { console.log(message.data); }; client.onerror = function (err) { console.log(`Error: ${JSON.stringify(err)}`); }; ``` ## Device Feed This is the WebSocket you'll use for all device-related communication. You will receive the following messages from the Indigo server during the lifetime of the WebSocket. Here are the URLs you will use to connect to this feed. ```bash ws://localhost:8176/v2/api/ws/device-feed wss://YOUR-REFLECTOR-NAME.indigodomo.net/v2/api/ws/device-feed ``` ### Device messages from the server The following are examples of all the device messages that you will receive from the server. #### add device message ```json { "message": "add", "objectType": "indigo.Device", "objectDict": { "class": "indigo.DimmerDevice", "address": "3B.04.7A", "batteryLevel": null, "blueLevel": null, "brightness": 100, "buttonConfiguredCount": 0, "buttonGroupCount": 1, "configured": true, "defaultBrightness": 100, "description": "- sample device -", "deviceTypeId": "", "displayStateId": "brightnessLevel", "displayStateImageSel": "indigo.kStateImageSel.DimmerOn", "displayStateValRaw": 100, "displayStateValUi": "100", "enabled": true, "energyAccumBaseTime": null, "energyAccumTimeDelta": null, "energyAccumTotal": null, "energyCurLevel": null, "errorState": "", "folderId": 1552926800, "globalProps": { "com.indigodomo.indigoserver": {}, "emptyDict": {} }, "greenLevel": null, "id": 1508839119, "lastChanged": "2023-02-16T15:43:53", "lastSuccessfulComm": "2023-02-16T15:43:53", "ledStates": [], "model": "LampLinc (dual-band)", "name": "Insteon Dimmer", "onBrightensToDefaultToggle": true, "onBrightensToLast": false, "onState": true, "ownerProps": {}, "pluginId": "", "pluginProps": {}, "protocol": "indigo.kProtocol.Insteon", "redLevel": null, "remoteDisplay": false, "sharedProps": {}, "states": { "brightnessLevel": 100, "onOffState": true }, "subModel": "Plug-In", "subType": "Plug-In", "supportsAllLightsOnOff": true, "supportsAllOff": true, "supportsColor": false, "supportsOnState": true, "supportsRGB": false, "supportsRGBandWhiteSimultaneously": false, "supportsStatusRequest": true, "supportsTwoWhiteLevels": false, "supportsTwoWhiteLevelsSimultaneously": false, "supportsWhite": false, "supportsWhiteTemperature": false, "version": 67, "whiteLevel": null, "whiteLevel2": null, "whiteTemperature": null } } ``` When a new device is added to the Indigo Server after you've opened the connection, you will receive this message. It contains a [device object](messages.md#device-objects) that you will want to add to your device list (since you'll want to patch it as it changes over time - see the next section). You'll also receive this message when a device's remote display property is changed from False to True. #### update device message ```json { "message": "patch", "objectType": "indigo.Device", "objectId": 1508839119, "patch": [ [ "change", "brightness", [100, 0] ], [ "change", "displayStateImageSel", ["indigo.kStateImageSel.DimmerOn", "indigo.kStateImageSel.DimmerOff"] ], [ "change", "displayStateValRaw", [100, 0] ], [ "change", "displayStateValUi", ["100", "0"] ], [ "change", "lastChanged", ["2023-02-17T16:29:56", "2023-02-17T16:30:54"] ], [ "change", "lastSuccessfulComm", ["2023-02-17T16:29:56", "2023-02-17T16:30:54"] ], [ "change", "onState", [true, false] ], [ "change", "states.brightnessLevel", [100, 0] ], [ "change", "states.onOffState", [true, false] ] ] } ``` Patch objects are created via the [dictdiffer python module](https://dictdiffer.readthedocs.io/en/latest/), by comparing the device dictionary (`dict(some_device)`) for the old device with the one for the new dictionary as they are received in the `device_updated()` plugin method call. We've implemented a JavaScript library, [dictdiffer-js](https://github.com/IndigoDomotics/dictdiffer-js), to patch JavaScript objects given the patch object created by the dictdiffer Python library. You can use it in your projects if you like. #### device refresh messages When you send a command that asks to refresh the entire list of devices, you'll receive the following message from the server: ```json { "id": "optional-custom-user-message", "message": "refresh", "objectType": "indigo.Device", "list": [] } ``` And if you requested just a single device refresh, you will receive the following message from the server: ```json { "id": "optional-custom-user-message", "message": "refresh", "objectType": "indigo.Device", "objectDict": {} } ``` !!! note That the first message includes a list of objectDict elements, and the second includes a single objectDict element. #### delete device message ```json { "message": "delete", "objectType": "indigo.Device", "objectId": 123456789 } ``` This is the simplest of the messages, as it just contains the Indigo ID of the device to delete from your collection. You'll receive this message when a device is deleted from the Indigo server and when a device's remote display property is set from True to False. ### Device messages to the server You have a variety of messages you can send to the server. #### device refresh requests To refresh either the full device list or a single device from the server, send the following message. ```json { "id": "optional-custom-user-message", "message": "refresh", // Specify the object type "objectType": "indigo.Device", "objectId": 123456789 } ``` If you want the entire device list, simply omit the `objectId` key and the server will return the full list. The server will respond with the appropriate [device refresh message](#device-refresh-messages) shown above. #### device command messages To command devices to do something, you will be using the [Device Command Messages](#device-command-messages) described below. For instance, to toggle a lamp device, you would send the following message: ```json { "id": "optional-custom-user-message", "message": "indigo.device.toggle", "objectId": 123456789, } ``` You will receive a "patch" message as a result of this command. ## Variable Feed This is the WebSocket you'll use for all variable-related communication. You will receive the following messages from the Indigo server during the lifetime of the WebSocket. Here are the URLs you will use to connect to this feed. ```bash ws://localhost:8176/v2/api/ws/variable-feed wss://YOUR-REFLECTOR-NAME.indigodomo.net/v2/api/ws/variable-feed ``` ### Variable messages from the server The following is an example of the variable message that you will receive from the server. #### Example variable object (dictionary in Python) ```json { "class": "indigo.Variable", "description": "", "folderId": 0, "globalProps": { "com.indigodomo.indigoserver": {} }, "id": 345633244, "name": "house_status", "pluginProps": {}, "readOnly": false, "remoteDisplay": true, "sharedProps": {}, "value": "home" } ``` Here are some examples of the server messages that clients will receive on the variable feed. #### add variable message ```json { "message": "add", "objectType": "indigo.Variable", "objectDict": {} // Variable object as outlined above } ``` When a new variable is added to the Indigo Server after you've opened the connection, you will receive this message. It contains a [variable object](messages.md#variable-objects) that you will want to add to your variable list (since you'll want to patch it as it changes over time - see the next section). You'll also receive this message when a variable's remote display property is changed from False to True. #### update variable message ```json { "message": "patch", // we use a patch rather than send the entire updated device "objectType": "indigo.Variable", "patch": {} // A patch object - see the Object Patches below for details } ``` Variable patch objects are created via the [dictdiffer python module](https://dictdiffer.readthedocs.io/en/latest/) by comparing the variable dictionary (`dict(some_variable)`) for the old variable with the one for the new dictionary as they are received in the `variable_updated()` Plugin method call. #### delete variable message ```json { "message": "delete", "objectType": "indigo.Variable", "objectId": 123456789 } ``` This is the simplest of the messages, as it just contains the Indigo ID of the variable to delete from your collection. You'll receive this message when a variable is deleted from the Indigo server and when a variable's remote display property is set from True to False. ### Variable messages to the server There are a few messages you can send to the server. #### variable refresh messages ```json { "id": "optional-custom-user-message", "message": "refresh", "objectType": "indigo.Variable", "objectDict": {} // Variable object as outlined above } ``` #### Example refresh all variables message ```json { "id": "optional-custom-user-message", "message": "refresh", "objectType": "indigo.Variable", "list": [] // a list of Variable objects as outlined above } ``` ### variable command messages The `updateValue` command is currently the only command message you can send to the variable feed. It closely mirrors the Python-based [IOM command for updating variables](../scripting/iom-concepts.md). This was completely intentional to make learning one API a stepping stone to another. The HTTP API messages and the Websocket API messages are identical, and are very clearly a JSON-rendered version of the associated IOM command. #### updateValue ```json { // Note, values passed in the parameter dictionary must be strings. You can // pass in an empty string ("") to clear the variable value. "id": "optional-custom-user-message", "message": "indigo.variable.updateValue", "objectId": 123456789, // the variable id to update "parameters": { "value": "Some string value" } } ``` ## Action Group Feed This is the WebSocket you'll use for all action group-related communication. You will receive the following messages from the Indigo server during the lifetime of the WebSocket. Here are the URLs you will use to connect to this feed. ```bash ws://localhost:8176/v2/api/ws/action-feed wss://YOUR-REFLECTOR-NAME.indigodomo.net/v2/api/ws/action-feed ``` ### Action Group messages from the server The following are examples of all the action group messages that you will receive from the server. #### Example action group object (dictionary in Python) ```json { "class": "indigo.ActionGroup", "description": "", "folderId": 532526508, "globalProps": { "com.indigodomo.indigoserver": { "speakDelayTime": "5", "speakTextVariable": "speech_string" } }, "id": 94914463, "name": "Movie Night", "pluginProps": {}, "remoteDisplay": true, "sharedProps": { "speakDelayTime": "5", "speakTextVariable": "speech_string" } } ``` Here are some examples of the server messages that clients will receive on the action feed. #### add action group message ```json { "message": "add", "objectType": "indigo.ActionGroup", "objectDict": {} // ActionGroup object as outlined above } ``` When a new action group is added to the Indigo Server after you've opened the connection, you will receive this message. It contains an [action group object](messages.md#action-group-objects) object that you will want to add to your action group list (since you'll want to patch it as it changes over time - see the next section). You'll also receive this message when an action group's remote display property is changed from False to True. #### update action group message The update action group message is received when an action group has been updated on the Indigo server. It doesn't allow users to update action groups via the WebSocket API. ```json { "message": "patch", // we use a patch rather than send the entire updated device "objectType": "indigo.ActionGroup", "patch": {} // A patch object - see the Object Patches below for details } ``` Action group patch objects are created via the [dictdiffer python module](https://dictdiffer.readthedocs.io/en/latest/), by comparing the action dictionary (`dict(some_action_group)`) for the old action with the one for the new dictionary as they are received in the `action_group_updated()` Plugin method call. #### delete action group message ```json { "message": "delete", "objectType": "indigo.ActionGroup", "objectId": 123456789 } ``` This is the simplest of the messages, as it just contains the Indigo ID of the action group to delete from your collection. You'll receive this message when an action group is deleted from the Indigo server and when an action group's remote display property is set from True to False. ### Action Group messages to the server The following are examples of the action group messages that you can send to the server. #### action group refresh messages ```json { "id": "optional-custom-user-message", "message": "refresh", "objectType": "indigo.ActionGroup", "objectDict": {} // ActionGroup object as outlined above } ``` #### Example refresh all action groups message ```json { "id": "optional-custom-user-message", "message": "refresh", "objectType": "indigo.ActionGroup", "list": [] // a list of ActionGroup objects as outlined above } ``` ### action group command messages The `execute` command is currently the only command message you can send to the action group feed. It closely mirrors the Python-based [IOM command for executing action groups](../scripting/iom-concepts.md). This was completely intentional to make learning one API a stepping stone to another. The HTTP API messages and the Websocket API messages are identical, and are very clearly a JSON-rendered version of the associated IOM command. #### Execute ```json { "id": "optional-custom-user-message", "message": "indigo.actionGroup.execute", "objectId": 123456789 // the action group id to execute } ``` ## Control Page Feed This is the WebSocket you'll use for all control page-related communication. You will receive the following messages from the Indigo server during the lifetime of the WebSocket. The control page feed has no command messages you can send to the server; rather, its purpose is to use incoming messages to manage a list of the available control pages which (presumably) the user would select to open that page. Here are the URLs you will use to connect to this feed. ```bash ws://localhost:8176/v2/api/ws/page-feed wss://YOUR-REFLECTOR-NAME.indigodomo.net/v2/api/ws/page-feed ``` ### Control Page messages from the server The following are examples of the control page messages that you will receive from the server. #### Example variable object (dictionary in Python) { #control-page-messages-from-the-server-example-variable-object-dictionary-in-python } ```json { "class": "indigo.ControlPage", "backgroundImage": "", "description": "", "folderId": 0, "globalProps": {}, "hideTabBar": true, "id": 963336187, "name": "Weather Images", "pluginProps": {}, "remoteDisplay": true, "sharedProps": {} } ``` Here are some examples of the server messages that clients will receive on the control page feed. #### add control page message ```json { "message": "add", "objectType": "indigo.ControlPage", "objectDict": {} // ControlPage object as outlined above } ``` When a new control page is added to the Indigo Server after you've opened the connection, you will receive this message. It contains a control page that you will want to add to your control page list (since you'll want to patch it as it changes over time - see the next section). You'll also receive this message when a control page's remote display property is changed from False to True. #### update control page message The update control page message is received when a control page has been updated on the Indigo server. It doesn't allow users to update control pages via the WebSocket API. ```json { "message": "patch", // we use a patch rather than send the entire updated control page "objectType": "indigo.ControlPage", "patch": {} // A patch object - see the Object Patches below for details } ``` Page patch objects are created via the [dictdiffer python module](https://dictdiffer.readthedocs.io/en/latest/), by comparing the device dictionary (`dict(some_page)`) for the old page with the one for the new dictionary as they are received in the `control_page_updated()` Plugin method call. #### delete control page message ```json { "message": "delete", "objectType": "indigo.ControlPage", "objectId": 123456789 } ``` This is the simplest of the messages, as it just contains the Indigo ID of the control page to delete from your collection. You'll receive this message when a control page is deleted from the Indigo server and when a control page's remote display property is set from True to False. ## Indigo Object Folders Each object type above may have folders. Since those folders are specific to the type, you will use the same feed (i.e. device-feed, etc.) to get all the available folders for that type. This is an example of a folder object. It is the same for any folder in any feed - children is the generic name for the indigo objects contained in the folder (device, variable, etc.) ```python { "id": 617272302, "class": "indigo.Folder", "name": "My Device Folder", "remoteDisplay": true } ``` ### refresh folder server message To get the full folder list from the server, send the following message. ```python { "message": "refresh", "id": "optional-custom-user-message", "objectType": "indigo.Device.Folder", } ``` You will then receive the following message with all the folders for that Indigo object type. ```python { "message": "refresh", "id": "optional-custom-user-message", "objectType": "indigo.Device.Folder", // or indigo.Variable.Folder, etc "list": [] // A list of folder objects defined above } ``` As of this release, this is the only way to get the current state of folders (there are no add/update/delete messages). If you think you need to refresh your folder list, then send the refresh message. Since folders are generally static, there really isn't much need to continually get the folder list. ## Log Feed Use this feed to catch all log messages as they happen in the Indigo Server. When you first open the log-feed WebSocket, you will receive the last 25 log messages from the server ***in chronological order***. After that, the messages come through the socket as they are generated (chronological order). See the [Log Messages](messages.md#log-messages) section below for a description of a log message object. Here are the URLs you will use to connect to this feed: ```text ws://localhost:8176/v2/api/ws/log-feed wss://YOUR-REFLECTOR-NAME.indigodomo.net/v2/api/ws/log-feed ``` The only message you will receive on the log feed will be an add message for every new log entry: ```json { "message": "add", "objectType": "indigo.LogEvent", "objectDict": { "message": "Stopping plugin \"Web Server 2025.1.0\" (pid 1020)", "timeStamp": "2022-12-01T12:03:27.759000", "typeStr": "Application", "typeVal": 0 "objectType": "indigo.LogEvent" } } ``` You can also send messages to the log feed from your websocket client with the following payload: ```json { "id": "optional-custom-user-message", "messageText": "My log message.", // required "message": "indigo.server.log", // required } ``` --- Plugin Development (https://docs.indigodomo.com/2025.2/plugin-dev/) --- # Plugin Development Plugins integrate new devices, triggers, actions, and services natively into Indigo — distributed as a single `.indigoPlugin` bundle users can double-click to install. Plugins are written in Python against the same [Indigo Object Model](../scripting/iom-concepts.md) used for scripting, plus a declarative XML layer for configuration UI. ## Where to start Read the [Plugin Developer's Guide](guide.md) first — bundle structure, `Info.plist`, and how the Indigo Plugin Host runs your code. Then grab the [Indigo SDK](https://github.com/IndigoDomotics/IndigoSDK/releases) and explore the [example plugins](sdk-examples.md); modifying an example that's close to your goal is the fastest path to a working plugin. The [Building a Plugin tutorial](tutorials/building.md) walks through adding device types, actions, and event handlers. If you haven't scripted Indigo before, skim the [Scripting Tutorial](../scripting/tutorial.md) first — plugin callbacks are ordinary IOM Python. ## Reference - [plugin.py Method Reference](reference/plugin-py/index.md) — `PluginBase` lifecycle methods and every callback hook. - [Plugin XML Reference](reference/xml/index.md) — `PluginConfig.xml`, `Devices.xml`, `Events.xml`, `Actions.xml`, `MenuItems.xml`, and ConfigUI fields. - [IOM Reference](../scripting/index.md#iom-reference) — the object model shared with scripting. - [Python Packages](../scripting/guides/python-packages.md) — what's bundled and how to vendor dependencies. ## Distributing your plugin Submit finished plugins to the [Indigo Plugin Store](https://www.indigodomo.com/pluginstore/) from [your Indigo account](https://www.indigodomo.com/account/plugins). --- Developer's Guide (https://docs.indigodomo.com/2025.2/plugin-dev/guide/) --- # Indigo Plugin Developer's Guide v2.0 { #indigo-plugin-developer-s-guide-v10 } !!! abstract "In this guide" Introduction to the Indigo plugin bundle format: the required `Info.plist` keys, folder structure (`Server Plugin`, `Resources`, `Packages`, `Menu Items`), and how the Indigo Plugin Host (IPH) sandboxes and manages each plugin process. Start here before reading the XML Reference or the implementation reference for `plugin.py`. ## Indigo Plugins and APIs Indigo has a long history of extensibility - AppleScript Attachment scripts were available from the start (and deprecated in Indigo 7.4). Later, the Indigo Web Server (IWS) was added along with the IWS plugin and the ability to add custom images. With Indigo 5.0, we added a new server plugin API that allows 3rd party developers to more natively add devices, triggers, and actions to Indigo. This server API allows users and 3rd party vendors to implement their own functionality in Python with full access to all the objects and events that Indigo understands (referred to as the [Indigo Object Model](../scripting/iom-concepts.md), or **IOM**). Someone with sufficient skills can implement support for any kind of device and have them integrated into the Indigo UI as first-class citizens. To deliver this additional functionality, we created the Indigo plugin bundle. Let’s start by first looking at the Application Support folder structure which the Indigo installer creates. A note about version numbers: We increment the API version number when the API is revised. The major number (X.0) is incremented when we do something that will break backwards compatibility. The minor number (1.X) is incremented when we add new features. See the API version chart to see which API versions were released in which Indigo version. If any of the API tables in the documentation don't have a version number you can assume that the feature is available in API version 1.0 and later. [Back to Top](#indigo-plugin-developer-s-guide-v10) ## Indigo Support Folder Structure Indigo's folder structure looks like this: ![Folder Structure Image](../images/folder_structure.png) in this location: `/Library/Application Support/Perceptive Automation/Indigo [VERSION])` ] You'll notice these two folders in particular: *`Plugins`* and `*Plugins (Disabled)*`. These two folders are where the plugin bundles are stored (see the next section for details). Plugins that are enabled from the UI are located in the *`Plugins`* folder and when a user disables a plugin it’s moved to the `*Plugins (Disabled)*` folder. [Back to Top](#indigo-plugin-developer-s-guide-v10) ## The Indigo Plugin Bundle 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 its functions: ![Bundle Layout Image](../images/bundle_layout.png) 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. Creating a bundle is really easy: just create a folder in the Finder and end the name with *`.indigoPlugin`*. The Finder will prompt you about adding the extension *`.indigoPlugin`*. Click “Add”, and now your folder appears as a file. To get to the contents, right click on it and select `Show Package Contents` and it will open a separate window that is, in fact, just a new Finder window just like any other. In this window, you can create the folder structure above to have the elements that your plugin will need. Let’s go through each folder/file and discuss what it does. First, though, you can see that there is only one top-level folder in the bundle - *`Contents`*. All other files/folders are inside that folder. This is the standard macOS bundle construction, so we decided to follow the pattern. So, why did we go to the trouble of using the bundle format when there's just a file and a couple of folders? Because in future versions, we're going to add capabilities to the plugin bundle. ### The Info.plist File There’s only one file that’s directly inside the *`Contents`* folder, and it’s required (read very important). The *`Info.plist`* file is a standard XML property list file that contains several important key/value pairs. The keys in this file will help Indigo understand what functionality your plugin provides, what it’s name and version number are, etc. Editing a plist file isn’t difficult since it's just a text XML file that looks like this: ```xml PluginVersion 1.2.3 ServerApiVersion 2.0 CFBundleDisplayName Rachio Sprinklers CFBundleIdentifier com.yourorgidentifier.yourpluginidentifier CFBundleVersion 1.0.1 CFBundleURLTypes CFBundleURLName https://somehost.com/path/to/help/ ``` Here is what they keys are for: - *`PluginVersion`* (Plugin version) - this is the version number for your plugin - it’s shown to the user in the UI and will help you when supporting your plugin users. This key is required and should only contain numerical characters and periods (0-9 and .). For example, "1.0.5.2" is valid, but "1.0.5b2" is not. This is very important as it will help Indigo determine what do to when a user double-clicks a plugin to install it. If the version number is a higher version, Indigo will notify the user that the plugin will be installed and enabled. If the version number is lower than an already-installed version, Indigo will prompt the user to confirm that they want to downgrade the plugin. The version number is also used in version checking, and may eventually be used for automatic updates. - *`ServerApiVersion`* (Server API version) - this value refers to the minimum server API version your plugin requires. In other words, if your plugin requires server API version 3.0, users must be running Indigo 2022.1.0 or later (the first Indigo version that supports server API version 3.0). Otherwise, Indigo will not allow the plugin to be installed. In rare circumstances, a new server API version may deprecate prior functions, so it's best to review the [API Version Chart](https://www.indigodomo.com/indigo/api_version_chart.html) as new API versions are released. - *`CFBundleDisplayName`* (Bundle display name) - this is a standard macOS key, and its value represents the name of your plugin. It’s used in a bunch of places in the UI, so make sure that it appropriately identifies your plugin. This key is required. - *`CFBundleName`* (Bundle name) - this is another standard macOS key, and it's a name that should be less than 16 characters long and be suitable for displaying in menu items with various strings appended (i.e. "ShortName Device Controls"). If it's not provided we'll use the bundle display name instead. - *`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. - *`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. ### Menu Items Folder You can drop Python scripts into this folder, and they will show up in your plugin's sub-menu on the new "Plugins" menu. When the user selects the menu item, the script is executed. This is a really simple way of giving your plugin some visible UI. ### Resources Folder This folder is meant to contain any assets (like images, templates, etc.) that your plugin might need. If there is an `icon.png` file in here, it will be displayed in the [Plugin Store](https://www.indigodomo.com/pluginstore/) once you submit the plugin to the [Indigo Plugin Store](https://www.indigodomo.com/pluginstore/). Note that the code that loads [config UI XML templates](https://www.indigodomo.com/indigo/api_release_notes/1.4/) assumes that the `Server Plugin` folder is the root, so those template files should always be in the `Server Plugin` tree (i.e., `Server Plugin/Templates/my_template.xml`). If this folder contains any of the following sub-folders, that content will be made available directly from the Indigo Web Server: | Folder | Authentication | Description | |----------|--------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `images` | IWS Configured | Any images in this folder or any sub-folders will be delivered if the user is authenticated via whatever authentication methods are enabled for IWS (digest, basic, api key). | | `public` | None (open to all) | Any files in this folder or any sub-folders will be delivered to anyone without any authentication. | | `static` | IWS Configured | Any files in this folder or any sub-folders will be delivered if the user is authenticated via whatever authentication methods are enabled for IWS (digest, basic, api key). | | `videos` | IWS Configured | Any files in this folder or any sub-folders will be delivered if the user is authenticated via whatever authentication methods are enabled for IWS (digest, basic, api key). | The URL for those files will be constructed using the plugin's id followed by the path. Here are some examples: - https://yourreflector.indigodomo.net/com.your.pluginid/static/html/something.html - http://localhost:PORT/com.your.pluginid/images/an_image.png - http://YourServer.local:PORT/com.your.pluginid/public/open_to_all.txt ### Packages Folder This folder is optional, but it's useful when your users will need to install additional Python packages in order to use your plugin. Any library installs should be directed to the *`../Contents/Packages/`* folder. Note that this may be required in future versions of Indigo. See the [Python Packages and Indigo](../scripting/guides/python-packages.md) page for more information. ## Indigo Server Plugins The most comprehensive way to extend Indigo is by implementing a Server Plugin. This mechanism allows you to add native components such as device types, events, actions, and menu items. Because we didn’t want to make the server plugin mechanism dependent on any single OS architecture, we decided to implement them in Python and have the description and user interface for the plugins’ components described in HTML and XML files respectively. We chose Python for several reasons: - it’s object-oriented nature fits well with the IndigoServer’s representations of various objects - it is easily interfaced with C++ - which is what the server is written in - it’s cross-platform so there’s a lot of documentation and expertise out there (many hardware makers supply a Python interface to their hardware) - it’s easy to learn (really - we promise) Likewise, we chose XML because it’s very readable and universally understood and supported. We will not discuss XML in general in this document, but we believe that even those developers that aren’t familiar with XML will be able to quickly grasp the concepts since HTML is structurally similar. If you find understanding XML challenging, there are plenty of resources both on the web and in print that can help you get up to speed. [Back to Server Plugins](#indigo-server-plugins) ### Building a Server Plugin vs Scripting IOM The IOM is used for two similar purposes: scripting Indigo and building Server Plugins. They aren’t mutually exclusive, but they serve different needs. For instance, you may just want to write an embedded Python script action that just does some specific things vs building a full Server Plugin. Likewise, you may be interested in building a Server Plugin that doesn’t actually create any new device types, but simply adds events, actions, and menus to Indigo. [Back to Server Plugins](#indigo-server-plugins) ### Indigo Plugin Host Before we get to the specifics, let’s describe the process by which your plugin will get executed. Each Server Plugin will be launched in a special application called the Indigo Plugin Host (IPH). Each plugin will have its own instance of an IPH as well, so one plugin isn’t likely to bring down another or the IndigoServer. The IPH communicates with the IndigoServer through the XML interface the IndigoServer provides. But, fear not, the IPH hides all this complexity from you. It creates and manages C++ objects (and bridges them to native Python objects) that represent the Indigo Object Model (IOM), deals with communication with the IndigoServer, and makes sure that the IOM is kept in sync with the IndigoServer. More specifically, the IOM is presented to your plugin as a python module called *`indigo`*, that all plugins automatically import. Every object that represents an Indigo object and every command that you use to communicate with Indigo is done through the *`indigo`* module. For example, to write something to the Indigo Log, you would do this: `indigo.server.log(“Write this to the event log”)` To have the server speak a text message using your Mac's built-in speech synthesizer: `indigo.server.speak("Message to speak")` We describe the IOM in detail in the [IOM Reference Guide](../scripting/iom-concepts.md). The IndigoServer will manage the IPH for your plugin - starting it at IndigoServer startup or when the user enables your plugin, shutting it down at IndigoServer shutdown time or when the user disables your plugin. You never need to worry about process management or what happens when your plugin fails. IndigoServer will attempt to restart a failed plugin and warn the user when it can’t. ### Plugin Failure Handling For robustness and performance, plugins are executed inside their own process sandbox by a special Indigo application wrapper called the Indigo Plugin Host (see above). Runtime errors or crashes that occur within a plugin are handled differently based on when and where they occur: - **Plugin Fatal**: Errors that occur because of invalid XML or a failure inside the plugin initialization code (**init** method) are considered fatal. They will log an error and cause the Indigo Server to temporarily suspend the plugin -- the plugin will remain enabled but won't be running. Once the errors are corrected, the plugin can be restarted by its Reload menu option. Because the plugin is still in the enabled state, it will also be restarted on the next Indigo Server restart. - **Plugin Auto-Restarted**: If a plugin successfully initializes but then experiences a runtime crash (or is terminated via a kill signal not originating from an Indigo Server plugin disable request), then the Indigo Server will log an error and automatically restart the plugin after several seconds. This is only a crash fail-safe -- if your plugin has crashes then please fix the underlying problem (or forward the information to us if you think the problem is in the Indigo Plugin Host). If a plugin crashes multiple times over a short period of time, then the Indigo Server may slow or suppress its plugin auto-restart functionality requiring the plugin to be manually restarted via the Reload menu item. - **Concurrent Thread Auto-Restarted**: If a plugin defines runConcurrentThread() and an uncaught python exception is thrown within that method, then the Indigo Plugin Host will automatically (after several seconds) create a new thread and again call runConcurrentThread(). An error will also be logged with a call stack trace. In general, plugins should catch and handle common errors (hardware communication problems, out-of-bounds parameters, etc.) themselves inside runConcurrentThread(). The thread restart functionality implemented by the plugin Indigo Plugin Host is a fail-safe in case an error isn't handled, and should only be relied on for unexpected errors (if at all). - **Error Logged**: If an uncaught python exception is thrown out of any callback method (deviceStartComm, deviceStopComm, validatePrefsConfigUi, validateDeviceConfigUi, actionControlDimmerRelay, etc.) then an error will be logged with a call stack trace. In general, plugins should catch and handle common errors (hardware communication problems, out-of-bounds parameters, etc.) themselves. Errors logged for uncaught exceptions should be used by developers to implement their own (and more user-friendly) error handling. ### Server Plugin Folder The structure of the *`Server Plugin`* directory in the plugin bundle is something like this: ![Server Plugin Folder Image](../images/serverpluginfolder.png) 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. **Note**: you can’t edit generic XML documents like these with the Property List Editor application - it will only edit correctly formatted property lists (which are XML, but specially formatted). You’ll need to use a generic text editor such as TextMate, TextWrangler, BBEdit or Xcode. ### Indigo Plugin XML Conventions The Indigo plugin XML is formulated with a few rules that will help you navigate it’s constituent parts. Elements, such as *`Field`*, *`Action`*, *`Device`*, etc., will always start with a capital letter. Attributes, such as *`id`*, *`type`*, *`defaultValue`*, etc., will always start with a lowerCase letter. Both elements and attributes will be camel case - that is, aside from the initial character described above, each new word will be capitalized. !!! important Most of the major elements will have an *`id`* of some kind. It's extremely important that you follow these rules when creating the *`id`*. *`id`*'s: - can contain letters, numbers, and other ASCII characters, - cannot start with a number or punctuation character, - cannot start with the letters xml (XML, Xml, etc.), and - cannot contain spaces. ## Adding Your Plugins to the Plugin Store This section outlines the rules for submitting a plugin to the [Indigo Plugin Store](https://www.indigodomo.com/pluginstore/). While it may seem like a lot, it's mostly common sense. You can start the process from the [Plugin Contributions section of your Indigo Account](https://www.indigodomo.com/account/plugins). ### Creating Your Developer Account If you haven't already, you'll need to go to the [Plugin Contributions](https://www.indigodomo.com/account/plugins) page in your Indigo Account and add your developer information: ![Plugin Contributions Image](../images/plugin_contributions.png) Please read through the entire top section of that page as it contains important information. **Warning**: please **do not use** `com.indigodomo` or `com.perceptiveautomation` as your developer ID (or embed those in your developer ID). Those are our internal IDs and should not be used. ### Two Ways to Add a Plugin First and foremost: please *do not* attempt to add a plugin that you don't "own." If a developer of a plugin has abandoned it and you would like to take it over, please let us know and we'll take it from there (we've done this in the past and it's usually not a problem). Once we get permission we'll let you know and we can coordinate from there. If the developer is still active, let them know that you'd like to see it in the Plugin Store. There are two ways to add a plugin: ![Add Plugin Image](../images/add_plugin_1.png){ width=400 } The first and recommended option is via a GitHub Repository. You'll manage your plugin almost completely through GitHub. You'll create releases there, manage the various descriptions, etc. You will have the option to override some of that information in the plugin administration UI that we provide, but you'll likely find that managing everything through GitHub will be a better and more consistent experience. The other option is to directly manage everything through our UI. With this approach you'll create your own plugin releases and upload each new release via our plugin administration UI. You'll need to manage all descriptions through our UI. If you don't want to learn how to use GitHub then this is the option for you. ### General Rules First, some general rules that you need to follow regardless of which approach you take: - Version numbers in the `Info.plist` file must be of the form X.Y.Z (except the ServerApiVersion, which is X.Y). There are no beta/pre-release signifiers allowed. - No two releases can have the same version number (X.Y.Z). - The Plugin ID (CFBundleIdentifier) can't change once a plugin has been added. - The Plugin ID **must begin with your developer ID** specified above. This **should not** begin with `com.indigodomo` or `com.perceptiveautomation`. So, if you specified `com.johnsmith` as your developer ID, your plugin IDs would look like `com.johnsmith.myfirstplugin`. - If the `Contents/Resources/icon.png` file exists in your Plugin bundle it will be used as the icon in the store (the ![Plugin Image](../images/plugin_128x128.png){ width=24 } icon will be used if the file doesn't exist). See [Icons and Branding](#icons-and-branding) below for more details. - Plugins and releases can only be deleted by Indigo Domotics staff. Email us with details if you need a release removed. ### GitHub Specifics If you want to use GitHub (and there are many advantages to doing so), there are a few things to consider. We have a few (but not many) requirements in how your repo is constructed and how you do releases. There are also some optional things that you'll want to consider to make the experience for users even better. If you're new to GitHub but want to try it out, we've written [a simple How-To](https://wiki.indigodomo.com/doku.php?id=developer:github_setup_for_plugins) with lots of screen captures describing one way to set up your repository. #### GitHub Repo Layout This is the required GitHub Repo layout: ![GitHub Repo Layout Image](../images/github_repo_layout.png){ width=600 } At the top level of the repo should be your plugin folder and an optional `README` file. You may have other files in there as well, like the `LICENSE` file in the rachio-indigo repo above. The plugin is there for obvious reasons — it's the path into the source for your plugin. The `README` file's contents will automatically be used as the plugin's description and shown on the About tab on your Plugin's detail page: ![Rachio Plugin Store Detail Image](../images/rachio-pluginstore-detail.png){ width=600 } Markdown in the `README.md` file will be rendered correctly in the Plugin Store though you can also use a plain text file (`README.txt`). #### GitHub Releases When you are ready to publicly release a version of the plugin (either initially or a follow-up release), you should add a release in GitHub: ![GitHub Releases Image](../images/github-releases.png){ width=600 } We **do not** look at the "master branch" (or "tips") of the repo. Only published releases (not pre-releases) will be added to the Plugin Store's release list (see [Adding a GitHub Release](#adding-a-github-release) below for details). The Plugin Store currently doesn't support listing betas (pre-releases) but you can still create them and point your beta testers to them on GitHub (one of the advantages to using GitHub). ##### Release Requirements There are a few requirements for GitHub releases: - The GitHub release tag # must match PluginVersion in the `Info.plist` file, otherwise the release will be rejected when added to the Plugin Store. **Note**: we will be looking in the source code zipball from the release for the Info.plist, **not** the attached plugin. If you're getting the mismatch error, that's where you'll need to look to fix any mismatching version numbers. - The `README.md` or `README.txt` file must be at the top level of the archive for it to automatically be used. This is to avoid issues if your plugin includes other source projects that may also have their own README files. - Although optional, we strongly encourage you to add a zipped version of just the plugin folder (not the entire repo) to each release. Make sure that `indigoPlugin` is in the name and that it's a zip file (i.e. `MyPlugin.indigoPlugin.zip`) and make sure that there aren't any other zip files attached to the release. If you don't include an attached zipped plugin, the user will download a zipped copy of the repo itself, including the plugin, the README, etc. (basically everything in the repo). This may make it confusing for users to find and install the plugin. ### Adding a Plugin As we mentioned at the top, there are two ways to add a new plugin to the Plugin Store: by specifying a GitHub repo or by uploading an existing plugin: ![Add Plugin Image](../images/add_plugin_1.png){ width=400 } !!! note There is no automated way to convert between a GitHub-based plugin and a directly managed plugin, so please carefully consider which approach you want to use before initially adding the plugin (we recommend GitHub!). We can manually convert between the two, but it takes some time and effort. #### Adding a Plugin from GitHub To add a new plugin from a GitHub repo, just enter the GitHub user and repo names. As an example, for our Rachio Sprinkler repo, which is located here: `https://github.com/IndigoDomotics/rachio-indigo` We entered **IndigoDomotics** for the user and **rachio-indigo** as the repo. Note your repo must have at least one published release to be added to the Plugin Store. Click the **Next** button and you'll see the following page with the appropriate data pre-populated: ![Add Plugin From GitHub Image](../images/add_plugin_from_github.png){ width=600 } This form has a variety of fields (some read-only) that fall into two groups. The first group are fields about the plugin itself (not any specific release of the plugin): 1. **Plugin ID** — the ID from the `Info.plist` file (read-only). 2. **Name** — the name from the `Info.plist` file (read-only). 3. **Summary\*** — a very brief (200 character) summary of your plugin. It is pre-populated with the description for your GitHub repo but you may edit it as you want. It will never be overwritten from GitHub. 4. **Description\*** — the `README` file's contents from the repo will be inserted here. It may contain Markdown and you can edit it to make any changes you like. You will have the option of updating it from GitHub when you add new releases. 5. **Category** — the major category of your plugin. If you can't find one that seems suitable, then select **Miscellaneous**. This can be changed later. 6. **Help URL\*** — the help URL pre-populated from the `Info.plist`. You may override it and it will never be overwritten automatically. 7. **Documentation URL** — a separate and optional URL that can point to more comprehensive documentation. This is a good place to put the URL to the GitHub wiki for this repo (see the [Rachio Plugin's wiki page](https://github.com/IndigoDomotics/rachio-indigo/wiki) for an example). 8. **Plugin Icon** — if your plugin's bundle has an icon (`Contents/Resources/icon.png` file) then it will automatically be used if you leave this field blank. Alternately, you can add a PNG file here. 9. **GitHub User and Repo** — a read-only copy of what you entered on the first screen. And the next section is specific information about the release that you're adding: 1. **Release Version** — automatically retrieved from the releases section of GitHub. If you have more than one release in the repo, this will be the most recent (only repos that are not pre-release or draft are used). **Note**: the release version number in the GitHub release source zip's Info.plist **must match** the tag for the GitHub release. Otherwise, the release will not be added. This sanity check will help ensure that you don't end up with version mismatches and the associated problems that will result (failed update checking, failed upgrades, etc). 2. **Release Title** — also retrieved from GitHub, it's the short release title. 3. **What's New** — again, retrieved from GitHub, this is the full description of the release. 4. **Requirements** — an optional separate field that may contain Markdown. The intention of this field is to outline any specific requirements or steps needed for this release. GitHub doesn't have anywhere (other than the release description) to put this kind of information. You may, of course, just add it to the description (and therefore the What's New section) and leave this field blank. We won't show it if it's blank. 5. **Date Released** — the release date as specified in the GitHub release info. You can change it here if you like, using the YYYY-MM-DD format. Note if you change it to some date in the future it won't show up in the Plugin Store until that date. 6. **Server API** and **Bundle Versions** — retrieved from the `Info.plist` and just shown here for completeness. The Server API version is particularly important in that it determines what's shown as the minimum Indigo version required to use the plugin. Required fields are marked with an asterisk (\*). The majority of GitHub repos will have all the information needed for the required fields so it's only likely that you'll have to edit the Category field as a minimum (and if your plugin is an A/V or IR plugin you won't even have to do that!). Once you have reviewed all the form fields hit **Add Plugin**. You'll notice that on the right side of the screen we've provided a quick cheatsheet for Markdown syntax. This will be helpful when editing any fields that allow Markdown. #### Adding a Directly Managed Plugin To add a new plugin from an existing zipped plugin, just click the Choose File button and select the file. Note that Safari will automatically zip plugin folders before uploading (but your browser of choice may not). Click the **Next** button and you'll see the following page (with the appropriate data pre-populated): ![Add Plugin From File Image](../images/add_plugin_from_file.png){ width=600 } This form has a variety of fields (some read-only) that fall into two groups. The first group are fields about the plugin itself (not any specific release of the plugin): 1. **Plugin ID** — the ID from the `Info.plist` file (read-only). 2. **Name** — the name from the `Info.plist` file (read-only). 3. **Summary\*** — a very brief (200 character) summary of your plugin. 4. **Description\*** — a full description of what your plugin does. The field may contain Markdown. 5. **Category** — the major category of your plugin. If you can't find one that seems suitable, then select **Miscellaneous**. 6. **Help URL\*** — the help URL pre-populated from the `Info.plist`. You may override it and it will never be overwritten automatically. Commonly developers use this URL to point to a specific post on [their own sub-forum](https://forums.indigodomo.com/viewtopic.php?f=121&t=7526). 7. **Documentation URL** — a separate and optional URL that can point to more comprehensive documentation. So if you have a forum post that contains the documentation for the plugin for instance (and if it's different than the Help URL) then this is the place to put it. 8. **Plugin Icon** — if your plugin's bundle has an icon (at `Contents/Resources/icon.png`) then it will automatically be used if you leave this field blank. Alternately, you can add a PNG file here that will be used in the plugin's display in the Plugin Store. 9. **GitHub User and Repo** — disabled since this plugin will be managed directly through our pages. And the next section is specific information about the release that you're adding: 1. **Release Version** — automatically retrieved from the `Info.plist` file. 2. **Release Title\*** — a short summary (100 characters max) of this release of the plugin. 3. **What's New\*** — a full description of the release. This field may contain Markdown. 4. **Requirements** — an optional separate field that may contain Markdown. The intention of this field is to outline any specific requirements or steps needed for this release. You may, of course, just add it to the What's New section and leave this field blank. We won't show it if it's blank. 5. **Date Released** — the release date defaulting to today. You can change it here if you like, using the YYYY-MM-DD format. Note, if you change it to some date in the future it won't show up in the Plugin Store until that date. 6. **Server API** and **Bundle Versions** — retrieved from the `Info.plist` and just shown here for completeness. The Server API version is particularly important in that it determines what's shown as the minimum Indigo version required to use the plugin. Required fields are marked with an asterisk (\*). Once you've filled out the form hit **Add Plugin**. You'll notice that on the right side of the screen we've provided a quick cheatsheet for Markdown syntax. This will be helpful when editing any fields that allow Markdown. ### Editing a Plugin To edit a plugin, just go to your Indigo Account's [Plugin Contributions](https://www.indigodomo.com/account/plugins) page, find the release in the list and click the **Edit** button. You'll be able to change all editable fields there. ### Adding a Release When you have an update to a plugin, you just need to add a release to it. Go to the [Plugin Contributions](https://www.indigodomo.com/account/plugins) section of your Indigo Account, find the plugin, and click either the **Edit/Add Release** button for GitHub plugins or the **Add Release** button for directly managed plugins. #### Adding a GitHub Release Adding a GitHub release couldn't be easier. Once you've clicked the **Edit/Add Release** button, you'll see the plugin edit form where you can edit the plugin and release information. At the bottom of the form, you'll see the following checkboxes: ![GitHub Checkboxes Image](../images/github_checkboxes.png) The first checkbox is automatically checked and therefore when you Save the form it will add any new releases. The next checkbox will update the main plugin description with the `README` file from the most recent release, and the last checkbox will update the icon from the most recent release. Super simple! **Note**: the release version number in the GitHub release source zip's Info.plist **must match** the tag for the GitHub release. Otherwise, the release will not be added. This sanity check will help ensure that you don't end up with version mismatches and the associated problems that will result (failed update checking, failed upgrades, etc). #### Adding a Release for a Directly Managed Plugin Adding a new release this way isn't really very hard either, there are just a few fields you need to fill out: ![Add Plugin Release File Image](../images/add_plugin_release_file.png){ width=600 } 1. **Plugin ZIP File** — click the **Choose File** button and select the plugin folder. Note that Safari will automatically zip plugin folders before uploading (but your browser of choice may not). 2. **If there is an icon in the bundle, use it for the plugin's icon in the Plugin Store** — if checked then the icon in the bundle will replace the icon currently being used. 3. **Release Title\*** — a short summary (100 characters max) of this release of the plugin. 4. **What's New\*** — a full release description. This field may contain Markdown. 5. **Requirements** — an optional separate field that may contain Markdown. The intention of this field is to outline any specific requirements or steps needed for this release. You may, of course, just add it to the What's New section and leave this field blank. We won't show it if it's blank. 6. **Date Released** — the release date defaulting to today. You can change it here if you like, using the YYYY-MM-DD format. Note, if you change it to some date in the future it won't show up in the Plugin Store until that date. Required fields are marked with an asterisk (\*). Once you've filled out the form hit **Add Release**. You'll notice that on the right side of the screen we've provided a quick cheatsheet for Markdown syntax. This will be helpful when editing any fields that allow Markdown. ### Editing a Release To edit a release, just go to your plugin's detail page, switch to the Releases tab, expand the release you want to edit and click the **Edit** button. You'll be able to change all editable fields there. ### Icons and Branding The Plugin Store shows icons for each plugin. If you don't include one in the plugin bundle (`Contents/Resources/icon.png`) and you don't add one explicitly to the release (see [Adding a Release](#adding-a-release) for details) then the default Indigo Plugin icon ![Plugin Image](../images/plugin_128x128.png){ width=24 } will be shown. To make it easy for users browsing the Plugin Store to identify something they're looking for, it's quite helpful to include an icon that represents what your plugin does. For instance, if you are integrating a specific vendor's products (like the Rachio Sprinkler example above) then you'll probably want to use their icon. Here are some tips to help find an appropriate icon: - Many companies provide media links, including icons, that can be used for promotional purposes. - Some do it as part of the developer API documentation (often under "marketing" or some such). - You can often find logos on their social media accounts in the photograph sections. We believe that the vast majority of companies won't mind you using their logos to promote their products as long as it's clear that you have no direct affiliation to the company. If there is an issue it's easy for us to remove. Icon details: 256 × 256 is the optimal size. Images **must** be 128px high or they aren't going to look good (somewhat wider might work on some screen sizes). The icons must be `.png` files and the file name should always be `icon.png`. Note the macOS Preview app can be used to convert other image formats to `.png`. ### Linking to Indigo Docs If your plugin's documentation needs to link to Indigo's documentation, use the version-agnostic redirect URL so that your links always resolve to the current release of the docs: `https://www.indigodomo.com/docs/` For example, `https://www.indigodomo.com/docs/overview#sprinkler_controls` maps to the most recent documentation for the sprinkler controls. If you need to pin a link to a specific release, you can link directly to a versioned page at `https://docs.indigodomo.com//…` instead. ### A Few Final Thoughts That's pretty much all there is to managing a plugin in the Plugin Store. Here are a few final random thoughts and important reminders: - Putting your plugin on GitHub will encourage others to help you maintain the plugin. Wouldn't it be great if someone with a problem could actually fix it and submit a patch to you? - We highly recommend that you attach a zipped plugin (don't forget to delete the `.pyc` files) in your GitHub releases. When users click the Download Release button in the Plugin Store, it will download just the zipped plugin (not the entire repo). It'll be much clearer to users what they need to do next. - The GitHub wiki page is the best way to provide documentation (see the [Rachio](https://github.com/IndigoDomotics/rachio-indigo/wiki) and [Alexa-Hue Bridge](https://github.com/IndigoDomotics/alexa-hue-bridge/wiki) examples). The advantage to using the repo wiki is that others can help you maintain the documentation. - The most common way to provide support for a plugin (see *Help URL* above) is via our online forum. You can [request your own sub-forum](https://forums.indigodomo.com/viewtopic.php?f=121&t=7526) and create topics (or children sub-forums) for each plugin. - You may not delete plugins or releases — if you need to for some reason just let us know and we'll handle it. - If you want to give someone else edit access (only edit, not add privileges) to your plugins, let us know and we can do that. - If you need to change a plugin from Directly Managed to GitHub or vice versa, let us know. It's a manually intensive process so it may take us a few days to get everything converted but we will do it for you. --- SDK Example Plugins (https://docs.indigodomo.com/2025.2/plugin-dev/sdk-examples/) --- # SDK Example Plugins The [Indigo SDK](https://github.com/IndigoDomotics/IndigoSDK/releases) ships a set of fully working example plugins with complete XML and Python source. They're the recommended starting point for new plugins — find the example closest to what you're building and modify it. Several are also installed with Indigo itself (look in the Plugins menu under the disabled plugins list). ## Custom Device Plugin { #example-custom-device } Illustrates how a plugin can create custom Indigo devices which, like native devices, have states, triggers, actions, and UI. ## Relay/Dimmer Device Plugin { #example-relay-dimmer } Creates Indigo devices which inherit basic relay and dimmer states, triggers, actions, and UI — and shows how to add additional device states and actions on top. ## Sensor Device Plugin { #example-sensor } Creates devices which inherit basic sensor states, triggers, actions, and UI, plus plugin-defined states and actions. ## Energy Meter Device Plugin { #example-energy-meter } Creates devices which inherit energy meter states (watts, kWh), triggers, actions, and UI. ## Speed Control Device Plugin { #example-speed-control } Creates speed-control devices (for example, ceiling fans) with inherited speed states, triggers, actions, and UI. ## Sprinkler Device Plugin { #example-sprinkler } Creates sprinkler controller devices with inherited zone-control states, triggers, actions, and UI. ## Insteon/X10 Listener Plugin { #example-insteon-x10-listener } Shows how a plugin can subscribe to receive callbacks whenever an Insteon or X10 command is received or sent by Indigo. ## Database Traverse Plugin { #example-db-traverse } Shows how to traverse the [Indigo Object Model](../scripting/iom-concepts.md) to enumerate all devices, triggers, schedules, and other objects. ## Twisted Telnet Server Plugin { #example-twisted-telnet } Shows how a plugin can use the Python [Twisted event framework](https://twistedmatrix.com). The example runs a small telnet server that can flash devices on/off; once enabled, connect from Terminal with: ```bash telnet 127.0.0.1 9176 ``` ## More examples in the SDK The SDK repository also includes examples not documented here — device factories, thermostat devices, action APIs, an HTTP responder, broadcaster/subscriber patterns, variable-change and Z-Wave listeners — plus the *Updating to API version 3.0 (Python 3)* migration guide. Browse the [SDK on GitHub](https://github.com/IndigoDomotics/IndigoSDK) for the full set. --- Setting Up a Development Environment (https://docs.indigodomo.com/2025.2/plugin-dev/reference/dev-environment/) --- # Setting Up a Development Environment If you're interested in developing an Indigo plugin to share with other users or just for yourself, the way you approach development can make a big difference. Whether you choose to write your Indigo plugin without any specialized tools or expensive software packages, or instead choose one of the open source applications -- or even a plain text editor -- there are ways to make the process easier and more streamlined. This page describes many authoring packages and steps you can take to make your effort more successful. ## Integrated Development Environments Using an Integrated Development Environment (IDE) can make writing Indigo plugins much easier. IDEs are programming environments that can help you reference Python functions, make recommendations on syntax, and even color code and highlight sections of code to make things easier for you. There are way too many Python IDEs to list them all here, but the more popular ones include (in alphabetical order): - [Atom](https://atom.io) - Atom is a free and open-source text and source code editor for macOS, Linux, and Microsoft Windows with support for plug-ins written in JavaScript, and embedded Git Control. It was developed by GitHub. **NOTE:** Atom and all projects under the Atom organization were officially sunset on December 15, 2022. - [BBEdit](https://www.barebones.com) - BBEdit is a proprietary text editor made by Bare Bones Software, originally developed for Macintosh System Software 6, and currently supporting macOS. The free version of BBEdit works very well for writing Indigo plugins, and the paid version includes some IDE-type integration as well. - [PyCharm](https://www.jetbrains.com/pycharm/) - PyCharm is an excellent full-featured commercial Python development environment made by JetBrains. There are several licenses available for PyCharm from paid to free -- the license you need depends on what you use the program for. **NOTE:** certain features (including the ability to debug Indigo plugins from within the IDE) require a paid PyCharm Professional Edition license. - [Spyder](https://www.spyder-ide.org) - Spyder is an open-source cross-platform integrated development environment for scientific programming in the Python language. While it is more geared towards scientific programming, it works for non-scientific applications, too. - [Sublime Text](https://www.sublimetext.com) - Sublime Text is a shareware cross-platform source code editor. It natively supports many programming languages and markup languages. Users can expand its functionality with plugins, typically community-built and maintained under free-software licenses. - [VSCode](https://code.visualstudio.com) - VSCode is a free source code editor made by Microsoft that runs on Mac, Linux and Windows. VSCode is an extremely popular tool used for Python development. ## Other Editors You can use any of several great text editors to write plugin code, but one aspect is crucial -- they must be able to save **plain text files**. - **TextEdit** - the editor that ships with macOS. If you choose to use Apple's TextEdit app, when you save your code to file, you MUST select **Make Plain Text** from the **Format** menu, and when you save, be sure that the **Plain Text Encoding** is set to `Unicode (UTF-8)`. - [vim](https://www.vim.org) - Vim is a highly configurable text editor built to make creating and changing any kind of text very efficient. It is included as "vi" with most UNIX systems and with Apple macOS. ## Setting Up Your Development Environment Choosing what tools to use to develop your plugin is only one of the considerations you'll need to address. You'll also need to decide how you're going to configure your environment. - **Project organization considerations** -- will you be developing on the same machine where your Indigo server lives? Where will your project files be located? On your machine? Online? A common approach is to have a separate folder structure just for development -- with subfolders dedicated to each project. You should also consider whether you'll benefit from a common location within your project space that can be used for segments of code that you'll use across multiple plugin projects. - **Virtual environments** -- some developers choose to build their projects using [virtual environments](https://docs.python.org/3/tutorial/venv.html). This allows for each project to reside in a separate environment where changes can be made to one environment while leaving the others unchanged. For example, installing different versions of Python libraries depending on the project's needs. - **Backups and version control** -- You'll want to save your work along the way. Backups are easy. Make them. Often. But you'll also want to be in a position to be able to revert back to prior versions in case you decide to undo some changes you've made. GitHub is a popular choice, but there are other options out there and you can always roll your own. - **Plugin versioning** -- You'll need to come up with a way to assign version numbers to your plugin. By incrementing the version number with each release, Indigo will be better able to identify updates and more easily install them -- version numbers are required if you release your plugin through the Indigo Plugin Store. There are many ways to do versioning, but it's common to use a two- or three-number [semantic versioning system](https://en.wikipedia.org/wiki/Software_versioning#Semantic_versioning) where: - Major version - often incremented with substantial changes to the software. - Minor version - often incremented with new features or enhancements. - Patch - often incremented with bug fixes and minor code refinements. - **Collaboration** -- will you be working on the plugin by yourself or will you be coordinating with other developers? If you're working in a group environment, you'll need to establish a method (again, GitHub is a popular choice) but also protocols for how your team's work will be combined. - **Symlinks** -- Once you're ready to test your code, you'll need to install your plugin on your Indigo server in order for it to run and access the Indigo Object Model Framework (IOM). You could choose to install your plugin like you would with any other plugin, but there is a better way -- using symbolic links. A link allows your "original" plugin code to remain outside the Indigo file system but in a place where the Indigo server will still see it. Creating symlinks for your development plugins using the Terminal app is easy: 1. Open two Finder windows, one pointing to the folder that contains your plugin code and the second pointing to the Indigo file tree. 2. First, type the following command in Terminal: `ln -s` 3. Drag your plugin file and drop it on the Terminal window. 4. Drag the Indigo Plugins (Disabled) folder and drop it on the Terminal window, so it looks something like this: `ln -s /Users/User/My Development Environment/my_plugin.indigoPlugin /Library/Application\ Support/Perceptive\ Automation/Indigo\ 2022.1/Plugins\ \(Disabled\)` 5. Switch to Terminal and hit return. If things went according to plan, you should see your plugin in the Plugins (Disabled) folder with a small arrow in the lower left corner (indicating that it's a linked file). 6. Restart the Indigo server and you should see your plugin in the Plugins Menu. With the file successfully linked, you can safely Enable and Disable the plugin in Indigo and the operating system will update the symbolic link accordingly. You can then safely make changes to your code, save, and reload the plugin in Indigo. - **Hosting** -- If you choose to share your plugin with the world, you'll need a place to host the plugin files so that others can download it. - **Indigo Plugin Store** -- The recommended way to share plugins with other Indigo users is via the Indigo Plugin Store. By using the store, you get a central place where users can search for plugins to solve various scenarios, you get built-in version notifications to users via the Mac Client, etc. Check out the [Plugin Store Submission Guidelines](../guide.md#adding-your-plugins-to-the-plugin-store). - **GitHub** -- Many developers manage their plugin code on GitHub, and in fact you can configure your Plugin Store entry to pull information and releases from GitHub (which is the recommended way of adding plugins to the store). GitHub is a very powerful tool used for distribution, version tracking, collaboration and other things. A GitHub account is free with a few limitations that most users won't encounter. - **Providing Support** -- If you choose to share your plugin with others, it's inevitable that someone will have a question, an issue, or want to contribute code to your effort. There are a variety of ways to do this, but there are several common approaches that developers currently use: - **Indigo Forums** - many developers have dedicated forums for their plugins on the Indigo forums site. If you think your plugin might qualify for its own forum, please [contact Support](mailto:support@indigodomo.com). - **GitHub Wikis** - many developers write documentation for their plugins, and one popular way to make that available to others is by creating a dedicated wiki page on GitHub. - **Plugin Issues** - Some developers prefer users report problems via the Indigo forums, while others prefer users to file issues on GitHub. ## Debugging As a quick summary, Indigo plugins and scripts must run in a special process called the Indigo Plugin Host. That process bridges Indigo native objects (C++ objects) to Python. One side effect of this requirement is that debugging a plugin has some special challenges. For instance, you can't directly debug plugins from most of the IDEs listed above. However, we have facilitated the use of several Python debuggers together with Indigo: - [pdb](https://docs.python.org/3/library/pdb.html) (command line tool), - [PuDB](https://pypi.python.org/pypi/pudb/) (command line tool), and - [PyCharm](https://www.jetbrains.com/pycharm/) (professional version). Each has its pros and cons, but all are a significant improvement over `self.logger.debug()...` In the Plugins Preferences tab, there is a Development section: ![Indigo Plugin Preference for Debugging Image](../../images/ss88.png) (Bet you didn't even remember that tab was there). Because we need to start plugins in a special way for debugging to work, we need to show some special debugging menus. Check the checkbox to see those menus in each plugin's submenu. Next, each debugger needs to be started in a specific way, so select the debugger that you want to use. By enabling debugging menus, each plugin will have some additional menu items on their submenu: - Enable/Reload in Debugger - Enable/Reload in Interactive Shell ([discussed below](#plugin-specific-interactive-shell)) Selecting the first will enable or restart the plugin with the plumbing enabled for the debugger you selected. We'll talk about the specifics for each next. ### pdb [pdb](https://docs.python.org/3/library/pdb.html) is a command line debugger that's built-in to Python. We'll let you read the docs to find commands and features. What you do need to know is when you select Enable/Reload in Debugger, Indigo will restart your plugin and open a terminal window running pdb: ![PDB Image](../../images/ss89.png) To add breakpoints to your code, you just add `indigo.debugger()` method calls wherever you want plugin execution to pause in the debugger. Trying to interactively add breakpoints from pdb or PuDB is hit-or-miss because of the threaded way in which Indigo plugins run. The most reliable way to force a breakpoint is by manually adding the `indigo.debugger()` call to the python source and restarting the plugin. Also note `indigo.debugger()` calls are ignored (NOPs) when the plugin is not launched in debugger mode, so don't lose sleep over leaving an `indigo.debugger()` call in a shipping plugin. ### PuDB [PuDB](http://heather.cs.ucdavis.edu/~matloff/pudb.html) is a more graphical debugger (though it's still character based), much like the old [Borland Turbo Debugger](https://en.wikipedia.org/wiki/Borland_Turbo_Debugger) from many years ago: ![PuDB Image](../../images/ss90.png) As with pdb, you add breakpoints to your code by adding `indigo.debugger()` method calls wherever you want plugin execution to pause in the debugger. ### PyCharm Finally, we were able to use PyCharm's **Python Debug Server** feature to enable plugin debugging (formerly referred to as "remote debugging"). It requires a bit more setup, but if you want to use a fantastic modern IDE, this is the choice for you. **Note**: because we're using the Debug Server feature, you can only use the paid professional version of PyCharm as the community edition doesn't support it. #### Configure Local Debugging The most straight-forward debug configuration is to use PyCharm to debug your plugin running in Indigo on the same Mac. With your plugin's project open in PyCharm, create a run configuration of type **Python Debug Server** (formerly **Remote Python Debug**): ![Python Debug Server Image](../../images/python_debug_server.png) There are three important config parameters in this dialog (you can name the configuration anything you want). The first two tell how to connect: specify localhost in the **Local host name** box and 5678 in the **Port** field. The next field you need to adjust is the Path mappings field. Recall that the recommended way of developing Indigo plugins is to put your plugin in a central location (not inside the Indigo folders), then make a symbolic link to it in the Plugins directory. We do this because the Indigo server moves a plugin between two different folders when enabling/disabling. An IDE/editor will get confused when this happens, so by putting the actual code in a place that never moves and allowing the Indigo Server to move a symbolic link around, you get around this issue. Because of this, you need to tell PyCharm where the actual path to the source is when the plugin is enabled and being debugged. Click the ellipsis button at the end of the **Path mappings** field to add a mapping. On the **Local path** side, you want to specify the actual path to your plugin's source (i.e. `/Users/you/path/to/myplugin.indigoPlugin`). On the **Remote path** side, you want to specify the path to your plugin's symlink when the plugin is enabled (i.e. `/Library/Application Support/Perceptive Automation/Indigo 2022.1/Plugins/myplugin.indigoPlugin`). **TIP:** to get the path to a file in the Finder, right-click on the file to show the contextual menu, then press the Option key. The Copy item will change to Copy as Pathname which is exactly what you want. One other option is the **Suspend after connect** checkbox: if you have that checked, then when your plugin restarts with the debugger, it will pause execution in the `__init__()` method. We don't recommend doing this since you can add breakpoints anywhere you want in the code, including in the `__init__()` method. **Note:** the PyCharm debug configuration dialog says that you need to install `pydevd` and add two lines of code to connect to the debug server, however, the IPH (Indigo Plugin Host) takes care of this for you so **_there's no need to complete those steps._** That's it for setup! To debug, just run the debug configuration and then restart your plugin in the debugger. Unlike when using pdb and PuDB, you don't need to add explicit breakpoints to the source code using `indigo.debugger()` -- rather, just interactively add breakpoints in PyCharm: ![Breakpoints in PyCharm Image](../../images/ss92.png) You can step through code, inspect, etc., just like you are debugging any other Python project. We hope that you'll find a great debugging solution for your needs in one of these options. We've added a couple of new API methods on the plugin objects that are part of this change: - `plugin.isRunning()` -- will return true if the plugin is enabled, initialized, and running, and - `plugin.restartAndDebug()` -- a parallel to the `restart()` method except that it starts the plugin running in the selected debugger. ##### PyCharm Plugin Restart Tool In PyCharm, you can add "External Tools" like linters and custom tools as you build out your development environment. This script is a convenience tool which allows you to restart an Indigo plugin directly from PyCharm. For example, you might make changes to your plugin code and need to restart the plugin to continue debugging. This approach allows you to do this all through the PyCharm UI. The Python code you need for the external tool is: ```python #! /usr/bin/python3 import os import sys import argparse import plistlib def searchFile(fileName, path): return None def main(argv): parser = argparse.ArgumentParser( description="Restart a plugin given its full name or ID" ) parser.add_argument( "project_directory", help="the project directory in which to search", type=str ) args = parser.parse_args() info_plist = None found = False for root, dirs, files in os.walk(args.project_directory, topdown=False): if not found: for name in files: if name == "Info.plist": info_plist = os.path.join(root, name) found = True break if info_plist: with open(info_plist, "rb") as f: plist_dict = plistlib.load(f, fmt=plistlib.FMT_XML) os.system(f"/usr/local/indigo/indigo-restart-plugin -d -n {plist_dict['CFBundleIdentifier']}") else: print("Info.plist not found") if __name__ == "__main__": main(sys.argv[1:]) ``` And you would set up the external tool with these settings: ![Remote Plugin Restart Image](../../images/remote_plugin_restart.png) Now, when you use that External Tool, it will reload your plugin with the debugger enabled. You can even configure your debug run configuration to run this as a **Before launch** tool (see the configuration image above). So every time you run the debug configuration, it will also restart the plugin. #### Configure Remote Debugging (Experimental) The above local debugging configuration presumes that the Indigo Server and the plugin you are debugging are running on the same machine on which you are running PyCharm. It's also possible (and experimental at this point) to debug a plugin running on an Indigo Server on a separate Mac on the network from the one on which you are running PyCharm. We're listing this approach as experimental for the time being as it hasn't been fully tested; however, the implementation shows promise. The setup isn't radically different than the local configuration outlined above: it's the same "Python Debug Server" configuration, EXCEPT that you specify an IP address. In the Debug configuration in PyCharm you specify the IP address of the Mac _PyCharm_ is running on (local won't work, it has to be the IP address). In the plugin's init method, make the very first line of the init method: ```python def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.DEBUG_SERVER_IP = "192.168.1.24" # IP address of the Mac running PyCharm super(Plugin, self).__init__(pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.debug = self.pluginPrefs.get("showDebugInfo", False) # Etc... ``` Note that it **must** happen before you call the `super` method. You can use the same port number (_5678_ by default) or you can specify a custom port as well by setting `indigo.DEBUG_SERVER_PORT` along with the IP. Another note: you must remove any custom IP addresses/ports that you specify in the plugin before distributing your plugin to avoid any erroneous error messages during startup on users' Indigo Servers. The next requirement is that the **EXACT** same version of PyCharm has to be installed in the remote Mac's `/Applications/` directory. It doesn't have to be licensed (and you don't have to launch it). It needs to be there and match exactly because Indigo looks for the debug module inside the PyCharm application bundle, and it has to be the exact same module on both sides of the connection. You might be tempted to try to install it from pip3, and while it looks like the same pydevd-pycharm version exists on pypi (the pip repository) that PyCharm is using, we've found that it just doesn't work consistently when installed from pip. It's best to put PyCharm on the remote Indigo Server Mac (and make sure PyCharm is the same version on both Macs). ### Plugin Specific Interactive Shell Another great debugging tool is the ability to open a scripting shell that's specific to your plugin's context. This shell is like the more general shell you get when you select the **Plugins->Open Scripting Shell** menu item, except that because we launch it as part of your plugin's startup, it has access to everything in your plugin. You can call methods that your plugin implements, inspect your plugin's objects, etc. ### Detecting if Your Plugin is in Debug Mode It may be useful for your plugin to be able to detect whether it has been loaded in Debug Mode (Plugin Menu > My Plugin > Enable/Reload in Debug Mode) and what debugger it's loaded in. This can reduce the kinds of "one off" debug logging levels that might be needed in your plugin. To do this, call this command: ```python indigo.host.debugMode() ``` Possible values are currently these integers: ```text kPluginDebugMode_none = 0, kPluginDebugMode_debugPdb = 100, kPluginDebugMode_debugPudb, kPluginDebugMode_debugPyCharm, kPluginDebugMode_debugShell = 200 ``` ## Tips, Tricks and Best Practices ### External Requirements Python has a huge collection of libraries/modules available on [pypi.org](https://pypi.org) and installable with the `pip3` command. Your plugin can use those libraries, and by specifying which of those libraries in a `requirements.txt` file, Indigo will automatically install those when your plugin first starts. Check out the [Python Packages for Plugin Developers](../../scripting/guides/python-packages.md) section of the developer docs (**starting with the 2023.2 release**) for more details. One option for setting up your development environment would be to create a virtual environment into which you install/manage the libraries that your plugin will need. This is best done when you first start working on a plugin or when you make significant changes (additional libraries, etc.) There's lots of information available online regarding setting up and working with virtual environments -- it may be best to start at [the source](https://docs.python.org/3.11/library/venv.html). Once you create a virtual environment (let's say you named it `venv`), this is what its directory structure will look like: ```text - venv - bin - lib - python3.XX - site-packages - pyvenv.cfg ``` The `site-packages` directory is where any pip-installed modules (in that venv) will be installed. You can make a symbolic link from that directory as your `Contents/Packages` directory using a command like this: ```text ln -s /path/to/venv/lib/python3.XX/site-packages /path/to/YourPlugin.indigoPlugin/Contents/Packages ``` and then you can do this: ```text touch /path/to/YourPlugin.indigoPlugin/Contents/Packages/pip-install-log-success.txt ``` This will keep the plugin host process from trying to install any requirements.txt file that you have in the `Server Plugin` folder. Then, you just manage the packages in the `venv` as necessary until you are satisfied that you have everything working. Once you are ready to release your plugin, then you can generate the requirements.txt file (make sure the terminal window you're using has the `venv` activated): ```text pip3 freeze > "/path/to/YourPlugin.indigoPlugin/Contents/Server Plugin/requirements.txt" ``` And this will write out the packages you have installed in your `venv` that the host process will install for the user. The last step would be to remove (or just move out of the way) the `Packages` directory, and create a release for your plugin using whatever method you currently use. ### Indigo Utilities Indigo ships with several command line utilities -- several of which can help you with your development efforts. The tools can be found in the `/usr/local/indigo` folder. From the command prompt, type: - `indigo-restart-plugin` - to restart a plugin. Pass the plugin ID as an argument: `indigo-restart-plugin com.myOrganization.my_plugin`. For security reasons, you can not start a plugin that was not previously running and you can not stop a running plugin. Only `Restart` is allowable and only one plugin can be restarted at a time. - `indigo-clean-and-zip-plugin` - to package a plugin file for distribution (more below). - `indigo-start` - to start the Indigo server. This utility will only work if the "Auto start Indigo Server on user login" option is checked within the Indigo Server preferences pane. - `indigo-host` - to start an interactive shell session with the Indigo server (the same as if started from the Plugins menu in the Indigo UI). - `indigo-stop` - to send a stop signal to the Indigo server. **_The indigo-clean-and-zip-plugin utility is especially useful for developers_**. You can use this utility to prepare your `indigoPlugin` file for distribution. The utility will inspect the plugin package and remove unnecessary files (such as `.pyc` files) and zip the plugin package to the same location as the plugin file. **_Using the clean and zip tool before publishing your plugin is highly recommended._** ### Linters There are several utilities available to improve your code; one type of utility is often referred to as a "Linter". Linters inspect your code for things that you might want to address including: - Syntax problems, - Duplicated code segments, - typos, and - other potential issues. Even if your code is running the way you expect, a linter may still suggest potential improvements. ### How to Tell if a Plugin was Started in Debug Mode If you want to know whether your plugin was started in debug mode, you can call `indigo.host.debugMode`. The method will return an int, so you'll need to cast it to a bool. If it's `True` it's in debug mode, otherwise `False`. ```python debug_mode = indigo.host.debugMode debug_mode_bool = bool(debug_mode) ``` ### No Module Named 'indigo' Your preferred IDE may provide some automated syntax checking, highlighting potential errors in your code. Typically, plugins will often make references to the Indigo base class `indigo.*` and your syntax checker might alert you that it can't find the indigo module. This is to be expected because you can't import Indigo into a plugin like you can with standard Python modules. However, you can minimize the number of syntax error alerts by "tricking" your IDE by including the following with your other import statements: ```python try: import indigo except ImportError: pass ``` You will likely still get a syntax error warning on the `import indigo` line, but this is preferable to many, many syntax warnings throughout your code base. --- plugin.py Method Reference (https://docs.indigodomo.com/2025.2/plugin-dev/reference/plugin-py/) --- # Plugin Method Reference !!! abstract "In this guide" This page is a complete reference for `plugin.py` — the main Python class that every Indigo plugin must implement — covering lifecycle methods, device and action callbacks, logging, and HTTP request handling. ## plugin.py Once you have your UI all described, it’s time to write some code. Your `plugin.py` file is just like any other Python file - it will start with any `import` statements to include various libraries, and it can define global variables. The most important part of the `plugin.py` file is the definition of your plugin’s main class: `class Plugin(indigo.PluginBase):` This is the class that will define all the entry points into your plugin from the host process and the object bridge between your Python objects and the host process’s C++ objects. Your class must inherit from the `indigo.PluginBase` class. A quick note here - all bridge objects and communication with the IndigoServer will be done through the `indigo` module. Because it’s so important, we automatically import it for you so you don’t need an import statement. There are a few methods that the host process will call at various times during your plugins lifecycle, some required and others are optional. !!! note "Subscribing to Object Changes" Some of these methods may require you to [subscribe to object changes](../../../scripting/iom-concepts.md#subscribing-to-object-change-events) - specifically, if they're objects that your plugin didn't directly create (devices of other types) or other object types (triggers, schedules, variables). Often, those subscriptions should be made in the `startup()` method. ## In This Section The `Plugin` class (subclass of `indigo.PluginBase`) implements your plugin through a set of callbacks: - **[General Plugin Methods](general-methods.md)** — lifecycle: startup, shutdown, concurrent thread, prefs. - **[Device Methods](device-methods.md)** · **[Trigger Methods](trigger-methods.md)** · **[Variable Methods](variable-methods.md)** — per-object-type callbacks. - **[Helper Methods](helper-methods.md)** · **[Properties](properties.md)**. - **[Logging](logging.md)** · **[Processing HTTP Requests](http-requests.md)** · **[Event and Message Flow](message-flow.md)**. - **[Additional Topics](additional-topics.md)** — preferences file, 3rd-party libraries, dev environment. --- Additional Topics (https://docs.indigodomo.com/2025.2/plugin-dev/reference/plugin-py/additional-topics/) --- # Additional Topics ## Plugin Preferences File The plugin's preferences are stored in its preferences file. Plugin prefs are cached but are flushed periodically to the actual preference file. It will also automatically flush when the plugin exits. ## 3rd Party Python Libraries Indigo {{ version }} includes a variety of popular 3rd party Python libraries which are described on the [Python Packages](../../../scripting/guides/python-packages.md) page. Any changes to the libraries installed will be detailed there. !!! warning You should not make changes to the packages that Indigo installs. If you need a different package version, you should include it within your plugin package distribution. ## Setting Up a Development Environment Many plugin developers choose to write their code in an IDE (Integrated Development Environment) such as [PyCharm](https://www.jetbrains.com/pycharm/) or [pdb](https://docs.python.org/3/library/pdb.html). There are several tips that will make using IDEs to develop Indigo Plugins more effective on the [Setting Up a Development Environment](../dev-environment.md) page. --- Device Methods (https://docs.indigodomo.com/2025.2/plugin-dev/reference/plugin-py/device-methods/) --- # Device Specific Methods ## deviceCreated() { .ref-head data-toc-label="deviceCreated" } This method will get called whenever a new device defined by your plugin is created. In many circumstances you won't need to implement this method since the default behavior — which is to call the `deviceStartComm()` method if the device belongs to your plugin and is enabled — is what you want anyway (see `deviceStartComm()` above for details). However, if you need to know when a device is created but before your plugin is asked to start communicating with it, this method provides that hook. If you implement this method you'll need to call `deviceStartComm()` yourself or duplicate the functionality here. You can also have this method called for devices that don't belong to your plugin. If you want to know when all devices are created (and updated/deleted), call `indigo.devices.subscribeToChanges()` to have the IndigoServer send all device 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. **Method** | Method Name | Required | |-------------------------------------------------------|----------| | `deviceCreated(self, dev)` | No | **Parameters** | Parameter | Description | |----------------------------------|--------------------------------------------------------------------| | `dev` | an `indigo.Device` object representing the device that was created | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def deviceCreated(self, dev): # Perform any tasks necessary to make sure the new device is fully configured, and optionally, perform an initial refresh. ``` ## deviceDeleted() { .ref-head data-toc-label="deviceDeleted" } 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. **Method** | Method Name | Required | |-------------------------------------------------------|----------| | `deviceDeleted(self, dev)` | No | **Parameters** | Parameter | Description | |----------------------------------|--------------------------------------------------------------------| | `dev` | an `indigo.Device` object representing the device that was deleted | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def deviceDeleted(self, dev): # Perform any clean up tasks after a plugin device is deleted ``` ## deviceStartComm() { .ref-head data-toc-label="deviceStartComm" } 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). **Method** | Method Name | Required | |---------------------------------------------------------|----------| | `deviceStartComm(self, dev)` | No | **Parameters** | Parameter | Description | |----------------------------------|---------------------------------------------------| | `dev` | an `indigo.Device` object representing the device | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def deviceStartComm(self, dev): # Perform any clean up tasks after communication with a plugin device is (re)established. ``` ## deviceStopComm() { .ref-head data-toc-label="deviceStopComm" } 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. **Method** | Method Name | Required | |--------------------------------------------------------|----------| | `deviceStopComm(self, dev)` | No | **Parameters** | Parameter | Description | |----------------------------------|---------------------------------------------------| | `dev` | an `indigo.Device` object representing the device | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def deviceStopComm(self, dev): # Perform any clean up tasks after communication with a plugin device is disabled. ``` ## deviceUpdated() { .ref-head data-toc-label="deviceUpdated" } Complementary to the `deviceCreated()` method described above, but signals device updates. You'll get a copy of the old device object as well as the new device object. The default implementation of this method will do a few things for you: if either the old or new device are devices defined by you, and if the device type changed OR the communication-related properties have changed (as defined by the `didDeviceCommPropertyChange()` method - see above for details) then `deviceStopComm()` and `deviceStartComm()` methods will be called as necessary (stop only if the device changed to a type that isn't your device, start only if the device changed to a type that belongs to you, or both if the props/type changed and they both belong to you). **Method** | Method Name | Required | |-------------------------------------------------------------------|----------| | `deviceUpdated(self, origDev, newDev)` | No | **Parameters** | Parameter | Description | |--------------------------------------|---------------------------------------------------------------------| | `origDev` | an `indigo.Device` object representing the device before the change | | `newDev` | an `indigo.Device` object representing the device after the change | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def deviceUpdated(self, origDev, newDev): # You are responsible for isolating the te difference(s) between the old and new device objects as needed. ``` ## didDeviceCommPropertyChange() { .ref-head data-toc-label="didDeviceCommPropertyChange" } This method gets called by the default implementation of `deviceUpdated()` to determine if any of the properties needed for device communication (or any other change requires a device to be stopped and restarted). The default implementation checks for any changes to properties. You can implement your own to provide more granular results. For instance, if your device requires 4 parameters, but only 2 of those parameters requires that you restart the device, then you can check to see if either of those changed. If they didn't then you can just return False and your device won't be restarted (via `deviceStopComm()`/`deviceStartComm()` calls). **Method** | Method Name | Required | |---------------------------------------------------------------------------------|----------| | `didDeviceCommPropertyChange(self, origDev, newDev)` | No | **Parameters** | Parameter | Description | |--------------------------------------|---------------------------------------------------------------------| | `origDev` | an `indigo.Device` object representing the device before the change | | `newDev` | an `indigo.Device` object representing the device after the change | **Return Value:** | Type | Description | |------|----------------------------------------------------------------------------| | bool | True if communication-relevant device properties changed; False otherwise. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def didDeviceCommPropertyChange(self, origDev, newDev): # You are responsible for isolating the te difference(s) between the old and new device objects as needed. ``` ## getDeviceConfigUiValues() { .ref-head data-toc-label="getDeviceConfigUiValues" } This method will get called whenever a Device configuration is opened. Indigo will look for this method and, if it exists, will pre-populate the configuration dialog with the information created/modified in the method. This method is particularly helpful when you want a Device's configuration to be different from the default (set in the Device configuration XML file). A simple example is provided below. **Method** | Method Name | Required | |----------------------------------------------------------------------------------------|----------| | `getDeviceConfigUiValues(self, pluginProps, typeId, devId)` | No | **Parameters** | Parameter | Description | |------------------------------------------|--------------------------------------------------------| | `pluginProps` | a dictionary of the device's current plugin properties | | `typeId` | the device type ID string as defined in Devices.xml | | `devId` | the integer ID of the device being configured | **Return Value:** | Type | Description | |-------|-------------------------------------------------------------------------| | tuple | A `(valuesDict, errorMsgDict)` tuple to pre-populate the config dialog. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def getDeviceConfigUiValues(self, pluginProps, typeId, devId): valuesDict = pluginProps errorMsgDict = indigo.Dict() if not valuesDict.get("someProp"): valuesDict["someProp"] = "default value" return valuesDict, errorMsgDict ``` ## getDeviceDisplayStateId() { .ref-head data-toc-label="getDeviceDisplayStateId" } 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 `` element in your Devices.xml file. You can, however, implement the method the plugin needs to dynamically determine the which state ID to display. **Method** | Method Name | Required | |-----------------------------------------------------------------|----------| | `getDeviceDisplayStateId(self, dev)` | No | **Parameters** | Parameter | Description | |----------------------------------|---------------------------------------------------| | `dev` | an `indigo.Device` object representing the device | **Return Value:** | Type | Description | |------|-------------------------------------------------------------| | str | The state ID to display in the device list UI state column. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def getDeviceDisplayStateId(self, dev): return dev.states['some_state_id'] ``` ## getDeviceStateList() { .ref-head data-toc-label="getDeviceStateList" } If your plugin defines custom devices, this method will be called by the server when it tries to build the state list for your device. The default implementation just returns the `` element (reformatted as an `indigo.List()` that's available to your plugin via `devicesTypeDict["yourCustomTypeIdHere"]`) in your Devices.xml file. You can, however, implement the method yourself to return a custom set of states. For instance, you may want to allow the user to create custom labels for the various inputs on your device rather than use generic "Input 1", "Input 2", etc., labels. Check out the EasyDAQ plugin which uses this approach. Most plugins will not need to subclass `get_device_state_list()` because -- by default -- it returns the `` list as defined in Devices.xml. So only subclass this method if you dynamically need to change the device states list provided based on specific device instance data (not just device types). **Method** | Method Name | Required | |------------------------------------------------------------|----------| | `getDeviceStateList(self, dev)` | No | **Parameters** | Parameter | Description | |----------------------------------|---------------------------------------------------| | `dev` | an `indigo.Device` object representing the device | **Return Value:** | Type | Description | |-------------|--------------------------------------------| | indigo.List | The list of device states for this device. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def getDeviceStateList(self, dev): type_id = dev.deviceTypeId default_states_list = self.devicesTypeDict[type_id]['States'] new_states_list = indigo.List() for state in default_states_list: # Make your changes new_states_list.append(state) return new_states_list ``` --- General Plugin Methods (https://docs.indigodomo.com/2025.2/plugin-dev/reference/plugin-py/general-methods/) --- # General Plugin Methods ## \_\_init\_\_() { .ref-head data-toc-label="\_\_init\_\_" } This is where the class is initialized. Here you can initialize class-wide variables, initialize and configure custom logging and so on. You have the opportunity to use or alter the prefs before passing them on, but most plugins simply forward them to the base class. You'll most likely use the `startup()` method, described below, to do your global plugin initialization. **Method Signature** ```python def __init__(self, pluginId: str, pluginDisplayName: str, pluginVersion: str, pluginPrefs: indigo.Dict) -> None: ``` **Parameters** | Parameter | Description | |------------------------------------------------|------------------------------------------------------------------| | `pluginId` | the bundle identifier of the plugin (e.g. `com.example.myplugin`) | | `pluginDisplayName` | the human-readable name of the plugin | | `pluginVersion` | the version string of the plugin | | `pluginPrefs` | a dictionary of preferences that the server read from disk | **Return Values** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |------|-----------------------------------------------------------------------------------------------------------| | None | This method should not raise any exceptions. If it does, then that would stop plugin startup immediately. | **Command Syntax Examples** ```python def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): super().__init__(pluginId, pluginDisplayName, pluginVersion, pluginPrefs) ``` ## \_\_del\_\_() { .ref-head data-toc-label="\_\_del\_\_" } This is the destructor for the class. You will almost certainly never need to override this method, but if you do, you'll need call the super class's method when you have finished your code. **Method Signature** ```python def __del__(self) -> None: ``` **Parameters** | Parameter | Description | |-----------|---------------------------------------------| | None | This method does not accept any parameters. | **Return Values** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |------|--------------------------------------------------------------------------------------------------| | None | This method should not raise any exceptions. If it does, the plugin will shut down ungracefully. | **Command Syntax Examples** ```python def __del__(self): super().__del__(self) ``` ## startup() { .ref-head data-toc-label="startup" } 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). **Method Signature** ```python def startup(self) -> None | bool | str: ``` **Parameters** | Parameter | Description | |-----------|---------------------------------------------| | None | This method does not accept any parameters. | **Return Values** | Type | Description | |--------------|--------------------------------------------------------| | None or True | Plugin starts up normally. | | False | Plugin stops with a default message. | | str | Plugin stops with the string used as the stop message. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def startup(self): indigo.server.info(u"Startup called") # if your plugin needs to connect to a single service and remain connected # this is a good place to do it self.connection = start_some_connection() ``` ## runConcurrentThread() { .ref-head data-toc-label="runConcurrentThread" } This method is called in a newly created thread after the `startup()` method finishes executing. It's expected that it should run a loop continuously until asked to shut down. You must call `self.sleep()` with the number of seconds to delay between loops. `self.sleep()` will raise an `self.StopThread` exception when you should end `runConcurrentThread`. You don't have to catch that exception if you don't need to do any cleanup before returning - it will just throw out to the next level. Note: shutdown will be called after `runConcurrentThread` finishes processing so you can do your cleanup there. **Method** | Method Name | Required | |--------------------------------------------------------|----------| | `runConcurrentThread(self)` | No | **Parameters** | Parameter | Description | |-----------|---------------------------------------------| | None | This method does not accept any parameters. | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def runConcurrentThread(self): try: while True: # Do your stuff here self.sleep(60) # in seconds except self.StopThread: # do any cleanup here pass ``` ## stopConcurrentThread() { .ref-head data-toc-label="stopConcurrentThread" } This method will get called when the IndigoServer wants your plugin to stop any threads that it may have created. The default implementation (below) will set the stopThread instance [variable](../../../user/glossary.md) which causes the `self.sleep()` method to throw the exception that you handle in runConcurrentThread above. In most circumstances, your plugin won't need to implement this method. **Method** | Method Name | Required | |---------------------------------------------------------|----------| | `stopConcurrentThread(self)` | No | **Parameters** | Parameter | Description | |-----------|---------------------------------------------| | None | This method does not accept any parameters. | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def stopConcurrentThread(self): self.stopThread = True ``` ## prepareToSleep() { .ref-head data-toc-label="prepareToSleep" } The default implementation of this method will call `deviceStopComm()` for each device instance and `triggerStopProcessing()` for each [trigger](../../../user/glossary.md) instance provided by your plugin. You can of course override them to do anything you like. **Method** | Method Name | Required | |---------------------------------------------------|----------| | `prepareToSleep(self)` | No | **Parameters** | Parameter | Description | |-----------|---------------------------------------------| | None | This method does not accept any parameters. | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def prepareToSleep(self): # Perform any tasks that should be done before the server machine sleeps ``` ## wakeUp() { .ref-head data-toc-label="wakeUp" } The default implementation of this method will call `deviceStartComm()` for each device instance and `triggerStartProcessing()` for each [trigger](../../../user/glossary.md) instance provided by your plugin. You can of course override them to do anything you like. **Method** | Method Name | Required | |-------------------------------------------|----------| | `wakeUp(self)` | No | **Parameters** | Parameter | Description | |-----------|---------------------------------------------| | None | This method does not accept any parameters. | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def wakeUp(self): # Perform any tasks that should be done immediately upon waking the server ``` ## shutdown() { .ref-head data-toc-label="shutdown" } This method will get called when the IndigoServer wants your plugin to exit. If you define a global shutdown [variable](../../../user/glossary.md), 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](../../../user/glossary.md) will already have had a chance to shut down by the time this method is called (see the methods below). !!! note `shutdown()` will be called after runConcurrentThread (discussed above) so any cleanup here will be performed after any changes that might result from a loop in runConcurrentThread. **Method** | Method Name | Required | |---------------------------------------------|----------| | `shutdown(self)` | No | **Parameters** | Parameter | Description | |-----------|---------------------------------------------| | None | This method does not accept any parameters. | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def shutdown(self): # do any cleanup necessary before exiting ``` --- Helper Methods (https://docs.indigodomo.com/2025.2/plugin-dev/reference/plugin-py/helper-methods/) --- # Helper Methods ## applicationWithBundleIdentifier() { .ref-head data-toc-label="applicationWithBundleIdentifier" } What's returned is a scripting bridge SBApplication instance. See the [Scripting Bridge documentation](http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ScriptingBridgeConcepts/Introduction/Introduction.html) for more information. **Method** | Method Name | |------------------------------------------------------------------------------| | `applicationWithBundleIdentifier(self, bundleID)` | **Return Value:** | Type | Description | |---------------|------------------------------------------| | SBApplication | A Scripting Bridge application instance. | **Parameters** | Parameter | Description | |---------------------------------------|----------------------------------------------------------------------------------------------| | `bundleID` | the bundle identifier for the app (usually a fully qualified string like `com.apple.iTunes`) | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python app = self.applicationWithBundleIdentifier("com.apple.iTunes") if app: app.playpause() ``` ## browserOpen() { .ref-head data-toc-label="browserOpen" } This method will open the specified URL in the default browser. Note it does so on the server machine and not on any remotely connected clients. **Method** | Method Name | |-----------------------------------------------------| | `browserOpen(self, url)` | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Parameters** | Parameter | Description | |----------------------------------|--------------------------------| | `url` | the URL to open in the browser | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python self.browserOpen("https://www.indigodomo.com") ``` ## debugLog() { .ref-head data-toc-label="debugLog" } !!! warning "Deprecated" (See Logging below) If, at any point in your plugin, you set `self.debug = True`, then any time debugLog is called the string will get inserted into Indigo's event log. If `self.debug = False` (the default) any call to debugLog does nothing. **Method** | Method Name | |--------------------------------------------------| | `debugLog(self, msg)` | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Parameters** | Parameter | Description | |----------------------------------|-----------------------------------------| | `msg` | the string to insert into the event log | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python self.debugLog("This is a debug message") # deprecated; use self.logger.debug() instead ``` ## errorLog() { .ref-head data-toc-label="errorLog" } !!! warning "Deprecated" See Logging below If you want an error to show up in the event log (in red text), use this log method rather than `indigo.server.log()`. **Method** | Method Name | |--------------------------------------------------| | `errorLog(self, msg)` | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Parameters** | Parameter | Description | |----------------------------------|-----------------------------------------| | `msg` | the string to insert into the event log | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python self.errorLog("An error occurred") # deprecated; use self.logger.error() instead ``` ## openSerial() { .ref-head data-toc-label="openSerial" } This method is identical to creating a new [pySerial Serial object](http://pyserial.sourceforge.net/pyserial_api.html#classes) 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. **Method** | Method Name | |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `openSerial(self, ownerName, portUrl, baudrate, bytesize, parity, stopbits, timeout, xonxoff, rtscts, writeTimeout, dsrdtr, interCharTimeout)` | **Return Value:** | Type | Description | |---------------|------------------------------------------------------------------| | serial.Serial | The opened serial port object, or None if the connection failed. | **Parameters** | Parameter | Description | |---------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------| | `ownerName` | the name of the device or plugin that owns this serial port (used for error logging); must be ASCII text with no Unicode characters | | `all other args` | passed directly to [pySerial's Serial constructor](http://pyserial.sourceforge.net/pyserial_api.html#classes) | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python self.serial_port = self.openSerial( dev.name, dev.pluginProps["portUrl"], baudrate=9600, bytesize=8, parity="N", stopbits=1, timeout=1.0, xonxoff=False, rtscts=False, writeTimeout=1.0, dsrdtr=False, interCharTimeout=None ) if self.serial_port is None: self.logger.error(f"Unable to open serial port for \"{dev.name}\"") ``` ## sleep() { .ref-head data-toc-label="sleep" } 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 shut down or restart the plugin. See `runConcurrentThread` documentation above for more details. **Method** | Method Name | |---------------------------------------------------| | `sleep(self, seconds)` | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Parameters** | Parameter | Description | |--------------------------------------|-------------------------------------| | `seconds` | the sleep duration as a real number | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python self.sleep(60) # sleep for 60 seconds; raises StopThread on plugin shutdown ``` ## substituteVariable() { .ref-head data-toc-label="substituteVariable" } This method will allow any string with the following markup to have a variable value substituted: `%%v:VARID%%` where VARID is the unique variable ID as found in the UI. It's recommended that you call this method twice: first during validation to check syntax and confirm the variable exists, and again at action execution time to perform the substitution. Errors will show up in the event log if the variable doesn't exist (or if there's a formatting problem) at action execution time. **Method** | Method Name | |-------------------------------------------------------------------------------------| | `substituteVariable(self, inString, validateOnly=False)` | **Return Value:** | Type | Description | |-------------|-------------------------------------------------------------------------| | str | The substituted string (when `validateOnly` is False). | | (bool, str) | A `(isValid, errorString)` tuple (when `validateOnly` is True). | **Parameters** | Parameter | Description | |-------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------| | `inString` | the string which contains a valid variable ID | | `validateOnly` | if False (default), returns the substituted string; if True, returns a `(bool, errStr)` tuple indicating whether the syntax is valid and the variable exists | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python # Validate during UI validation, then substitute at execution time is_valid, err_msg = self.substituteVariable(action.props["varString"], validateOnly=True) if is_valid: result = self.substituteVariable(action.props["varString"]) ``` ## substituteDeviceState() { .ref-head data-toc-label="substituteDeviceState" } This method will allow any string with the following markup to have a device state value substituted: `%%d:DEVICEID:STATEKEY%%` where DEVICEID is the unique device ID and STATEKEY is the identifier for the state. It's recommended that you call this method twice: first during validation, and again at action execution time. Errors will show up in the event log if the device doesn't exist (or if there's a formatting problem) at action execution time. **Method** | Method Name | |----------------------------------------------------------------------------------------| | `substituteDeviceState(self, inString, validateOnly=False)` | **Return Value:** | Type | Description | |-------------|-------------------------------------------------------------------------| | str | The substituted string (when `validateOnly` is False). | | (bool, str) | A `(isValid, errorString)` tuple (when `validateOnly` is True). | **Parameters** | Parameter | Description | |-------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------| | `inString` | the string which contains a valid device ID and state key | | `validateOnly` | if False (default), returns the substituted string; if True, returns a `(bool, errStr)` tuple indicating whether the syntax is valid and the device exists | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python # Validate during UI validation, then substitute at execution time is_valid, err_msg = self.substituteDeviceState(action.props["devString"], validateOnly=True) if is_valid: result = self.substituteDeviceState(action.props["devString"]) ``` ## substitute() { .ref-head data-toc-label="substitute" } Validation works the same and should be called when your dialog validates user input. This method calls `substituteVariable()` first followed by `substituteDeviceState()`. The ordering was carefully chosen such that the variable substitution could, in fact, add more device markup to the string before the device substitution happens. So the user can even more dynamically generate content by inserting device markup into a variable value. However, only device markup will be honored in variable values - we don't recursively call variable markup on variable values. **Method** | Method Name | |-----------------------------------------------------------------------------| | `substitute(self, inString, validateOnly=False)` | **Return Value:** | Type | Description | |-------------|-------------------------------------------------------------------------| | str | The substituted string (when `validateOnly` is False). | | (bool, str) | A `(isValid, errorString)` tuple (when `validateOnly` is True). | **Parameters** | Parameter | Description | |-------------------------------------------------------------------------------------------------------------------------------|-------------| | `inString` and `validateOnly` as described in `substituteVariable()` and `substituteDeviceState()` | | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python # Validate during UI validation, then substitute at execution time is_valid, err_msg = self.substitute(action.props["inputString"], validateOnly=True) if is_valid: result = self.substitute(action.props["inputString"]) ``` --- Processing HTTP Requests (https://docs.indigodomo.com/2025.2/plugin-dev/reference/plugin-py/http-requests/) --- # Processing HTTP requests in your plugin Your plugin can process arbitrary HTTP GET or POST requests that are sent to a specific Indigo Web Server (IWS) URL: `https://myreflector.indigodomo.net/message/PLUGINID/actionId/` Substitute the ID of your plugin (as specified in its Info.plist) and the ID of the action that will handle the request (as specified in the Actions.xml). You can also use the direct IP address instead of your reflector. The HTTP request must authenticate if the IWS server has authentication enabled: - The recommended approach is to use an API key: the request must contain an "Authorization" HTTP header, the value of which is "Bearer API_KEY_HERE", where you substitute an API key that is generated from the [Authorizations page](https://www.indigodomo.com/account/authorizations) in the user's Indigo Account. This is also how OAuth is used to authenticate when using an external service like Alexa or Google Home. - If for some reason you can't include the header, you may pass the API key as an additional GET argument on the URL (`?other=args&api-key=KEYHERE`) ## Request As a reminder, here's how you specify an action in Actions.xml that is used only via an API (from another plugin such as the IWS plugin): **Command Syntax Examples** ```xml some message handle_some_action ``` And here's how an action method is defined in your plugin: ```python def handle_some_action(self, action, dev=None, callerWaitingForResult=None): some_value = action.props["somekey"] return some_value ``` Given these examples, here's the URL that would get directed to that action: `https://myreflector.indigodomo.net/message/com.your.pluginId/handle_message/` Calls via this method will always pass in `callerWaitingForResult=True`. You will want to make sure that your plugin returns as quickly as possible - any long-running processes should be put into another thread with some sort of asynchronous message back to the caller if necessary. IWS will insert several things into the `action.props` dictionary: | **Key** | **Value** | |------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `incoming_request_method` | this will be either **POST** or **GET**. | | `headers` | this is a dictionary of the headers in the request. | | `body_params` | if this key exists, it will be a dictionary containing any form **POST** name/value pairs. It won't exist if the request was a **GET** or a **POST** *with a body*. | | `url_query_args` | if there were any query args on the URL line, they will be in this dictionary if it exists. | | `request_body` | this will be the contents of the body of the HTTP request. It won't exist if the request was a **GET** or a **POST** *without any body*. | | `file_path` | this will be a list of path parts from the URL after the action ID. So for the url: `http://host/message/pluginid/actionid/path/to/some/file.txt` the value will be an `indigo.List` with the following items: `["path", "to", "some", "file.txt"]` | !!! note The dicts mentioned above will all be *indigo.Dict* objects, not standard Python dicts. ### Validating the request payload When you handle a JSON (or form) message, you'll typically want to validate the incoming fields before acting on them. The [`indigo.utils.ValidationError`](../../../scripting/reference/utils.md#validationerror) exception is handy here: accumulate per-field errors, raise once, and return a readable error message with an appropriate HTTP status. The same validation helper can be shared with your [Config UI validation methods](../xml/configui/validation.md#using-validationerror). ```python import json def handle_some_action(self, action, dev=None, callerWaitingForResult=None): try: payload = json.loads(action.props.get("request_body", "{}")) errors = indigo.utils.ValidationError("Invalid request payload") if "name" not in payload: errors.add_error("name", "the 'name' field is required") if "value" in payload and not indigo.utils.is_int(payload["value"]): errors.add_error("value", "'value' must be a number") errors.raise_if_errors() # raises only if an error was added except (ValueError, indigo.utils.ValidationError) as exc: # ValueError covers malformed JSON from json.loads() return {"status": 400, "content": str(exc)} # ...payload is valid, do the work and return a reply... return json.dumps({"result": "ok"}) ``` ## Reply What your plugin should return in the simplest case is a JSON string which will just be passed back in the HTTP reply. Your plugin can also return a more complex dictionary containing the following keys: | **Key** | **Value** | |--------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `status` | this is an integer representing a valid [HTTP return code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes). If it's not included, a 200 will be returned. | | `headers` | a dictionary of HTTP headers that will be added to the reply. Most useful will be the 'Content-Type' header if you want to return something else besides JSON (which is the default). | | `content` | this is the actual string returned in the HTTP reply. Defaults to an empty string. | This will allow you to return just about anything - HTML, XML, plain text, etc. IWS will do **no postprocessing** of the content string, so you must ensure that you are returning the properly formatted information that the requester is expecting. You can also pass back an `indigo.Dict` instance that will instruct IWS to stream a file from the filesystem back to the requester. This is the structure: **Command Syntax Examples** ```text { "status": 310, # Internal status code indicating that IWS should stream back the specified file. "file_path": path, # this is a full path string "headers": indigo.Dict({"Content-Type": content_type} # you should add a Content-Type header ) ``` We've provided [a utility method](../../../scripting/reference/utils.md#return-static-file) that will validate that the file specified exists and then will pass back the appropriate `indigo.Dict` instance. ## Errors IWS will return the following HTTP error responses if an error occurs trying to process a request: | **Status** | **Meaning** | |----------------------------------|----------------------------------------------------------------------------------| | `401` | if the OAuth token is invalid. | | `405` | if any method other than **POST** or **GET** is attempted. | | `500` | any unexpected/uncaught exception. | | `501` | if the plugin isn't installed or doesn't define the action specified in the URL. | | `503` | if the plugin is installed but is disabled. | For `50x` errors, a JSON dictionary will be returned describing the issue: | **Key** | **Value** | |------------------------------------------|----------------------------------------------------------------------------------------------------------| | `error` | One of: `plugin_disabled`, `invalid_plugin`, `invalid_action`, `unknown_error` | | `description` | A textual description of the error | | `exception` | Only returned when an `unknown_error` is returned. It will be the stack trace of the uncaught exception. | If your handler returns any kind of error (4xx or 5xx) and includes a message, that message will be returned to the caller as the body of the HTTP reply. This will allow you to return a custom page (404 for instance) that will more appropriately reflect the error. ### Usage Guidance This API is meant primarily for small(ish) text message handling, like JSON/XML messaging APIs or small HTML files. There are a few things that you will want to avoid: - Long-running actions - if your action should be relatively quick to respond: 15-20 seconds is the max guidance - Large files - large files will slow the total turnaround time, which you need to minimize (see above) - Binary data - the API isn't designed for binary data For long-running actions, one pattern would be to reply immediately with some kind of acknowledgement of the incoming message, then handle the processing asynchronously. As this approach would complete the HTTP request/response loop, if your caller needs some kind of status after processing you would need to handle that yourself. --- Logging (https://docs.indigodomo.com/2025.2/plugin-dev/reference/plugin-py/logging/) --- # Logging In previous versions of the API, the plugin base class defined three methods: `debugLog()`, `errorLog()`, and `exceptionLog()`. These were deprecated in favor of the standard [Python Logging Module](https://docs.python.org/2/library/logging.html) (don't worry, the previous APIs will continue to work). The plugin base now has a couple of new attributes related to logging: | Attribute | Description | |--------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `logger` | An instance of the [standard Python Logging class](https://docs.python.org/2/library/logging.html#logger-objects). You should use this instance to log messages. See the examples below for details. | | `indigo_log_handler` | An instance of a special [Python Handler class](https://docs.python.org/2/library/logging.html#handler-objects) that emits messages to the Indigo Event Log window and the Indigo Server's log file. | | `plugin_file_handler` | An instance of a Python [TimedRotatingFileHandler](https://docs.python.org/2/library/logging.handlers.html#timedrotatingfilehandler) which writes you own log files in the /Logs/ directory. A subdirectory will be created using your plugin's ID as the name. Inside that directory will be daily log files (with 20 days of backups) of all log messages from your plugin. See below for examples of the default format of that file. | The [Python logging module](https://docs.python.org/3/library/logging.html) is extremely powerful and flexible, and we recommend reading through [the docs](https://docs.python.org/2/library/logging.html) for a good understanding of how it works and how you can add great logging to your plugin. We'll describe the minimum here that you need to know to do basic logging to the Indigo Event Log window and to your plugin's log file. The logger module defines [5 levels of logging](https://docs.python.org/2/library/logging.html#logging-levels) shown below. Instances of the [Logger](https://docs.python.org/2/library/logging.html#logger-objects) object can be set to log messages at any of those 5 levels, and the level logged can be changed at any time. By default, the `self.logger` instance is set to `logging.DEBUG` (we'll see why a bit further down). You can change this if you want using the `setLevel()` method. We've named the `self.logger` instance "Plugin", because it's the name of the class from which your plugin begins. We'll see why this is important later in the examples. You can very easily write log messages at any level using the following convenience methods defined in the [Logger](https://docs.python.org/2/library/logging.html#logger-objects) class: **Command Syntax Examples** ```python self.logger.debug(u"Debug log message") self.logger.info(u"Info log message") self.logger.warn(u"Warning log message") self.logger.error(u"Error log message") self.logger.critical(u"Critical log message") ``` The `debugLog()`, `errorLog()`, and `exceptionLog()` methods are now just wrappers around the corresponding method above. There is another convenience method defined in the logging module: `self.logger.exception("Error log message with exception appended")`. If you call this method from within an `except` block in your plugin, it will automatically create a `logging.ERROR` level message and append the stack trace to whatever message you supply. A [Logger](https://docs.python.org/2/library/logging.html#logger-objects) instance can have any number of [Handler](https://docs.python.org/2/library/logging.html#handler-objects) objects associated with it. These handler objects are what actually do the heavy lifting in terms of where log messages go and how those messages are formatted. The plugin base class provides two of them: `self.indigo_log_handler` and `self.plugin_file_handler`, both of which are automatically added to `self.logger`. Therefore, calling any of the 5 methods above (`self.logger.debug`, `self.logger.info`, etc.) will automatically route those messages to both the Indigo Server and the plugin file handler. ## self.indigo_log_handler The `self.indigo_log_handler` is an instance of a custom [Handler](https://docs.python.org/3.10/library/logging.html#handler-objects) 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 (except for the `info` level). For convenience, the various log levels are also represented in color. Here's an example of each level: ![Embedded Script Logging Image](../../../images/embedded_script_logging.png) ## self.plugin_file_handler The `self.plugin_file_handler` is an instance of a [TimedRotatingFileHandler](https://docs.python.org/2/library/logging.handlers.html#timedrotatingfilehandler). This handler is used to write log files with some automatic file management: it can rotate the log files (so they don't get too big), and can be configured to keep some number of backups. By default, we've configured it to rotate the log files at midnight each night and to keep 20 backups. You can, of course, change those settings on the handler. [Handler](https://docs.python.org/2/library/logging.html#handler-objects) objects have a [Formatter](https://docs.python.org/2/library/logging.html#formatter-objects) object set, which is how the log line is formatted. By default, we format the log lines with the date/time stamp, the level (e.g. DEBUG), `[the logger name ("Plugin" by default)].[method name]:`, message. Each element is separated by a tab. So, for example, the lines from the methods above will result in these lines in your log file: **Command Syntax Examples** ```text 2016-02-10 15:27:18.194 DEBUG Plugin.runConcurrentThread: Debug logging 2016-02-10 15:27:18.194 INFO Plugin.runConcurrentThread: Info logging 2016-02-10 15:27:18.195 WARNING Plugin.runConcurrentThread: Warning logging 2016-02-10 15:27:18.195 ERROR Plugin.runConcurrentThread: Error logging 2016-02-10 15:27:18.195 CRITICAL Plugin.runConcurrentThread: Critical logging ``` So, date time, level, `Plugin.method` (in this case we're writing from the runConcurrentThread method): message. However, if that's not what you want, you can create your own [Formatter](https://docs.python.org/2/library/logging.html#formatter-objects) instance and set the handler to use that instead. ## Log Levels Earlier, we mentioned that we set the level of the logger to `logger.DEBUG`. Does this mean that all debug or better messages are automatically sent to both the file and the Indigo Event Log? Actually, no. This is because you can also specify at the handler what level to actually log. We default the `plugin_file_handler` to `logger.DEBUG` but we default the `indigo_log_handler` to `logger.INFO`. The reasoning is that you want the file to have all debugging information, but you are likely to need less logging to the Event Log by default. Again, because you have access to those handlers, you can use their `setLevel()` methods to change them as well, and if you've implemented user selectable debug levels, this should fall right in line with what you're expecting. In fact, we've done a little trickery for you so that the old `self.debug` attribute will continue to behave as you might expect. If you have `self.debug` set to `True`, we set the `indigo_log_handler` to `logger.DEBUG`. And if it's `False`, we set it to `logger.INFO`. We also continue to maintain the `self.debug` attribute so your legacy code will continue to work, though that is deprecated as well. ## Logging from Another Class or Submodule Logging from another class or submodule is relatively straight-forward. You can either get the logger for the plugin: **Command Syntax Examples** ```python plugin_logger = logging.getLogger("Plugin") ``` For example, ```python class MyClass(object): def __init__(self): self.logger = logging.getLogger("Plugin") self.logger.debug("MyClass Object") ``` and use it directly, or you can pass your logger into the methods of the submodule. You could also pass in the event log handler defined for you by the plugin base (`self.indigo_log_handler`), and then attach that to a custom logger in your module. ### Custom IndigoLogHandler If you use the instance of `self.indigo_log_handler`, the message emitted to the Event Log window will have a type that is the name of the plugin. If you want log lines with titles other than the plugin name (like a name specific to the submodule), you can instantiate an instance of the `IndigoLogHandler` class (which is what `self.indigo_log_handler` is) instead: **Command Syntax Examples** ```python custom_logger = logging.getLogger("MyModule") custom_handler = self.IndigoLogHandler("MyModule", logging.DEBUG) custom_logger.addHandler(custom_handler) ``` And anything logged to your `plugin_logger` will be reflected in the Event Log: ```text MyModule Debug Some event log debug message here ``` ### exc_info With the logging message `self.logger.critical("Something bad happened.")` you will see the following in the log: **Command Syntax Examples** ```text My Plugin Error Something bad happened. My Plugin Error plugin runConcurrentThread function returned or failed (will attempt again in 10 seconds) ``` which doesn't provide any detail on what actually went wrong. Fortunately, the logging method provides a way to pass more information about the error to the logger with `exc_info`; such as `self.logger.critical("Something bad happened.", exc_info=True)` which yields: ```text My Plugin Error Something bad happened. Traceback (most recent call last): File "plugin.py", line 123, in runConcurrentThread x = 1 / 0 ZeroDivisionError: division by zero My Plugin Error plugin runConcurrentThread function returned or failed (will attempt again in 10 seconds) ``` ### Full Example While there are many different ways to implement logging, here is a "full" example all in one place. **Command Syntax Examples** ```python import logging def __init__(self): # Get the current logging level from pluginPrefs self.debugLevel = int(self.pluginPrefs.get('showDebugLevel', "30")) # Set preferred log format specifier log_format = '%(asctime)s.%(msecs)03d\t%(levelname)-10s\t%(name)s.%(funcName)-28s %(message)s' self.plugin_file_handler.setFormatter( logging.Formatter(fmt=log_format, datefmt='%Y-%m-%d %H:%M:%S') ) self.indigo_log_handler.setLevel(self.debugLevel) def runConcurrentThread(self): self.logger.debug("Starting concurrent thread.") try: x = 1 / 0 except ZeroDivisionError: self.logger.critical("Something bad happened.", exc_info=True) ``` Which yields: ```text My Plugin Error Something bad happened. Traceback (most recent call last): File "plugin.py", line 123, in runConcurrentThread x = 1 / 0 ZeroDivisionError: division by zero ``` ## Logging from Linked and Embedded Scripts Logging from linked and embedded scripts is very straight-forward. You can simply set the level you want by accessing the `logging.*` level you want: **Command Syntax Examples** ```text import logging indigo.server.log("debug message", level=logging.DEBUG) indigo.server.log("info message", level=logging.INFO) indigo.server.log("warning message", level=logging.WARNING) indigo.server.log("error message", level=logging.ERROR) indigo.server.log("critical message", level=logging.CRITICAL) ``` and then logging messages will appear with the colors above. Result: ![Embedded Script Logging Image](../../../images/embedded_script_logging.png) --- Event and Message Flow (https://docs.indigodomo.com/2025.2/plugin-dev/reference/plugin-py/message-flow/) --- # Event and Message Flow Under some circumstances, the Indigo Server will send callbacks to your plugin based on events that take place. For example, if your plugin devices expose features like Turn On, Turn Off or Status Request, Indigo will send a callback to your plugin so you can take actions on these events. There are many different callbacks that can occur which are documented extensively in the [SDK](https://github.com/IndigoDomotics/IndigoSDK/releases/tag/v2025.1). Here is a simple example showing how to handle these callbacks (consult the [SDK](https://github.com/IndigoDomotics/IndigoSDK/releases) for a detailed example that relates to your device's class). **Command Syntax Examples** ```python def actionControlDevice(self, action, dev): ###### TURN ON ###### if action.deviceAction == indigo.kDeviceAction.TurnOn: # Command hardware module (dev) to turn ON here: send_success = True # Set to False if it failed. if send_success: # If success then log that the command was successfully sent. self.logger.info(f"sent \"{dev.name}\" on") # And then tell the Indigo Server to update the state. dev.updateStateOnServer("onOffState", True) else: # Else log failure but do NOT update state on Indigo Server. self.logger.error(f"send \"{dev.name}\" on failed") ###### TURN OFF ###### elif action.deviceAction == indigo.kDeviceAction.TurnOff: # Command hardware module (dev) to turn OFF here: send_success = True # Set to False if it failed. if send_success: # If success then log that the command was successfully sent. self.logger.info(f"sent \"{dev.name}\" off") # And then tell the Indigo Server to update the state: dev.updateStateOnServer("onOffState", False) else: # Else log failure but do NOT update state on Indigo Server. self.logger.error(f"send \"{dev.name}\" off failed") ###### STATUS REQUEST ###### elif action.deviceAction == indigo.kUniversalAction.RequestStatus: # Report on the status of the device self.update_device_status() # Take your action(s) to update the device's status. self.logger.info(f"\"{dev.name}\" updated.") ``` Note that you may see camel case examples of these callbacks like `actionControlDevice()` or snake case `action_control_device()`. As a convenience, Indigo supports both naming styles; however, camel case may be deprecated in the future. [//]: # (FIXME Add discussion about plugin event and message flow for several types of plugins: with devices, events, actions - and plugins that use runConcurrentThread and plugins that start up threads, etc., so that it'll be easier to envision how to approach thinking about a plugin.) --- Properties (https://docs.indigodomo.com/2025.2/plugin-dev/reference/plugin-py/properties/) --- # Properties The base plugin provides some properties that are specific to a plugin instance. | Property | Value Type | Notes | |-----------------------------------------------|------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `pluginFolderPath` | string | The return value is the full path to the plugin. This is useful if you need to construct a full path to a file somewhere in the plugin's hierarchy, perhaps to have IWS stream the file back. | | `pluginSupportURL` | string | The return value is URL that's specified in the plugin's Info.plist. | --- Trigger Methods (https://docs.indigodomo.com/2025.2/plugin-dev/reference/plugin-py/trigger-methods/) --- # Trigger Specific Methods ## didTriggerProcessingPropertyChange() { .ref-head data-toc-label="didTriggerProcessingPropertyChange" } Much like it's device counterpart above (`didDeviceCommPropertyChange()`), this method gets called by the default implementation of `triggerUpdated()` to determine if any of the properties needed for recognizing an event have changed. The default implementation checks for any changes to any properties. **Method** | Method Name | Required | |------------------------------------------------------------------------------------------------|----------| | `didTriggerProcessingPropertyChange(self, origTrigger, newTrigger)` | No | **Parameters** | Parameter | Description | |------------------------------------------|-----------------------------------------------------------------------| | `origTrigger` | an `indigo.Trigger` object representing the trigger before the change | | `newTrigger` | an `indigo.Trigger` object representing the trigger after the change | **Return Value:** | Type | Description | |------|--------------------------------------------------------------------------| | bool | True if processing-relevant trigger properties changed; False otherwise. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def didTriggerProcessingPropertyChange(self, origTrigger, newTrigger): some_property_changed = (origTrigger['some_prop'] == newTrigger['some_prop']) if some_property_changed: # Implement any actions required if your target property changed. return True else: return False ``` ## triggerCreated() { .ref-head data-toc-label="triggerCreated" } This method will get called whenever a new trigger defined by your plugin is created. In many circumstances, you won't need to implement this method since the default behavior (which is to call the `triggerStartProcessing()` method if it's your trigger, and it's enabled) is what you want anyway (see the `triggerStartProcessing()` method above for details). However, if for some reason you need to know when a trigger is created, but before your plugin is asked to start watching for the appropriate conditions, this method can provide that hook. If you implement this method, you'll need to either call `triggerStartProcessing()` or duplicate the functionality here. You can also have this method called for triggers that don't belong to your plugin. If, for instance, you want to know when all triggers are created (and updated/deleted), you can call the `indigo.triggers.subscribeToChanges()` method to have the IndigoServer send all trigger 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. **Method** | Method Name | Required | |------------------------------------------------------------|----------| | `triggerCreated(self, trigger)` | No | **Parameters** | Parameter | Description | |--------------------------------------|----------------------------------------------------------------------| | `trigger` | an `indigo.Trigger` object representing the trigger that was created | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def triggerCreated(self, trigger): self.triggerStartProcessing(trigger) ``` ## triggerDeleted() { .ref-head data-toc-label="triggerDeleted" } 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. **Method** | Method Name | Required | |------------------------------------------------------------|----------| | `triggerDeleted(self, trigger)` | No | **Parameters** | Parameter | Description | |--------------------------------------|----------------------------------------------------------------------| | `trigger` | an `indigo.Trigger` object representing the trigger that was deleted | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def triggerDeleted(self, trigger): self.triggerStopProcessing(trigger) ``` ## triggerStartProcessing() { .ref-head data-toc-label="triggerStartProcessing" } If your plugin defines events, this is likely the place where you'll want to do the work to start watching for those events to occur. For instance, let's say that you have an event for a plugin update, then you'll want to periodically check your site to see if there's a new version available. This is where you'd start that process. When conditions are met in your plugin for a trigger to be executed, you would call indigo.trigger.execute(triggerReference) to tell the Server to execute the trigger (and it's conditions). **Method** | Method Name | Required | |--------------------------------------------------------------------|----------| | `triggerStartProcessing(self, trigger)` | No | **Parameters** | Parameter | Description | |--------------------------------------|-----------------------------------------------------| | `trigger` | an `indigo.Trigger` object representing the trigger | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def triggerStartProcessing(self, trigger): self.active_triggers[trigger.id] = trigger ``` ## triggerStopProcessing() { .ref-head data-toc-label="triggerStopProcessing" } This is the complementary method to `triggerStartProcessing()` - it gets called when the event should no longer be active/enabled. For instance, when the user disables or deletes a trigger, this method gets called. **Method** | Method Name | Required | |-------------------------------------------------------------------|----------| | `triggerStopProcessing(self, trigger)` | No | **Parameters** | Parameter | Description | |--------------------------------------|-----------------------------------------------------| | `trigger` | an `indigo.Trigger` object representing the trigger | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def triggerStopProcessing(self, trigger): if trigger.id in self.active_triggers: del self.active_triggers[trigger.id] ``` ## triggerUpdated() { .ref-head data-toc-label="triggerUpdated" } Complementary to the `triggerCreated()` method described above, but signals trigger updates. You'll get a copy of the old trigger object as well as the new trigger object. The default implementation of this method will do a few things for you: if either the old or new trigger are triggers defined by you, and if the trigger type changed OR the communication-related properties have changed (as defined by the `didTriggerProcessingPropertyChange()` method - see above for details) then `triggerStopProcessing()` and `triggerStartProcessing()` methods will be called as necessary. **Method** | Method Name | Required | |----------------------------------------------------------------------------|----------| | `triggerUpdated(self, origTrigger, newTrigger)` | No | **Parameters** | Parameter | Description | |------------------------------------------|-----------------------------------------------------------------------| | `origTrigger` | an `indigo.Trigger` object representing the trigger before the change | | `newTrigger` | an `indigo.Trigger` object representing the trigger after the change | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def triggerUpdated(self, origTrigger, newTrigger): indigo.PluginBase.triggerUpdated(self, origTrigger, newTrigger) ``` --- Variable Methods (https://docs.indigodomo.com/2025.2/plugin-dev/reference/plugin-py/variable-methods/) --- # Variable Specific Methods { .ref-head-no-code } ## variableCreated() { .ref-head data-toc-label="variableCreated" } 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. **Method** | Method | Required | |---------------------------------------------------------|----------| | `variableCreated(self, var)` | No | **Parameters** | Parameter | Description | |----------------------------------|------------------------------------------------------------------------| | `var` | an `indigo.Variable` object representing the variable that was created | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def variableCreated(self, var): pass # respond to the newly-created variable as needed ``` ## variableDeleted() { .ref-head data-toc-label="variableDeleted" } Complementary to the `variableCreated()` method described above, but signals variable deletes. **Method** | Method Name | Required | |---------------------------------------------------------|----------| | `variableDeleted(self, var)` | No | **Parameters** | Parameter | Description | |----------------------------------|------------------------------------------------------------------------| | `var` | an `indigo.Variable` object representing the variable that was deleted | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def variableDeleted(self, var): pass # respond to the deleted variable as needed ``` ## variableUpdated() { .ref-head data-toc-label="variableUpdated" } Complementary to the `variableCreated()` method described above, but signals variable updates. You'll get a copy of the old variable object as well as the new variable object. **Method** | Method Name | Required | |---------------------------------------------------------------------|----------| | `variableUpdated(self, origVar, newVar)` | No | **Parameters** | Parameter | Description | |--------------------------------------|-------------------------------------------------------------------------| | `origVar` | an `indigo.Variable` object representing the variable before the change | | `newVar` | an `indigo.Variable` object representing the variable after the change | **Return Value:** | Type | Description | |------|--------------------------------------| | None | This method does not return a value. | **Exceptions Raised** | Type | Description | |--------------|-------------| | | | **Command Syntax Examples** ```python def variableUpdated(self, origVar, newVar): pass # respond to the updated variable as needed ``` That’s all the methods that will be called automatically by the host process. You may, of course, define many more methods. Some that you will probably want to define: methods to be called when a button is clicked in a `` dialog and methods called by `` and ``. You can also define your own classes, either in `plugin.py` or more likely in separate files. We believe the plugin host process offers you a great deal of flexibility in how you construct your Python code. --- Plugin XML Reference (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/) --- # Plugin XML Reference !!! abstract "In this guide" This page is a complete reference for the XML files used to define Indigo plugin configuration dialogs, devices, events, actions, and menu items, including all supported field types and attributes. ## Plugin XML Files An Indigo plugin is configured through a set of XML files plus the ConfigUI markup they share: - **[ConfigUI Fields](configui/index.md)** — the dialog field types used throughout the XML files. - **[PluginConfig.xml](pluginconfig.md)** — plugin-wide configuration. - **[Devices.xml](devices.md)** · **[Events.xml](events.md)** · **[Actions.xml](actions.md)** — declaring device types, events, and actions. - **[MenuItems.xml](menuitems.md)** · **[SupportURL Elements](supporturl.md)**. --- Actions.xml (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/actions/) --- # Actions.xml { .ref-head-no-code #actions-xml } Your plugin will also very likely define some actions that a user can take. This is where you define those. Here’s a simple example: ```xml http://www.yourdomain.com/plugin/pluginActions.html Reset Interface resetInterface ``` As with ``, your `` elements can define a `` element as well - the actions dialog now has a help button on it and if one of your actions is selected clicking on the help button will take your user to the specified URL. If you don’t specify one then the default help page for all actions will show. You can specify an Action item field to be a label within a `ConfigUI` in your action list when you want to include some text -- for example, to explain what users should enter into a text field. Label tags require a unique `id` and the type should be set to `label`. Labels do not require any other elements. ```xml ``` You can specify an Action item to be a separator in your action list so that when they're displayed in the UI there is a visual separation. Simply insert an Action defined like this between two other Action elements: While you don't have to include any elements, the id still must be unique. `` For a full description of all the different XML conventions that Indigo uses, [see this page](../../guide.md#indigo-plugin-xml-conventions). **Attributes** When defining a full plugin action, a few elements are required. Here’s how you construct your `` elements in `Actions.xml`: | Attribute | Type | Required | Notes | | | |---------------------------------------------|-----------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--|--| | `id` | Attribute | Yes | This is a unique id for the event in this `Actions.xml` file. | | | | `deviceFilter` | Attribute | No | If present, a popup list of devices that match the specified [device filter](configui/dynamic-lists.md#config-dynamic-list) will be shown in the UI. Many actions may not need any configuration beyond a device selection so we've enabled you to specify that a device must be selected and passed to the action's `CallbackMethod`. This will avoid the necessity to create a `ConfigUI` element with just a device popup. | | | `uiPath` | Attribute | No | [Added in API v1.4](https://www.indigodomo.com/indigo/api_release_notes/1.4/). You can specify where in the menu hierarchy your action will be placed. By default, Indigo will create a submenu at the bottom of the Actions list and put your actions there. If you specify "DeviceActions", Indigo will create a submenu on the Device Actions menu and put your actions there. If you specify "NotificationActions", Indigo will insert your action n the Notification Actions menu without a submenu (be sure to name your action appropriately so that it's clear what it does). [Added in API v2.0](https://forums.indigodomo.com/viewtopic.php?p=126067#p126067): use "hidden" to hide the action in the UI. Useful for actions that are only intended to be used from scripts/plugins (an API of sorts). | | `Name` | Element | Yes* | This is the text that’s shown in the actions dialog that represents this action. `Name` is not required for labels and separators. | | | | `CallbackMethod` | Element | Yes* | This is the name of the method that implements the action in your code. `CallbackMethod` is not required for labels and separators. | | | | `ConfigUI` | Element | No | If your action requires any configuration (as most will), you can specify a `` element that’s defined exactly as above. | | | When the Action is fired in Indigo, the callback method will be called and an Indigo dictionary will be passed with information about how to handle the call. In the example below, the `action` payload will contain the necessary things to implement the call. Notice the call to `action.props.get()` in the Python callback below, which will pull the appropriate value from the specified key (`message`, `type`, etc.) using the standard Python dictionary `get()` method. Here is a sample action dict sent to a plugin: ```text configured : True delayAmount : 900 <-- Read only; currently not used description : redraw one chart <-- Taken from the Actions.xml element. props : com.foo.indigoplugin.my_plugin : (dict) config_prop_1 : 123 (integer) config_prop_2 : true (bool) config_prop_3 : "baz" (string) replaceExisting : True <-- Read only; currently not used textToSpeak : ``` Here is the code that implements the Write to Log action defined in the Action Collection plugin: ```python def writeToLog(self, action): # call for variable substitution on the message field they entered theMessage = self.substitute(action.props.get("message", "")) # set the type for the message if they configured one theType = action.props.get("type") # debugging - show the message if debugging is enabled self.debugLog(u"Write to log: " + theMessage) # if they entered a type, log the message with it, otherwise log the message without a type if theType: indigo.server.log(theMessage,type=theType) else: indigo.server.log(theMessage) ``` Notice the call to `self.substitute()` - this method is defined in the [plugin base class](../plugin-py/index.md). If your user inserts `%%v:12345%%` into their string where `12345` is the ID of a variable, the call will return a string with all variable occurrences substituted. If your user inserts `%%d:12345:someStateId%%` into their string where `12345` is the ID of a device and `someStateId` is a valid state identifier, the call will return a string with all device state occurrences substituted. See the [substitutions docs](../../../user/automation/substitutions.md) for more information. Your plugin actions can return any Python "primitive" value to users when they call your action with the `executeAction` method (a return value is not required). The return value can be any one of the following object types: - None `None`, - booleans `bool`, - integers `int()`, - floats `float()`, - strings `str()`, - Indigo Dicts `indigo.Dict()`, - Indigo Lists `indigo.List()`. Your plugin action may also receive an optional `callerWaitingForResult` parameter which is a request for your plugin to block until it can complete its tasks (and also return a response). ```python def actionSomeXmlDefineActionCallback(self, action, dev, callerWaitingForResult, event_data=None): # do your typical action stuff here # note: event_data contains a dictionary of information about what caused the event to fire return some_value ``` The action can block until it has the result of the action (which it can then just return directly), but note that all callbacks must be executed in the same plugin host thread. This means that other callbacks would become blocked, including those to get configuration UI XML and values. This turns into a potential usability problem in the Indigo client, as the plugin configuration, actions, trigger, etc., UI will become unresponsive and start to timeout. Instead, if the plugin cannot immediately return a result for the action (because it has to communicate with hardware, for example), then it can acquire a completion handler callback function that can be executed later (asynchronously) in another thread. The pattern would then be something like: ```python def actionSomeXmlDefineActionCallback(self, action, dev, callerWaitingForResult): completeHandler = None if callerWaitingForResult: # only acquire the completion handler if caller is wanting a result completeHandler = indigo.acquireCallbackCompleteHandler() self.pluginsActionQueue.put((action, dev, completeHandler)) # Note self.pluginsActionQueue is not implemented by the base plugin and is just an example # of how one might pass the action and completeHandler to another thread using a queue. def anotherThreadQueueHandlerFunc(self) try: (action, dev, completeHandler) = self.pluginsActionQueue.get(True, 60) # Process the requested action here. This can block and take a long # time without impacting UI usability because this method would be # called from another thread. # Upon completion, you can return the result to the original action caller # via: if completeHandler is not None: completeHandler.returnResult("success performing action") except Exception as exc: # If there was an exception performing the action (hardware not available, etc.) # then the exception can be returned to the original action caller via: if completeHandler is not None: completeHandler.returnException(exc) ``` So, rather than return a value from the actual callback, you request a callback completion handler from the server. You then can queue up data, including the handler object, so that some other async thread can perform whatever communication, calculation, etc., is needed and use the completion handler to return the result. As shown in the example above, you can also return an exception which will be thrown in the requesting process if necessary. Note: To increase the value of your plugin, you should ensure that you test and document the necessary information so that Python scripters can [script your plugin's actions](../../../scripting/tutorial.md#scripting-indigo-plugins). You should include information on the parameters your action will expect to receive and what your action will return (if anything) when the action is called. Here is an example that shows how a scripter can tell [the Timers plugin to restart a timer](../../../plugins/timersandpesters.md#restart-timer). You can provide that information in a relatively straight-forward way in your plugin's documentation as we've done with our plugins (check the [Airfoil Pro](../../../plugins/airfoilpro.md) and [Timers and Pesters](../../../plugins/timersandpesters.md#scripting-support) docs for examples). You shouldn't really have to do much - but you should test each action. Sometimes you can make assumptions about the data that you get from your action's ConfigUI that you may want to change - for instance you may expect data when it would be advantageous to not include it (optional data) from a scripters perspective. --- Devices.xml (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/devices/) --- # Devices.xml { .ref-head-no-code #device-xml } One of the main components that a server plugin can define is a device. In Indigo, devices are the primary objects that users deal with. Lights, thermostats, sprinklers, I/O devices are all examples of device types. One of the biggest requests we get is to support the [name your favorite device]. Obviously, we can’t keep up, so the server plugin architecture was designed to allow you to add device support to Indigo. The root element in the Devices.xml file is Devices. Contained in the Devices element are an unlimited number of Device elements that define the device types that your plugin will define. Each Device will contain a ConfigUI element to collect the specific configuration information for a device (note that you don’t need to include name, description, or typeId from the user - Indigo will collect those for you). Here are the specific attributes and elements that can be defined in a Device element: ## Device Types { .ref-head #devices-xml-device-types } | Name | Type | Required | Notes | |------------------------------------------------|-----------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `type` | Attribute | Yes | This must be one of: `dimmer`, `relay`, `sensor`, `speedcontrol`, `thermostat`, or `custom`. We’ll discuss each below. | | `id` | Attribute | Yes | This is your plugins unique identifier for this device type. It must be unique in this plugin. | | `subType` | Attribute | No | This must be one of: `kDimmerDeviceSubType`, `kRelayDeviceSubType`, `kSensorDeviceSubType`. We’ll discuss each below. There are also several device `SubType` examples in the [SDK](https://github.com/IndigoDomotics/IndigoSDK/releases/tag/v2025.1). | | `allowUserCreation` | Attribute | No | If set, the value must either be `true` or `false` (defaults to `true`). This attribute is discussed in more detail below. | | `Name` | Element | Yes | This is the name of the device type that users will see when selecting the type in the Devices dialog. | | `ConfigUI` | Element | No | This is the custom UI for configuring the device. It’s optional, but in reality we can’t envision a device that needs no configuration. If the optional `` element is not used, the "Edit Device Settings…" button will be displayed, but disabled. You'll also need to manually set `dev.configured = True` and then `dev.replaceOnServer()` for the change to take effect. | | `States` | Element | No | This element is used to describe the possible states for the device. For custom devices it is all available states. For subclassed devices it will describe any additional states for the device. See the description below for more details. | ```xml Example Temperature Sensor Module Integer Temperature Temperature ``` ## Device Subtypes { .ref-head #devices-xml-device-subtypes } | Category | SubType | Attribute(s) | Notes | |----------|---------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------| | Device | `kDeviceSubType` | Amplifier, Automobile, Camera, Keypad, Mobile, Remote, Robot, Security, Speaker, Streaming, Television, Weather, Other | For example, `indigo.kDeviceSubType.Amplifier` | | Dimmer | `kDimmerDeviceSubType` | Blind, Bulb, ColorBulb, ColorDimmer, Dimmer, Fan, InLine, Outlet, Plugin, Value | For example, `indigo.kDimmerDeviceSubType.Dimmer` | | Relay | `kRelayDeviceSubType` | DoorBell, DoorController, GarageController, InLine, Lock, Outlet, Plugin, Siren, Switch | For example, `indigo.kRelayDeviceSubType.Switch` | | Sensor | `kSensorDeviceSubType` | Analog, Binary, CO, DoorWindow, GasLeak, GlassBreak, Humidity, Illuminance, Motion, Presence, Pressure, Smoke, Tamper, Temperature, UV, Vibration, Voltage, WaterLeak, Zone | For example, `indigo.kSensorDeviceSubType.Temperature` | !!! note There are no device subtypes for other built-in devices at this time. In `Devices.xml` ```xml Example Adjustable Sensor Module ... ``` ![Device Subtype Example Image](../../../images/device_sub_type_example.png) In Python: ```python newdev = indigo.device.create(indigo.kRelayDeviceSubType.Plugin, deviceTypeId="Device1") newdev.model = "Device 1 Model" newdev.subModel = "Sub Model 1" newdev.name = u"Device 1" ``` To get information on the device group: ```python indigo.device.getGroupList(dev.id) ``` When the `allowUserCreation` attribute is present and set to `false`, Indigo will not display the device model type when the user elects to create a new device (the "Model" dropdown will not contain the device type). This is especially useful for complex devices that have multiple subtypes that are set programmatically (but not created using the [Device Factory](#devices-xml-device-factory) approach). Your plugin may not support individual subtypes as stand-alone devices and, therefore, you wouldn't want users to be able to create them. The `allowUserCreation` attribute was introduced with Indigo version `2022.1.2` and API `3.1`. ```xml ``` Check out the [SDK](https://github.com/IndigoDomotics/IndigoSDK/releases/tag/v2025.1) for example plugins of each type of device listed above. The examples show config dialogs, required method stubs, etc., to implement the respective device types. !!! important Plugin developers should account for each devices' callbacks when implementing built-in device types -- even if you don't use all the callbacks within your plugin. For example, if your plugin implements the `Speed Control` device type, your plugin should include handlers for all the Speed Control callbacks -- such as `turnOn`, `turnOff`, or `toggle`. This is very important because these callbacks will still be exposed in the Indigo Client UI for things like Actions and Triggers. It's best to give users an indication that the callback won't do anything in your plugin by writing a warning to the Event log. Consult the [SDK](https://github.com/IndigoDomotics/IndigoSDK/releases/tag/v2025.1) for more information on each device types' callbacks and examples of how to handle them. ## Custom Device Type { .ref-head #devices-xml-custom-device} The `custom` device type is special because it’s completely custom. Therefore, you must specify everything about the device - it’s states and their types (`Number`, `String`, `List` (enumeration), `Boolean`). So, here’s a simple example of the `` element of a custom device, in this case a thermostat: ```xml Number Temperature Temperature Number Heat Setpoint Heat Setpoint Number Cool Setpoint Cool Setpoint Operation Mode Changed Mode Changed to Current Mode Mode is ``` Each State listed in the States element will be available in various parts of the UI, including the "Device State Changed" Event dialog. **Attributes** The State element has several attributes and elements that should be defined: | Attribute | Type | Required | Notes | |-----------------------------------------------------|-----------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `id` | Attribute | Yes | This is a unique identifier for this State within the context of this Device element. IDs must follow the XML standard in terms of construction with one addition: they **may not** include periods ('.') - periods are reserved for internal use only. | | `readonly` | Attribute | No | If you want this state to be read only, set this attribute to "YES". For instance, as in this example, the temperature the thermostat is sensing isn’t user settable. (Currently unused but it may be used in the future) | | `ValueType` | Element | Yes | This is the type of the state, which must be one of the following: `Boolean`, `Number`, `List` (enumeration constructed just like the `` element of a `` as described above), `String`, and `Separator` (used only to visually group your states in the various popups). The `` element may also have an optional attribute if the value is *`Boolean`//: `boolType` can be: `"TrueFalse"` (default), `"OnOff"`, `"YesNo"`, and `"OneZero"`. Example: *`Boolean`// | | `TriggerLabel` | Element | Yes | When selecting "Device State Changed" type trigger events, you can select your device in the resulting list. A popup below the device will list all of your States using this text. | | `TriggerLabelPrefix` | Element | No | When defining an enumeration, trigger change label is usually going to need to be a bit different than just the trigger label ("Mode Changed to Heat" vs "Operation Mode Changed" - the former is to test a specific change, the latter will trigger on any change). To accomplish this, you can specify a prefix that’s prepended to the actual state value (with a space in between). See the example above. | | `ControlPageLabel` | Element | Yes | In the Control Page Editor, when selecting "Device State" as the display type, then select your device, the next popup will show all your states using this text. | | `ControlPageLabelPrefix` | Element | No | When defining an enumeration, the control page label is usually going to need to be a bit different ("Mode is Heat" vs "Current Mode" - the former will show whether a the state is true, the latter shows the state value). You can specify a prefix that’s prepended to the actual state value (with a space in between). See the example above. | The ValueType element is particularly important - it controls what types of controls are presented to the user in the UI: an integer tells it to show greater than, less than, equal to, not equal to, etc. and a way to enter a number (in the case of a trigger). See the [States](../../../scripting/reference/devices/base-class.md#about-custom-device-states) section of the Devices Class page for details on how to set states once the IndigoServer understands the structure of your device's states. ## Device Factory { .ref-head #devices-xml-device-factory } ![Device Factory Edit Device Group Image](../../../images/screenshot_2023-02-22_at_1.07.12_pm.png) One way to create "multifunction" devices is to use Indigo's Device Factory method. Device Factory devices are defined by using a special `` node in `Devices.xml`. A basic Device Factory device definition would look something like this: ```xml Device Factory Plugin Device Group Create ``` Note that when the `` node is present, devices that include the `subType` attribute will not be presented to the user as individual device types when they elect to create a new plugin device (it's not possible to have a Device Factory implementation and individual plugin device definitions together). You define sub-devices like any other device type, with the addition of the `subType` attribute: ```xml Plugin Device Group Create Example Dimmer Module String State State Example Adjustable Sensor Module String State State Example Lock Module String State State ``` Once the device definitions have been established, you can use your plugin code to create the device group. This function can be called from the `validate_device_factory_ui()`or `closed_device_factory_ui()` methods. ```python def create_the_device_group(self, my_name): """ Convenience method for device creation. This could also be done in closed_device_factory_ui() for example. Note that some device props are read only even when you create them from scratch (i.e., dev.version) and some will be ignored if you try to set them (i.e., dev.description, dev.errorState). Note that `protocol`, `name` and `deviceTypeId` are all required with the `indigo.device.create()` method call. """ self.debugLog("create_the_device_group called") # Create the first device in the group. Note that the ''deviceTypeId'' value matches the ''id'' we used in our ''Devices.xml'' definition. new_dev = indigo.device.create(protocol=indigo.kProtocol.Plugin, name=my_name, deviceTypeId="my_dimmer_device") new_dev.model = "Grouped Device" new_dev.subModel = "Dimmer" new_dev.name = f"{my_name} Dimmer" new_dev.replaceOnServer() # Add the group my_name setting to the props of device 1 for later use. new_props = new_dev.pluginProps new_props['name'] = my_name new_dev.replacePluginPropsOnServer(new_props) # Create the second device in the group. You can also add a state value here if desired. new_dev = indigo.device.create(protocol=indigo.kProtocol.Plugin, name=my_name, deviceTypeId="my_sensor_device") new_dev.model = "Grouped Device" new_dev.subModel = "Temperature" new_dev.name = f"{my_name} Temperature" new_dev.replaceOnServer() new_dev.updateStateOnServer('state', value="Some value.") # You can also create devices that don't have corresponding device parameters established in Devices.xml. new_dev = indigo.device.create(protocol=indigo.kProtocol.Plugin, name=my_name, deviceTypeId="my_lock_device") new_dev.model = "Grouped Device" new_dev.subModel = "Lock" new_dev.name = f"{my_name} Lock" new_dev.replaceOnServer() ``` !!! important You are responsible for catching all the mandatory methods for the devices you create. The Device Factory method does not create them for you. For another option to create Device Factory devices, check out the example in the [Indigo SDK](https://github.com/IndigoDomotics/IndigoSDK). --- Events.xml (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/events/) --- # Events.xml { .ref-head-no-code #events-xml } The XML in this file describes all events that your plugin will generate for use in Indigo. Your users will use these in the Trigger Events dialog just like any of the built-in Indigo events (like Power Failure, Email Received, etc.) Device State Changed events are handled by the `` defined in the `` elements described above, but your plugin can offer other types of events, including update notifications, battery low notifications, button press notifications, etc. Here’s a very small `Events.xml` file that just defines a plugin update event: ```xml http://www.yourdomain.com/plugin/pluginEvents.html Plugin Update Available ``` As you can see, your `` elements can define a `` element as well - the trigger events dialog now has a help button on it and if one of your events is selected clicking on the help button will take your user to the specified URL. If you don’t specify one then the default help page for all trigger events will show. You can specify an Event to be a separator in your event list so that when they're displayed in the UI there is a visual separation. Simply insert an Event defined like this between two other Event elements: `` **Attributes** While you don't have to include any elements, the id still must be unique. Here’s how to construct your `` elements: | Attribute | Type | Required | Notes | |---------------------------------------|-----------|----------|-------------------------------------------------------------------------------------------------------------------| | `id` | Attribute | Yes | This is a unique id for the event in this `Events.xml` file. | | `Name` | Element | Yes | This is the text that’s shown in the trigger event dialog that represents this event. | | `ConfigUI` | Element | No | If your event requires any configuration, you can specify a `` element that’s defined exactly as above. | --- MenuItems.xml (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/menuitems/) --- # MenuItems.xml { .ref-head-no-code #menuitems-xml } Your plugin may also define menu items, which will be shown at the bottom of your plugin’s sub-menu on the `Plugins` menu - they will be the last thing in the menu unless you also include scripts in the `Menu Items` folder of your plugin’s bundle. If there are scripts there, they will be last, and a separator will be placed between the menu items defined in this XML file and any script files. Here’s a sample MenuItems.xml file: ```xml Reset Interface resetInterface Check for Updates checkForUpdates Turn Off Light... turnOffLight Turn Off Execute Some Action ``` It’s pretty straight-forward. **Attributes** The `MenuItems` element will contain multiple `` elements. Here’s how to construct a ``: | Attribute | Type | Required | API Version | Notes | | |---------------------------------------------|-----------|----------|-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--| | `id` | Attribute | Yes | 1.0 | This is a unique id for the menu item in this file. | | | `Name` | Element | Yes | 1.0 | This is the text that’s shown in your plugin’s sub-menu. | | | `CallbackMethod` | Element | No | 1.0 (1.1) | This is the name of the method defined in your plugin that will be called when your user selects this menu item.

API v1.1: If you specify a for the menu, then this property is optional. If it's present, then the the method will be called with arguments much like a validation (see example below). If it's not present (in which case a ConfigUI must be present), then the dialog will contain only a single "Close" button. In that case the functionality in the dialog will be completely left up to buttons contained within the dialog. | | | `ConfigUI` | Element | No | 1.1 (1.2) | If you want a menu to open an associated dialog, you can include a standard ConfigUI element (see [Configuration Dialogs](configui/index.md#plugin-config) above for details).

API v1.2: You may also specify an actionId attribute on this element with the id of one of your actions and that action UI will be executed rather than having to specify the UI itself. If it requires a device ID that will be added at the top of the dialog. Ex: `` | | `ButtonTitle` | Element | No | 1.1 | If you have a ConfigUI element and a CallbackMethod, you can add this element to specify the title used on the button that executes the dialog. By default it's "Execute". | | Here's an example of a menu item method definition in your plugin.py file for the "Check for Updates" menu item above: ```python def checkForUpdates(self): indigo.server.log(u"checkForUpdates called") # Do the actual work here to see if there are any updates ``` As of API v1.1 you can have your menu item open a dialog which will allow you to gather input from a user before executing the action. Just add a `` element to your MenuItem definition and define that dialog just like any other. There are a couple of differences between this dialog and other dialogs. First, the `CallbackMethod` for menu items will be used rather than a validation method. It will, however, operate in much the same way. If you specify a `CallbackMethod` then it will get called with the valuesDict and the menu item's ID. You can return `True`, which will cause the dialog to close, or return false with an error dictionary which will mark the invalid fields just like the validation method would. You also have the option of adding a `ButtonTitle` element - which will allow you to change the name of the button (it defaults to `Execute`). If you don't specify a `CallbackMethod` then the dialog will not have `Execute` and `Cancel` buttons but rather a single `Close` button. Why? We wanted to allow you the flexibility of making a dialog perform multiple actions if you like using button field types within the dialog itself. When doing that it would be awkward to have an `Execute` button that didn't really do anything. Whenever a user executes a menu item that contains a configuration dialog, it’s running as if it’s the first time the menu item is run (default values will always be used if present). Any values the user enters will be discarded after the menu item action is run. In other words, the next time the dialog is opened, default values will be used even if a user changed that value the last time the menu item was run. If a field doesn't specify a default value, the field will be empty each time the dialog is opened. Here's an example of a menu item method definition in your plugin.py file for the "Turn Off Light" menu item above: ```python def turnOffLight(self, valuesDict, typeId): indigo.server.log(f"Turning off light: {valuesDict["targetDevice"]}") # perform the action here. If there are errors in any of the fields then # you can return (False, valuesDict, errorsDict) just like a validation # method and the dialog won't close and will show the errors. If you # return True then the dialog will close when the method completes. errorsDict = indigo.Dict() return (True, valuesDict, errorsDict) ``` **Get Menu Action Config UI Values** As noted above, a menu item config dialog will open as if it's being opened for the first time. However, if you would like values entered into the dialog to be persistent, you can load those values using the built-in `get_menu_action_config_ui_values` callback. Your plugin is responsible for storing and retrieving the values for the dialog yourself. How you store those values is up to you (you could save them to a file or store them in a hidden plugin configuration field, for example). Then, you can load them into the config dialog using `get_menu_action_config_ui_values` which will be called automatically if it exists: `get_menu_action_config_ui_values()` ```python def get_menu_action_config_ui_values(self, menu_id): menu_items = indigo.Dict() if menu_id == "my_menu_id": menu_items['foobar'] = "my stored value" return menu_items ``` This method will be called whenever a menu item with a config dialog is called, so be sure to apply values using the proper menu id. **Custom HTML Menu Item Dialogs** You can also implement your own custom menu item form in HTML if you prefer. Rather than adding `` and `` definitions, you simply specify a `` 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 [best base URL](../../../scripting/reference/server-commands.md#get-web-server-url). You would then handle those form requests using [the built-in request handling mechanism discussed below](../plugin-py/http-requests.md#processing-http-requests-in-your-plugin). See the **Example HTTP Responder** plugin in the [SDK](https://github.com/IndigoDomotics/IndigoSDK/releases/tag/v2025.1) for an example. --- PluginConfig.xml (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/pluginconfig/) --- # PluginConfig.xml { .ref-head-no-code #plugin-config } We started the discussion of the Indigo XML by describing the `ConfigUI` element that’s present for `Devices`, `Events`, and `Actions`. But, how do you configure your plugin itself? For instance, let’s say your plugin requires a connection to some hardware interface, say something akin to the Insteon PowerLinc interface. How do you specify which serial port that interface is connected to? The simple answer is that, just as there is a configuration interface for an Insteon hardware adaptor, your plugin has a configuration interface as well. The `PluginConfig.xml` file describes the UI to configure your plugin. The root element in that file is `PluginConfig`, and it contains exactly the same elements that the `ConfigUI` element described above does. Here’s an example: ```xml http://www.yourdomain.com/plugin/config.html [SNIP - lots of definitions] ``` The IPH will handle retrieving saved preferences and passing them to your plugin as well as saving changed preferences to disk. Your preferences will be stored in a file in this directory: `/Library/Application Support/Perceptive Automation/Indigo {{ version }}/Preferences/Plugins/` and it will be named by using your plugin’s ID (as defined by the `CFBundleIdentifier` in the `Info.plist` file). So, using the example `Info.plist` at the beginning of this doc, the pref file would be named: `com.yourdomain.plugin.indiPref` We write each plugin’s preferences into individual files so that it will be easier for you to help your users troubleshoot problems by deleting the prefs and starting over. We also write them into the standard `Preferences` folder so that when you upgrade your plugin -- or we upgrade Indigo -- the preferences won’t get lost. ## 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 `` definitions, you simply specify a `` 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 [best base URL](../../../scripting/reference/server-commands.md#get-web-server-url). You would then handle those form requests using [the built-in request handling mechanism discussed below](../plugin-py/http-requests.md#processing-http-requests-in-your-plugin). See the **Example HTTP Responder** plugin in the [SDK](https://github.com/IndigoDomotics/IndigoSDK/releases/tag/v2025.1) for an example. --- SupportURL Elements (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/supporturl/) --- # SupportURL Elements { .ref-head-no-code #support-url-elements} Anywhere you can specify a `` element, the value can be either a full URL or a relative URL. If it's relative, then Indigo will attempt to guess the [best base URL](../../../scripting/reference/server-commands.md#get-web-server-url). You can use this to supply [static HTML](../../guide.md#resources-folder) files or dynamic help provided through the [HTTP processing API discussed below](../plugin-py/http-requests.md#processing-http-requests-in-your-plugin). **Full URL** ```xml https://www.somesite.com ``` **Relative URL** ```xml /some/relative/path ``` --- Configuration Dialogs (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/configui/) --- # Configuration Dialogs { .ref-head-no-code #plugin-config } The Indigo plugin XML contains some structures that are used in the various component XML files. `Devices`, `Events`, `Actions`, and the `PluginConfig` each may describe some type of user interface, so we needed a simple description language that described the user interface elements and layout. So, for the first 3, we created an XML element called a `ConfigUI` (we’ll discuss the `PluginConfig` a bit later). Here’s an example for a device: ```xml http://www.yourdomain.com/plugin/ApplianceModule.html Find Node ID validPythonMethodName (Not Recommended) ``` The ConfigUI definition will result in the following dialog being presented to the user: ![Configuration UI Rendering Image](../../../../images/configuirendering.png) 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. The rest of the elements in the ConfigUI represent fields that are shown in the dialog. We’ll go through each field type, starting with a screenshot of how it’s rendered, then the XML, and finally the details for each. The order in which you specify the fields is the order that they will show up in the dialog, and the `id` attribute is required for every field and must be unique within the dialog - it’s used to establish enabled and visible bindings between fields and more importantly it’s the key used in the dictionary to get the values when you get messages from the dialog (discussed more later). The `id` must be alphanumeric (and may contain underscores) and must begin with an alphabetic character. You'll have the opportunity to validate the fields before they're saved (as well as know when the user cancels out of a dialog). How that's done is discussed at the end of this section. ## ConfigUI Field Types Each control you can place in a plugin configuration dialog: - [Button](button.md) · [Checkbox](checkbox.md) · [Color Picker](color-picker.md) · [Label](label.md) · [List](list.md) · [Popup Menu](popup-menu.md) - [Separator](separator.md) · [Serial Port](serial-port.md) · [Text Field](text-field.md) - [Dynamic Lists and Filters](dynamic-lists.md) — runtime-populated menus and lists. - [Validation Methods](validation.md) — validating dialog input. - [Dialog Close Notification](dialog-close.md) — reacting to dialog close. --- Button (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/configui/button/) --- # Button { .ref-head-no-code #config-button } Button fields allow your user to communicate with your plugin during configuration. The following table describes the attributes that are available and the XML elements that are required for button fields. ![Configuration UI Button Image](../../../../images/configui_button.png) **Attributes** | Attribute | Required | API Version | Notes | |----------------------------------------------------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `alwaysUseInDialogHeightCalc` | No | 1.0 | For dialogs that contain hidden controls (see `visibleBindingId` and `visibleBindingValue` below), setting this attribute to `true` will force Indigo to take hidden controls into account when initially sizing the dialog box. This is useful when hidden controls become visible based on user input. | | `enabledBindingId` | No | 1.0 (1.9) | If you want to conditionally enable/disable this field based on the value of a checkbox, set this value to the id of that field.

API v1.9: You can also bind to a list field and this field will only be enabled if something is selected in the list. | | `enabledBindingNegate` | No | 1.9 | Set this to "true" to negate the binding. So if the target binding field's value is true then the binding will be false. | | `id` | Yes | 1.0 | This is a unique identifier for this Field within the context of this `ConfigUI` element. | | `type` | Yes | 1.0 | This must be `button`. | | `visibleBindingId` | No | 1.0 | If you want to conditionally show/hide this field based on the value of another field, set this value to the `id` of another field. If you set this, you must also include a `visibleBindingValue` | | `visibleBindingValue` | No | 1.0 | If you specify a `visibleBindingId`, you must specify the value(s) here. For instance, if you are binding to a checkbox, setting this to `false` will mean the menu field is visible only when the checkbox is unchecked, and a `true` means the opposite. To make the field dependent on another list or menu, set this value to a comma separated list of option values (`item1, item2`). You can even bind it to the value in a text field, but that’s probably of limited use. **Note**: the string comparison is a contains - so if any option contains the specified string it will match. This offers some extra flexibility in defining dependency groups. | | Element | | | | | `CallbackMethod` | Yes | 1.0 | The `CallbackMethod` is the plugin method that you want to execute when the button is pressed. Several elements will be passed to the method when it's called: the dialog's configuration values (`valuesDict`), the type of device (`typeId`), and the device ID (`devId`). The 'CallbackMethod` field type is read-only, which means that you can't have an error message attached to it from a validation method or a button method (see below for details). | | `title` | Yes | 1.0 | The `title` is the text that will appear on the button itself. | ```xml Visible Button Title ConfigButtonPressed ``` As an example, if your plugin needs to start listening for a specific network broadcast packet that a device sends when it’s in a special discover mode, then you probably only want to listen for that packet when the user actually makes the device start to broadcast. So, as in the example device dialog above, you instruct the user to press a button on the device, then have them click the button. This would give your plugin an opportunity to do something while the configuration dialog was up and return the results to the dialog. The process flow is: 1. The user clicks the button. 1. A dictionary containing all the values of the fields in the dialog are sent to the method that you identify in the `` element. 1. Your plugin can then do whatever it needs to, and it returns a dictionary back to the dialog containing any field changes. 1. The dialog will update the values (and enable/visible bindings appropriately) for any changed fields. Each button's method signature will match the parameters that are used in the [validation methods](validation.md#validation-methods) described below. For instance, if you specify a method called "beginPairing" in a button field of the `` XML for a device like this: ```xml Music Server Begin Pairing beginPairing ``` The method for that event in your `plugin.py` file would look something like this: ```python def beginPairing(self, valuesDict, typeId, devId): # do whatever you need to here # typeId is the device type specified in the Devices.xml # devId is the device ID - 0 if it's a new device return valuesDict ``` Note that the parameters match those specified below for the `validateDeviceConfigUi` method. [Back to Configuration Dialogs](index.md) --- Checkbox (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/configui/checkbox/) --- # Checkbox { .ref-head-no-code #config-checkbox } Checkbox fields allow for binary selections (yes/no, true/false, etc.) The following table describes the attributes that are available for checkbox fields. ![Configuration UI Checkbox Image](../../../../images/configui_checkbox.png) **Attributes** | Attribute | Required | API Version | Notes | |----------------------------------------------------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `alwaysUseInDialogHeightCalc` | No | 1.0 | For dialogs that contain hidden controls (see `visibleBindingId` and `visibleBindingValue` below), setting this attribute to `true` will force Indigo to take hidden controls into account when initially sizing the dialog box. This is useful when hidden controls become visible based on user input. | | `defaultValue` | No | 1.0 | You can enter a default value here. The value must be either `true` or `false`. | | `enabledBindingId` | No | 1.0 (1.9) | If you want to conditionally enable/disable this field based on the value of a checkbox, set this value to the id of that field.

API v1.9: You can also bind to a list field and this field will only be enabled if something is selected in the list. | | `enabledBindingNegate` | No | 1.9 | Set this to "true" to negate the binding. So if the target binding field’s value is true then the binding will be false. | | `hidden` | No | 1.0 | If this attribute is set to `true` then the field will never be displayed regardless of what other options you have set for it. It’s useful if you need to contain some kind of state variables for the dialog that are controlled by button presses rather than controlled directly by the user. | | `id` | Yes | 1.0 | This is a unique identifier for this Field within the context of this `ConfigUI` element. | | `readonly` | No | 1.0 | This attribute will make the field readonly - useful to show the user data that changes in some other way rather than being manipulated directly by the user. | | `tooltip` | No | 1.0 | The tooltip will be shown when you hover over the actual list control. | | `type` | Yes | 1.0 | This must be "checkbox". | | `visibleBindingId` | No | 1.0 | If you want to conditionally show/hide this field based on the value of another field, set this value to the `id` of another field. If you set this, you must also include a `visibleBindingValue` | | `visibleBindingValue` | No | 1.0 | If you specify a `visibleBindingId`, you must specify the value(s) here. For instance, if you are binding to a checkbox, setting this to `false` will mean the menu field is visible only when the checkbox is unchecked, and a `true` means the opposite. To make the field dependent on another list or menu, set this value to a comma separated list of option values (`item1, item2`). You can even bind it to the value in a text field, but that’s probably of limited use. **Note**: the string comparison is a contains - so if any option contains the specified string it will match. This offers some extra flexibility in defining dependency groups. | ```xml What’s on the right side of the checkbox ``` Checkboxes contain the standard required Label element. They also contain an optional element, `Description`, that’s used to deliver extra information on the right side of the checkbox. Many checkboxes may not need this extra information. Like [button fields](button.md), you can specify a `CallbackMethod` element in your field definition: ```xml checkboxChanged ``` This method will be called every time the user clicks the checkbox. The method in your plugin.py file should look something like this: ```python def checkboxChanged(self, valuesDict, typeId, devId): # do whatever you need to here # typeId is the device type specified in the Devices.xml # devId is the device ID - 0 if it's a new device return valuesDict ``` [Back to Configuration Dialogs](index.md) --- Color Picker (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/configui/color-picker/) --- # Color Picker { .ref-head-no-code #config-color-picker } 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 Picker](../../../../images/color_picker.png){ width=200 } **Attributes** | Attribute | Required | API Version | Notes | |----------------------------------------------------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `alwaysUseInDialogHeightCalc` | No | 1.0 | For dialogs that contain hidden controls (see `visibleBindingId` and `visibleBindingValue` below), setting this attribute to `true` will force Indigo to take hidden controls into account when initially sizing the dialog box. This is useful when hidden controls become visible based on user input. | | `enabledBindingId` | No | 1.0 (1.9) | If you want to conditionally enable/disable this field based on the value of a checkbox, set this value to the id of that field.

API v1.9: You can also bind to a list field and this field will only be enabled if something is selected in the list. | | `enabledBindingNegate` | No | 1.9 | Set this to "true" to negate the binding. So if the target binding field’s value is true then the binding will be false. | | `id` | Yes | 1.0 | This is a unique identifier for this Field within the context of this `ConfigUI` element. | | `type` | Yes | 2.0 | This must be `colorpicker`. | | `visibleBindingId` | No | 1.0 | If you want to conditionally show/hide this field based on the value of another field, set this value to the `id` of another field. If you set this, you must also include a `visibleBindingValue` | | `visibleBindingValue` | No | 1.0 | If you specify a `visibleBindingId`, you must specify the value(s) here. For instance, if you are binding to a checkbox, setting this to `false` will mean the menu field is visible only when the checkbox is unchecked, and a `true` means the opposite. To make the field dependent on another list or menu, set this value to a comma separated list of option values (`item1, item2`). You can even bind it to the value in a text field, but that’s probably of limited use. **Note**: the string comparison is a contains - so if any option contains the specified string it will match. This offers some extra flexibility in defining dependency groups. | ```xml ColorPickerPressed ``` ![Color Selector](../../../../images/color_selector.png){ width=200 } 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`. ```text UiValuesDict : (dict) chosenColor : 80 00 FF (string) instructions : (string) ``` Aside from the standard `Label` element, the colorPicker field also contains one other required element--the `CallbackMethod` element--which contains the name of a Python method in your code that’s called when the user executes your configuration dialog. [Back to Configuration Dialogs](index.md) --- Dialog Close Notification (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/configui/dialog-close/) --- # Dialog Close Notification { .ref-head-no-code #dialog-close-notification } So you know that when the user clicks the "Save" button in the dialog, your validation method will get called (if it exists). But, what if your dialog starts some separate thread (perhaps as a result of a button click), then the user clicks the "Cancel" button? You still need to know to stop the thread that's doing something, right? There is another, final method that's called at the very end of the dialog's lifecycle. It's very similar to the validation method, but with an additional parameter that tells you whether the user canceled the dialog. Here are the various closed notification methods: **Dialog Types** | Dialog Type | Method Signature | |--------------|------------------------------------------------------------------------------------------------------| | device | `closedDeviceConfigUi(self, valuesDict, userCancelled, typeId, devId)` | | event | `closedEventConfigUi(self, valuesDict, userCancelled, typeId, eventId)` | | action | `closedActionConfigUi(self, valuesDict, userCancelled, typeId, actionId)` | | PluginConfig | `closedPrefsConfigUi(self, valuesDict, userCancelled)` | The parameters are exactly the same as in the [validation methods](validation.md#validation-methods) with the addition of `userCancelled` - which is a boolean. We're sure there are other reasons why one would need to know when the dialog closes so we wanted to make sure that the complete lifecycle of the dialog was exposed to you. !!! note The parameters in this instance are all read-only. If you need to modify the valuesDict, for instance, you need to do it in the validate UI (which is fine since a cancel should never change anything). --- Dynamic Lists & Filters (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/configui/dynamic-lists/) --- # Dynamic Lists and Filters { .ref-head-no-code #config-dynamic-list } 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: ```xml ``` 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). | **Device Filters** The `indigo.devices` class specifies that the list will contain Indigo devices. If you don’t specify a filter, all devices defined by the user in Indigo will be returned. You can supply up to two filters if and only if one is an interface filter (see the first three in the list below). The filters are ANDed together, so one interface filter and one device type filter are the only combinations that will result in a non-empty list. | Class: `indigo.devices` | | |-------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------| | Optional Filters | Description | | `indigo.zwave` | include Z-Wave devices - this is an interface filter that can be used with other filters | | `indigo.insteon` | include Insteon devices - this is an interface filter that can be used with other filters | | `indigo.x10` | include X10 devices - this is an interface filter that can be used with other filters | | `indigo.responder` | include devices whose state can be changed | | `indigo.controller` | include devices that can send commands | | `indigo.relay` | relay devices | | `indigo.dimmer` | dimmer devices | | `indigo.sprinkler` | sprinklers | | `indigo.thermostat` | thermostats | | `indigo.iodevice` | input/output devices | | `indigo.sensor` | all sensor type devices: motion sensors, TriggerLinc, SynchroLinc (sensor devices that have a virtual state in Indigo) | | `self` | all device types defined by the calling plugin | | `self.devTypeId` | all devices of type deviceTypeId, where deviceTypeId is one of the device types specified by the calling plugin | | `com.somePlugin` | all device types defined by some other plugin | | `com.somePlugin.devTypeId` | all devices of type deviceTypeId, where deviceTypeId is one of the device types specified in some other plugin | The `indigo.triggers` class specifies that the list will contain Indigo triggers. Only a single filter from the list below will return a useful list. **Trigger Filters** | Class: `indigo.triggers` | | |----------------------------------------------------|----------------------------------------------------------------------------------| | Optional Filters | Description | | `indigo.insteonCmdRcvd` | Insteon command received triggers | | `indigo.x10CmdRcvd` | x10 command received triggers | | `indigo.devStateChange` | device state changed triggers | | `indigo.varValueChange` | variable changed triggers | | `indigo.serverStartup` | startup triggers | | `indigo.powerFailure` | power failure triggers | | `indigo.interfaceFail` | interface failure triggers - can be used with or without a specified protocol | | `indigo.interfaceInit` | interface connection triggers - can be used with or without a specified protocol | | `indigo.emailRcvd` | email received triggers | Using the `indigo.schedules` class will result in a list of all schedules, as will `indigo.actionGroups` and `indigo.controlPages` for their respective object types. None of these classes support any kind of filter. Lastly, `indigo.variables` will get you a list of variables. If you include `filter="indigo.readWrite"`, you’ll get only variables for which you can change the value. Currently, there’s only one read-only variable (isDaylight) defined by the system, but it may be possible to create them 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 plugin (e.g. "/dev/tty*"). This is suitable for using with PySerial, which we include with Indigo. 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: ```xml ``` Then your plugin will have the method specified called with the filter. For the above example, you must define a method like this: ```python 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](validation.md#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: ```xml ``` 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 a one that's dynamically reloaded: ```xml ``` !!! 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. [Back to Configuration Dialogs](index.md) --- Label (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/configui/label/) --- # Label { .ref-head-no-code #config-label } Label fields are used to present text that span the width of the dialog. As you may have noticed, each control has an element called `Label` (except for `Separator`, discussed next). When the dialog is displayed, each of those Label elements is right aligned and the actual control is left aligned directly to the right of the label. This field gives you a way to communicate a much longer chunk of text - like instructions, etc. It will actually span the entire width of the dialog and will wrap as necessary. The following table describes the attributes that are available for label fields. ![Configuration UI Label Image](../../../../images/configui_label.png) **Attributes** | Attribute | Required | API Version | Notes | |----------------------------------------------------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `alignText` | No | 1.4 | This will control how the label text is justified. Valid options are: "left" (default), "center", and "right". | | `alignWithControl` | No | 1.4 | If set to "true", this will align the left margin of the label with the left margin for the actual controls (not their labels). Anything other than "true" will be considered "false" (which is also the default if not specified). This is very useful in conjunction with specifying font characteristics described below when you want to add some help specific to a control - just insert the label right below the control field and it will align with the actual control above. | | `alwaysUseInDialogHeightCalc` | No | 1.0 | For dialogs that contain hidden controls (see `visibleBindingId` and `visibleBindingValue` below), setting this attribute to `true` will force Indigo to take hidden controls into account when initially sizing the dialog box. This is useful when hidden controls become visible based on user input. | | `enabledBindingId` | No | 1.0 (1.9) | If you want to conditionally enable/disable this field based on the value of a checkbox, set this value to the id of that field.

API v1.9: You can also bind to a list field and this field will only be enabled if something is selected in the list. | | `enabledBindingNegate` | No | 1.9 | Set this to "true" to negate the binding. So if the target binding field’s value is true then the binding will be false. | | `fontColor` | No | 1.4 | Use this attribute to control the color of the label text. Valid options are: "black (default), "darkgray", "red", "orange", "green", and "blue". | | `fontSize` | No | 1.4 | Use this attribute to specify the size of the font. Valid options are: "regular" (default, same size as all other text in the dialog), "small, which is a point or two smaller, and "mini which is yet another point size or two smaller. | | `id` | Yes | 1.0 | This is a unique identifier for this Field within the context of this `ConfigUI` element. | | `type` | Yes | 1.0 | This must be `label`. | | `visibleBindingId` | No | 1.0 | If you want to conditionally show/hide this field based on the value of another field, set this value to the `id` of another field. If you set this, you must also include a `visibleBindingValue` | | `visibleBindingValue` | No | 1.0 | If you specify a `visibleBindingId`, you must specify the value(s) here. For instance, if you are binding to a checkbox, setting this to `false` will mean the menu field is visible only when the checkbox is unchecked, and a `true` means the opposite. To make the field dependent on another list or menu, set this value to a comma separated list of option values (`item1, item2`). You can even bind it to the value in a text field, but that’s probably of limited use. **Note**: the string comparison is a contains - so if any option contains the specified string it will match. This offers some extra flexibility in defining dependency groups. | | Element | | | | | Label | Yes | 1.0 | Labels have a single element, `Label`, that holds the text to be displayed. **Note**: in a label field type, the `Label` element is required. This field type is read-only, which means that you can't have an error message attached to it from a validation method or a button method (see below for details). | ```xml ``` [Back to Configuration Dialogs](index.md) --- List (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/configui/list/) --- # List { .ref-head-no-code #config-list } List fields allow for the selection of 0-N items with no implicit hierarchy. The following table describes the attributes that are available for list fields. ![Configuration UI List Image](../../../../images/configui_list.png) **Attributes** | Attribute | Required | API Version | Notes | |----------------------------------------------------------|----------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `alwaysUseInDialogHeightCalc` | No | 1.0 | For dialogs that contain hidden controls (see `visibleBindingId` and `visibleBindingValue` below), setting this attribute to `true` will force Indigo to take hidden controls into account when initially sizing the dialog box. This is useful when hidden controls become visible based on user input. | | `defaultValue` | No | 1.0 | You can enter a default value here. The value must be a comma separated list of values, each value must be listed as an option in the `List` element. | | `enabledBindingId` | No | 1.0 (1.9) | If you want to conditionally enable/disable this field based on the value of a checkbox, set this value to the id of that field.

API v1.9: You can also bind to a list field and this field will only be enabled if something is selected in the list. | | `enabledBindingNegate` | No | 1.9 | Set this to "true" to negate the binding. So if the target binding field’s value is true then the binding will be false. | | `hidden` | No | 1.0 | If this attribute is set to `true` then the field will never be displayed regardless of what other options you have set for it. It’s useful if you need to contain some kind of state variables for the dialog that are controlled by button presses rather than controlled directly by the user. | | `id` | Yes | 1.0 | This is a unique identifier for this `Field` within the context of this `ConfigUI` element. It must be alphanumeric (and may contain underscores) and must begin with an alphabetic character. | | `readonly` | No | 1.0 | This attribute will make the field readonly - useful to show the user data that changes in some other way rather than being manipulated directly by the user. | | `rows` | No | 1.4 | Use this attribute to specify the number of rows that will show in the list. The minimum (and default) is 4. | | `tooltip` | No | 1.0 | The tooltip will be shown when you hover over the actual list control. | | `type` | Yes | 1.0 | This must be `list`. | | `visibleBindingId` | No | 1.0 | If you want to conditionally show/hide this field based on the value of another field, set this value to the `id` of another field. If you set this, you must also include a `visibleBindingValue` | | `visibleBindingValue` | No | 1.0 | If you specify a `visibleBindingId`, you must specify the value(s) here. For instance, if you are binding to a checkbox, setting this to `false` will mean the menu field is visible only when the checkbox is unchecked, and a `true` means the opposite. To make the field dependent on another list or menu, set this value to a comma separated list of `Option` values (`item1, item2`). You can even bind it to the value in a text field, but that’s probably of limited use. **Note**: the string comparison is a contains - so if any option contains the specified string it will match. This offers some extra flexibility in defining dependency groups. | ```xml ``` Static list fields are constructed almost identically to static popup menu fields - with a `Label` element and a `List` element which contains multiple `Option` elements. Each `Option` element must have a `value` attribute and that attribute is what will be passed back to your plugin when the dialog is validated; it may not contain comma or semicolon characters. The text inside the `Option` element is what is displayed for each line in the user interface. As with `menu` fields, you may also specify a list dynamically at runtime - that is, when the UI is created, the client will call into your plugin to get the list items. This will allow you to dynamically adjust the list items every time the UI comes up. You can also specify some built-in dynamic items that the IndigoServer will provide automatically for you. See the [Dynamic Lists](dynamic-lists.md) section below for details. [Back to Configuration Dialogs](index.md) --- Popup Menu (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/configui/popup-menu/) --- # Popup Menu { .ref-head-no-code #config-pop-up-menu } Popup menu fields are used to select a single fixed value from a list with no inherent hierarchy. The following table describes the attributes that are available for menu fields. ![Configuration UI Menu Image](../../../../images/configui_menu.png) **Attributes** | Attribute | Required | API Version | Notes | |----------------------------------------------------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `alwaysUseInDialogHeightCalc` | No | 1.0 | For dialogs that contain hidden controls (see `visibleBindingId` and `visibleBindingValue` below), setting this attribute to `true` will force Indigo to take hidden controls into account when initially sizing the dialog box. This is useful when hidden controls become visible based on user input. | | `defaultValue` | No | 1.0 (1.9) | You can enter a default value here. The value must be one of the values from the List element described below.

API v1.9: If you specify a default value of "" (empty string) and there is no matching option, then the first menu item will be selected when the dialog is first run. This way you can avoid the user seeing "- no selection -" at the bottom of the menu. | | `enabledBindingId` | No | 1.0 (1.9) | If you want to conditionally enable/disable this field based on the value of a checkbox, set this value to the id of that field.

API v1.9: You can also bind to a list field and this field will only be enabled if something is selected in the list. | | `enabledBindingNegate` | No | 1.9 | Set this to "true" to negate the binding. So if the target binding field’s value is true then the binding will be false. | | `hidden` | No | 1.0 | If this attribute is set to "true" then the field will never be displayed regardless of what other options you have set for it. It’s useful if you need to contain some kind of state variables for the dialog that are controlled by button presses rather than controlled directly by the user. | | `id` | Yes | 1.0 | This is a unique identifier for this Field within the context of this ConfigUI element. It must be alphanumeric (and may contain underscores) and must begin with an alphabetic character. | | `readonly` | No | 1.0 | This attribute will make the field readonly - useful to show the user data that changes in some other way rather than being manipulated directly by the user. | | `tooltip` | No | 1.0 | Unfortunately, enabled menus won’t show tooltips (Cocoa limitation), so your tooltip should probably tell the user why it’s disabled (since that’s the only time it’ll show) if the field is ever disabled. | | `type` | Yes | 1.0 | This must be "menu". | | `visibleBindingId` | No | 1.0 | If you want to conditionally show/hide this field based on the value of another field, set this value to the id of another field. If you set this, you must also include a visibleBindingValue | | `visibleBindingValue` | No | 1.0 | If you specify a visibleBindingId, you must specify the value(s) here. For instance, if you are binding to a checkbox, setting this to `false` will mean the menu field is visible only when the checkbox is unchecked, and a `true` means the opposite. To make the field dependent on a list or another menu, set this value to a comma separated list of option values ("item1, item2"). You can even bind it to the value in a text field, but that’s probably of limited use. **Note**: the string comparison is a contains - so if any option contains the specified string it will match. This offers some extra flexibility in defining dependency groups. | ```xml ``` Static popup menu fields contain two elements. The first is the same `Label` element that every field has (except `Separator` which we’ll talk about later). The other is a `List` element which defines the menu items in your popup menu field. The `List` element contains multiple `Option` elements, each of which have a required `value` attribute. The `value` attribute will be passed back to your plugin when the dialog is validated; it may not contain comma characters. The text inside the `Option` element is what is displayed for each menu item in the user interface. Like [button fields](button.md), you can specify a `CallbackMethod` element in your field definition: ```xml menuChanged ``` This method will be called every time the user changes the menu. The method in your plugin.py file should look something like this: ```python def menuChanged(self, valuesDict, typeId, devId): # do whatever you need to here # typeId is the device type specified in the Devices.xml # devId is the device ID - 0 if it's a new device return valuesDict ``` Depending on which object type (plugin config, device, trigger, action, etc.) the dialog is being called for. This will allow you to adjust other fields in the valuesDict based on what's selected in the menu field. For instance, if you've selected a device type, you may decide to alter some hidden fields so that other fields are shown/and hidden. Used in combination with [Dynamic Lists](dynamic-lists.md), you may also repopulate other lists with more appropriate values. You may also specify a menu dynamically at runtime - that is, when the UI is shown, the client will call into your plugin to get the menu items. This will allow you to dynamically adjust the menu items every time the UI comes up. You can also specify some built-in dynamic items that the IndigoServer will provide automatically for you. See the [Dynamic Lists](dynamic-lists.md) section below for details. [Back to Configuration Dialogs](index.md) --- Separator (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/configui/separator/) --- # Separator { .ref-head-no-code #config-separator } Separators work very similarly to labels, except that they just draw a nice embossed line. You can use it to separate sections of your config dialogs. Used in conjunction with the visible attributes, you can make whole sections appear and disappear based on other data in the dialog. The following table describes the attributes that are available for separator fields. ![Configuration UI Separator Image](../../../../images/configui_separator.png) **Attributes** | Attribute | Required | API Version | Notes | |----------------------------------------------------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `alwaysUseInDialogHeightCalc` | No | 1.0 | For dialogs that contain hidden controls (see `visibleBindingId` and `visibleBindingValue` below), setting this attribute to `true` will force Indigo to take hidden controls into account when initially sizing the dialog box. This is useful when hidden controls become visible based on user input. | | `enabledBindingId` | No | 1.0 (1.9) | If you want to conditionally enable/disable this field based on the value of a checkbox, set this value to the id of that field.

API v1.9: You can also bind to a list field and this field will only be enabled if something is selected in the list. | | `enabledBindingNegate` | No | 1.9 | Set this to "true" to negate the binding. So if the target binding field’s value is true then the binding will be false. | | `id` | Yes | 1.0 | This is a unique identifier for this Field within the context of this `ConfigUI` element. | | `type` | Yes | 1.0 | This must be `separator`. | | `visibleBindingId` | No | 1.0 | If you want to conditionally show/hide this field based on the value of another field, set this value to the `id` of another field. If you set this, you must also include a `visibleBindingValue` | | `visibleBindingValue` | No | 1.0 | If you specify a `visibleBindingId`, you must specify the value(s) here. For instance, if you are binding to a checkbox, setting this to `false` will mean the menu field is visible only when the checkbox is unchecked, and a `true` means the opposite. To make the field dependent on another list or menu, set this value to a comma separated list of option values (`item1, item2`). You can even bind it to the value in a text field, but that’s probably of limited use. **Note**: the string comparison is a contains - so if any option contains the specified string it will match. This offers some extra flexibility in defining dependency groups. | ```xml ``` Separators are the only field type that contain no `Label` element. In fact, they don’t contain any elements at all, which is why the Field element that represents it is self terminating (notice the forward slash just before the closing bracket for the opening `Field` tag). This field type is read-only, which means that you can't have an error message attached to it from a validation method or a button method (see below for details). [Back to Configuration Dialogs](index.md) --- Serial Port (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/configui/serial-port/) --- # Serial Port { .ref-head-no-code #config-serial-port } PySerial, the Python-based serial communication library that we ship with Indigo, supports not only doing traditional serial communications via physical serial connections, but also by doing serial communications over the network using serial over sockets or RFC 2217 (a standard for serial communication over the network). If the device you're connecting to supports one of these methods of connection then you can use the `serialport` field type. This allows you to specify a single field type that, when rendered in the UI, will expand to multiple fields that will allow the user to select `Local (physical)`, `Network Socket`, or `Network RFC-2217`. Then, based on which option they select, we'll also present the other controls needed to completely select the connection method: a serial port list for the first or the hostname/ip address and port for the last two. Make sure you leave "socket://" or "rfc2217://" as the beginning of the address field for the last two. ![Configuration UI Serial Port Local Image](../../../../images/configui_serialport_local.png) ![Configuration Serial Port Socket Image](../../../../images/configui_serialport_socket.png) ![Configuration Serial Port RFC2217 Image](../../../../images/configui_serialport_rfc2217.png) `` This field type generates multiple controls in your dialog automatically. We've also included a helper method, `validateSerialPortUi(valuesDict, errorsDict, u"devicePortFieldId")` that will help you validate that the serialport control was used properly by the user. See the [Validating Serialport Fields](validation.md#validation-serialport-config) section for more information. [Back to Configuration Dialogs](index.md) --- Text Field (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/configui/text-field/) --- # Text Field { .ref-head-no-code #config-text-field } A text field is used to collect text input from the user. The following table describes the attributes that are available for text fields. ![Configuration UI Textfield Image](../../../../images/configui_textfield.png) **Attributes** | Attribute | Required | API Version | Notes | |----------------------------------------------------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `alwaysUseInDialogHeightCalc` | No | 1.0 | For dialogs that contain hidden controls (see `visibleBindingId` and `visibleBindingValue` below), setting this attribute to `true` will force Indigo to take hidden controls into account when initially sizing the dialog box. This is useful when hidden controls become visible based on user input. | | `defaultValue` | No | 1.0 | You can enter a default value here. | | `enabledBindingId` | No | 1.0 (1.9) | If you want to conditionally enable/disable this field based on the value of a checkbox, set this value to the id of that field.

API v1.9: You can also bind to a list field and this field will only be enabled if something is selected in the list. | | `enabledBindingNegate` | No | 1.9 | Set this to "true" to negate the binding. So if the target binding field's value is true then the binding will be false. | | `hidden` | No | 1.0 | If this attribute is set to `true` then the field will never be displayed regardless of what other options you have set for it. It’s useful if you need to contain some kind of state variables for the dialog that are controlled by button presses rather than controlled directly by the user. | | `id` | Yes | 1.0 | This is a unique identifier for this Field within the context of this ConfigUI element. It must be alphanumeric (and may contain underscores) and must begin with an alphabetic character. | | `readonly` | No | 1.0 | This attribute will make the field readonly - useful to show the user data that changes in some other way rather than being manipulated directly by the user - in the example above clicking the `Find Node ID` button will populate the ID field. | | `secure` | No | 1.4 | If you specify true for this value then when the user types into the field the actual characters won't show but rather will be replaced with the bullet (•) character. The values typed into these fields **are not** stored securely - this will solely mask the value in the field from viewing in the UI. | | `tooltip` | No | 1.0 | Unfortunately, disabled text fields won’t show tooltips (Cocoa limitation), so your tooltip should probably tell the user why it’s disabled (since that’s the only time it’ll show) if the field is ever disabled. | | `type` | Yes | 1.0 | This must be `textfield`. | | `visibleBindingId` | No | 1.0 | If you want to conditionally show/hide this field based on the value of another field, set this value to the id of another field. If you set this, you must also include a `visibleBindingValue` | | `visibleBindingValue` | No | 1.0 | If you specify a `visibleBindingId`, you must specify the value(s) here. For instance, if you are binding to a checkbox, setting this to `false` will mean the text field is visible only when the checkbox is unchecked, and a `true` means the opposite. To make the field dependent on a list or menu, set this value to a comma separated list of option values (`item1, item2`). You can even bind it to the value in another text field, but that’s probably of limited use. **Note**: the string comparison is a contains - so if any option contains the specified string it will match. This offers some extra flexibility in defining dependency groups. | ```xml ``` A text field contains only one element: Label. This label is shown to the left of the field and is optional, although there are probably very few times when you won’t want a label. Labels are available on every Field type except for the separator. [Back to Configuration Dialogs](index.md) --- Validation Methods (https://docs.indigodomo.com/2025.2/plugin-dev/reference/xml/configui/validation/) --- # Validation Methods { .ref-head-no-code #validation-methods } When the user clicks on the save button, a validation method is automatically called with all the field/value pairs from your dialog. This table shows which method names are called based on the calling dialog type: --- ## validateDeviceConfigUi() { #validation-device-config-ui } Validate the configuration settings of a device. **Method** `validateDeviceConfigUi(self, valuesDict, typeId, devId)` `validate_device_config_ui(self, valuesDict, typeId, devId)` **Parameters** | Parameter | Type | Signature | |-------------------------------------|---------------------------------------|------------------------------------------------------------------------------| | `valuesDict` | `indigo.Dict` | the dictionary of values currently specified in the dialog | | `typeId` | `str` | device type specified in the `type` attribute | | `deviceId` | `int` | the unique device ID for the device being edited (or 0 of it's a new device) | **Return** Returns one or more `indigo.Dict` objects (`valuesDict`, `errorMsgDict`) --- ## validateEventConfigUi() { #validation-event-config-ui } Validate the configuration of an event. **Method** `validateEventConfigUi(self, valuesDict, typeId, eventId)` `validate_event_config_ui(self, valuesDict, typeId, eventId)` **Parameters** | Parameter | Type | Signature | |--------------------------------------|---------------------------------------|---------------------------------------------------------------------------| | `valuesDict` | `indigo.Dict` | the dictionary of values currently specified in the dialog | | `typeId` | `str` | event type specified in the `type` attribute | | `eventId` | `int` | the unique event ID for the event being edited (or 0 if it's a new event) | **Return** Returns one or more `indigo.Dict` objects (`valuesDict`, `errorMsgDict`) --- ## validateActionConfigUi() { #validation-action-config-ui } Validate the configuration of an action. **Method** `validateActionConfigUi(self, valuesDict, typeId, deviceId)` `validate_action_config_ui(self, valuesDict, typeId, deviceId)` **Parameters** | Parameter | Type | Signature | |--------------------------------------|---------------------------------------|--------------------------------------------------------------------------------------------------------------------| | `valuesDict` | `indigo.Dict` | the dictionary of values currently specified in the dialog | | `typeId` | `str` | action type specified in the `type` attribute | | `deviceId` | `int` | the unique device ID for the device the user selected for the action if you specify a `deviceFilter` | **Return** Returns one or more `indigo.Dict` objects (`valuesDict`, `errorMsgDict`) --- ## validatePrefsConfigUi() { #validation-prefs-config-ui } Validate a plugin's configuration settings. **Method** `validatePrefsConfigUi(self, valuesDict)` `validate_prefs_config_ui(self, valuesDict)` **Parameters** | Parameter | Type | Signature | |--------------------------------------|---------------------------------------|--------------------------------------------------------------------------------| | `valuesDict` | `indigo.Dict` | the dictionary of values currently specified in the dialog | **Return** Returns one or more `indigo.Dict` objects (`valuesDict`, `errorMsgDict`) --- Before the dialog is dismissed, this method will be called with a dictionary containing all the fields in the dialog. In your validation method, you’d do whatever validation is necessary. If everything validates correctly, then just return `True`: ```python def validateEventConfigUi(self, valuesDict, typeId, eventId): # Do your validation logic here return True ``` If you need to adjust the values, return the valuesDict with changes: ```python def validateEventConfigUi(self, valuesDict, typeId, eventId): # Do your validation logic here valuesDict["someKey"] = someNewValue return (True, valuesDict) ``` If you have errors that the user must correct, then you’ll return 3 things: 1. `False`, to indicate that validation failed 1. The values dictionary (with or without any changes) 1. An error dictionary. The keys will be the fieldId and the value will be the error string. When the dialog receives this return, it will turn each field with an error red and add a tooltip to the label part of the field so the user can mouse over the label to see what’s wrong. API v1.4+: You can also add a special dictionary entry to your error message dictionary that will cause a sheet to drop down with the specified text. This will help the user identify what went wrong in the validation method. Just add a string with the key "showAlertText" to your error dictionary before return from your dialog. ```python def validateEventConfigUi(self, valuesDict, typeId, eventId): # Do your validation logic here errorDict = indigo.Dict() errorDict["someKey"] = "The value of this field must be from 1 to 10" errorDict["showAlertText"] = "Some very descriptive message to your user that will help them solve the validation problem." valuesDict["someOtherKey"] = someNewValue return (False, valuesDict, errorDict) ``` If you don't define these methods, then the default behavior is to have the validation calls always return `True`. ## Using ValidationError { .ref-head-no-code #using-validationerror } Building the error dictionary by hand works well for a field or two, but it gets tedious when you have several fields to check. The [`indigo.utils.ValidationError`](../../../../scripting/reference/utils.md#validationerror) exception is purpose-built for this: accumulate field errors as you validate, then convert its `error_dict` into the dictionary the validation method returns. Because a `ValidationError` is iterable, `dict()` turns it directly into the field/message pairs the dialog expects, and `str()` produces a readable summary suitable for the `showAlertText` sheet. ```python def validateDeviceConfigUi(self, valuesDict, typeId, devId): errors = indigo.utils.ValidationError("Device configuration is invalid") if not valuesDict.get("address"): errors.add_error("address", "You must enter an address.") if not indigo.utils.is_int(valuesDict.get("pollInterval", "")): errors.add_error("pollInterval", "Poll interval must be a whole number.") if errors.error_dict: # Turn the accumulated errors into the (False, valuesDict, errorsDict) the dialog expects. errorsDict = indigo.Dict(dict(errors)) errorsDict["showAlertText"] = str(errors) return (False, valuesDict, errorsDict) return (True, valuesDict) ``` `ValidationError` can also be raised from deeper validation helpers (for example a function that validates a JSON payload) and caught here, so the same validation logic can be shared between your Config UI methods and your [HTTP request handlers](../../plugin-py/http-requests.md). See the [ValidationError reference](../../../../scripting/reference/utils.md#validationerror) for its full constructor, attributes, and methods. ## Validating Serial Port Fields { .ref-head-no-code #validation-serialport-config } Because serialport fields are a bit special (they generate multiple visible fields), we've provided a helper method that you can use to make sure the user used the control correctly: ```python def validateDeviceConfigUi(self, valuesDict, typeId, devId): errorsDict = indigo.Dict() self.validateSerialPortUi(valuesDict, errorsDict, u"devicePortFieldId") # Put other config UI validation here -- add errors to errorDict. if len(errorsDict) > 0: # Some UI fields are not valid, return corrected fields and error messages (client # will not let the dialog window close). return (False, valuesDict, errorsDict) # User choices look good, so return True (client will then close the dialog window). return (True, valuesDict) ``` Notice that at the top of the validation method after we define the errorsDict, we call `self.validateSerialPortUi()`. This method will look for the fields that make up the specified field ("devicePortFieldId" in this case). It will make sure that a valid serial port is selected if it's a physical connection or that the address field is formatted properly if it's one of the network connections. If not, it will automatically add the appropriate errors to the errorsDict and will attempt to correct any URL prefix errors ("socket:%%*%%" or "rfc2217:%%*%%"). You can then continue to validate the rest of the fields in your dialog and use the length check on errorsDict to see if there were any errors discovered. If so, return False along with both dicts. If it's empty, just return true with the valuesDict. [Back to Configuration Dialogs](index.md) --- Building a Plugin (https://docs.indigodomo.com/2025.2/plugin-dev/tutorials/building/) --- # Building a Plugin !!! abstract "In this guide" Code-level reference for building and extending Indigo server plugins: reading and writing preferences, handling device configuration callbacks, implementing concurrent threads, and using the Indigo SDK example plugins as starting points. Complements the Plugin Developer's Guide with runnable, copy-pasteable examples. 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 (*`indigo.devices["office desk lamp"]`*). However, the preferred lookup mechanism is to use the object's numeric ID (*`indigo.devices[12345678]`*), which can be retrieved by control-clicking on the object name in Indigo's Main Window. By using the numeric ID you ensure the object will be found even if its name is changed. ## Indigo Plugin SDK Source Code Examples The Indigo Plugin SDK ([available here](https://github.com/IndigoDomotics/IndigoSDK/releases)) includes short, 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, sensor, speed control, sprinkler, energy meter 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. The following table lists the Indigo Plugin SDK examples that were available at the time Indigo {{ version }} shipped. | SDK Plugin task | Plugin that illustrates an approach | | --- | --- | | Broadcast Plugin Information to Listeners | Example Custom Broadcaster | | Subscribe to Plugin Information Broadcast | Example Custom Subscriber | | Walk Through the Indigo Database | Example Database Traverse | | Non-native Device Type | Example Device - Custom | | Energy Meter Device Type | Example Device - Energy Meter | | Device Factory Device Type | Example Device - Factory | | Switch / Relay / Dimmer Device Type | Example Device - Relay and Dimmer | | Sensor Device Type (Water, Motion, Light, Humidity) | Example Device - Sensor | | Speed Control Device Type (Fan) | Example Device - Speed Control | | Sprinkler Device Type | Example Device - Sprinkler | | Thermostat Device Type | Example Device - Thermostat | | Interact with the Indigo Server Using HTTP Client / Server | Example HTTP Responder | | Listen for Insteon and/or X-10 Traffic | Example Insteon:X10 Listener | | Listen for Indigo Variable Change Notifications | Example Variable Change Subscriber | | Listen for Z-Wave Traffic | Example Z-Wave Listener | 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 that are used in either built-in or freely available plugins that implement those tasks in some form or another (from simplest to most complex): | Plugin task | Plugin that illustrates an approach | | | --- | --- | --- | | Parsing JSON, XML from an IP source | NOAA Weather, [WeatherSnoop](https://www.indigodomo.com/pluginstore/10/) | | 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 | Email+ | | | Interacting with an HTTP Web server | Example HTTP Responder | | | Creating custom devices with states | Simple: NOAA Weather, [WeatherSnoop](https://www.indigodomo.com/pluginstore/10/) - Complex: EasyDAQ | | Creating custom actions | [GhostXML](https://www.indigodomo.com/pluginstore/38/) | | Creating custom events | Airfoil | | Each of these plugins is installed by default with Indigo - in the `/Library/Application Support/Perceptive Automation/Indigo [VERSION]/Plugins (Disabled)/` folder, or available as an Open Source plugin from the [Indigo Plugin Store](https://www.indigodomo.com/pluginstore/). To see the various XML and python source files, just right-click on it in the `Finder` and select `Show Package Contents`. The [SDK example](https://github.com/IndigoDomotics/IndigoSDK/releases) plugins, the plugins included with Indigo and Open Source plugins above are great places to see working examples of plugins and their source code. ## How to Read and Write Plugin Preferences - The per plugin preferences (pref) 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: `self.pluginPrefs["somePrefKey"]` - Or, if you're not sure the key exists: `self.pluginPrefs.get("somePrefKey", "default value if key doesn't exist")` - 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. - A plugin can also insert other values into its pluginPrefs space (not just values shown in the plugin's config UI). This is a great way to maintain values that are not directly visible or available to the end user. - To read a preference value access its key: ```python someVal = self.pluginPrefs["somePrefKey"] indigo.server.log("value is " + str(someVal)) ``` - To create a new or update an existing preference value assign it a new value: `self.pluginPrefs["somePrefKey"] = 1234` - The Indigo server will save the plugin prefs automatically, but you can also cause them to be saved to the server immediately using: ```python indigo.server.savePluginPrefs ``` ## 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(). ### Add new plugin metadata to the Device "den fixture" ```python dev = indigo.devices["den fixture"] # device ID preferred newProps = dev.pluginProps newProps["onCycles"] = 5 newProps["moreData1"] = "abc" newProps["moreData2"] = True newProps["moreData3"] = 123.45 dev.replacePluginPropsOnServer(newProps) ``` #### Read plugin specific property `onCycles` from the Device "den fixture" ```python dev = indigo.devices["den fixture"] # device ID preferred onCycles = dev.pluginProps["onCycles"] indigo.server.log("onCycles is " + str(onCycles)) ``` #### Increment the plugin specific property onCycles by 1 for the Device "den fixture" ```python dev = indigo.devices["den fixture"] # device ID preferred newProps = dev.pluginProps newProps["onCycles"] += 1 dev.replacePluginPropsOnServer(newProps) dev = indigo.devices["den fixture"] # device ID preferred onCycles = dev.pluginProps["onCycles"] indigo.server.log("onCycles is now " + str(onCycles)) ``` - Plugins have read-only access to other plugin metadata via *`.globalProps`*. - Plugins have read/write access to their own metadata space. ## How to Create 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 *``* 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 *``* 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 shown 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: ```python dev = indigo.devices["Custom Plugin Thermostat"] # device ID preferred dev.updateStateOnServer("heatSetPoint", dev.states["heatSetPoint"] + 1) ``` - Plugins should subclass `*deviceStartComm*` and `*deviceStopComm*` to start/stop any hardware communication (normally via a new per-device thread): ```python def deviceStartComm(self, dev): self.easydaq.startCommThread(dev) def deviceStopComm(self, dev): self.easydaq.stopCommThread(dev) ``` - Calls to `*deviceStartComm*` and `*deviceStopComm*` 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. `*deviceStopComm*` 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. ## How to Create a Custom Plugin Trigger Event The XML in this file describes all events that your plugin will generate for use in Indigo. Your users will use these in the Trigger Events dialog just like any of the built-in Indigo events (like Power Failure, Email Received, etc.) Your plugin can offer other types of events, including update notifications, battery low notifications, button press notifications, and so on. Here’s a very small `Events.xml` file that defines a plugin update event, with a small sample configuration dialog: ```xml http://www.yourdomain.com/plugin/pluginEvents.html Plugin Update Available https://www.yourdomain.com/plugin/someOtherEvent.html ``` As you can see, your `` elements can define a `` element as well -- and separate support elements for the event's configuration dialog (the trigger events dialog now has a help button on it and if one of your events is selected clicking on the help button will take your user to the specified URL). You can specify an Event to be a separator in your event list so that when they're displayed in the UI there is a visual separation. Simply insert an Event defined like this between two other Event elements: ```xml ``` There are several Event-related and Trigger-related methods that you can add to your `plugin.py` file--some of which are required in order to have the Event do something. These methods include: | Element | Description | | --- | --- | | self.closedEventConfigUi(self, values_dict, user_cancelled, type_id, event_id) | Called after user clicks the Save button within the event configuration dialog (after 'validateEventConfigUi()' method has finished successfully). Used to finalize any Event configuration steps. | | self.getEventConfigUiValues(self, plugin_props, type_id, event_id) | Called when an Event configuration window is first opened. | | self.getEventConfigUiXml(self, type_id, event_id) | Called when an Event configuration window is first opened. The method is used to provide a valid XML payload in place of the `Events.xml` file. | | validateEventConfigUi(self, values_dict, type_id, event_id) | Called when a user clicks the Save button within the event configuration dialog. Used to provide configuration input validation to ensure the settings are within your parameters. | The power of Events ## How to Monitor Changes to Various Indigo Objects In some instances, a plugin may need to know about changes to an Indigo object that it doesn't directly control. For example, you might have a [plugin that groups together a number of fans](https://www.indigodomo.com/pluginstore/89/) and treats the multiple fans as one single fan. To do this successfully, it would be necessary to monitor changes to a single fan (say a local call to turn the fans on) and propagate that call to the other fan objects. Indigo provides a method to do this by allowing the plugin to "subscribe" to changes for all objects in a particular class. ### Subscribe to Indigo Object Changes To monitor changes to different classes of Indigo objects, you can include a call to the appropriate `subscribeToChanges` method. Calling any of these methods tells Indigo to send a notification to the plugin of any changes to all objects of that type -- not only plugin's own objects -- so use these methods sparingly. | Element | | --- | | indigo.devices.subscribeToChanges() | | indigo.variables.subscribeToChanges() | | indigo.triggers.subscribeToChanges() | | indigo.schedules.subscribeToChanges() | | indigo.actionGroups.subscribeToChanges() | | indigo.controlPages.subscribeToChanges() | ```python def __init__(self, plugin_id, plugin_display_name, plugin_version, plugin_prefs): super().__init__(plugin_id, plugin_display_name, plugin_version, plugin_prefs) indigo.devices.subscribeToChanges() indigo.variables.subscribeToChanges() indigo.triggers.subscribeToChanges() indigo.actionGroups.subscribeToChanges() ``` These methods only invoke the notification process, they don't do anything with the notifications they subscribe to. In order to react to any notifications, you'll need to use the methods below. #### Monitor Device State Changes from a Plugin | Element | | --- | | self.deviceCreated(self, dev) | | self.deviceDeleted(self, dev) | | self.deviceUpdated(self, orig_dev, new_dev) | ```python def deviceCreated(self, dev): # Receives a copy of the device instance self.logger.debug(f"{dev.name} created." ``` ```python def deviceDeleted(self, dev): # Receives a copy of the device instance self.logger.info(f"{dev.name} deleted." ``` ```python def deviceUpdated(self, orig_dev, new_dev): self.logger.debug("===== deviceUpdated =====") # Call the parent implementation of deviceUpdated() base class indigo.PluginBase.deviceUpdated(self, orig_dev, new_dev) # Convert the payload objects from indigo.Dict() objects to Python dict() objects. orig_dict = {} for (k, v) in orig_dev: orig_dict[k] = v new_dict = {} for (k, v) in new_dev: new_dict[k] = v # Create a dictionary that contains only those properties and attributes that have changed. diff = {k: new_dict[k] for k in orig_dict if k in new_dict and orig_dict[k] != new_dict[k]} self.logger.debug(f"Attributes changed: {diff}") ``` #### Monitor Changes to Variables | Element | | --- | | self.variableCreated(self, var) | | self.variableDeleted(self, var) | | self.variableUpdated(self, orig_var, new_var) | The variable change monitoring methods operate like the methods for devices above. Note, unlike Indigo devices, you'll need to monkey patch the `var` object in order to iterate over it. --- Bundled Plugins (https://docs.indigodomo.com/2025.2/plugins/) --- # Bundled Plugins Indigo ships with a collection of plugins that extend the core product. Each is documented here; enable them from the **Plugins** menu in the Indigo Mac client. (For installing *third-party* plugins, see [managing plugins](../user/concepts/plugins.md#managing-plugins) in the Getting Started guide; to build your own, see [Plugin Development](../plugin-dev/index.md).) ## Voice & integration - [Alexa](alexa/index.md) — make Indigo devices and action groups available to Amazon Alexa for voice control. - [Email+](email.md) — send email from actions and trigger automations from received email. ## Audio & media - [Airfoil Pro](airfoilpro.md) — control Airfoil 5+ audio routing from Indigo. ## Data & devices - [EasyDAQ Relay Card](easydaq_1.md) — control EasyDAQ USB relay cards directly. - [Global Property Manager](globalpropertymanager.md) — add custom properties to any Indigo object. - [NOAA Weather](noaaweather.md) — pull U.S. NOAA weather conditions and forecasts into Indigo. - [SQL Logger](sql_logger.md) — log device state changes and variable updates to a database. ## Automation helpers - [Timers and Pesters](timersandpesters.md) — kitchen-timer-style timer devices and flexible repeating reminders. Looking for the `%%v:12345%%`-style substitution markup? That's a core Indigo feature, not a plugin — see [Substitutions](../user/automation/substitutions.md). --- Airfoil Pro (https://docs.indigodomo.com/2025.2/plugins/airfoilpro/) --- # Airfoil Pro [Airfoil](http://rogueamoeba.com/airfoil/) is a great application from [Rogue Amoeba](http://rogueamoeba.com/) that allows you to stream sound from your Mac or Windows computer to any combination of Airplay Devices, Bluetooth speakers, and [Airfoil satellite clients](http://rogueamoeba.com/airfoil/#satellite) (iOS, Android, Mac, Windows, Linux) - and it keeps it all in sync. Many Indigo users use it in conjunction with Apple TVs, Airport Expresses, and other computers and iOS devices to create whole-home audio systems, all streaming from your Mac/Windows computer running iTunes or other music applications. The latest major release, [Airfoil v5](http://rogueamoeba.com/airfoil/), has a new API that enables a much more reliable and capable integration with Indigo. It works with both Airfoil 5 on [Mac](http://rogueamoeba.com/airfoil/mac/) and [Windows](http://rogueamoeba.com/airfoil/windows/) and can work with as many Airfoil instances as there are on your network. !!! note This plugin requires v1.5 or later of the Airfoil API. This was delivered in Airfoil 5 for Mac and Windows. If you need support for earlier versions of Airfoil for Mac, please see [the legacy Airfoil plugin](https://github.com/IndigoDomotics/airfoil) that we open-sourced. Note, however, that it may not work with newer versions of Indigo. ## Airfoil Device Types Each Airfoil instance that can be found on your local network can be added as a device in Indigo. When you add an Airfoil device, each speaker that's available in that instance of Airfoil will also be represented by its own device. This gives you a much more flexible set of devices that can be used in triggers, actions, and control pages. ### Airfoil Instance This device type represents a single instance of Airfoil running on a Mac or Windows computer. Every Airfoil instance knows about the source that's currently selected, and in some cases (such as iTunes) knows about what's currently playing (album name, album art, artist, source name/id/icon, track name) and can control the source to some extent (toggle play/pause, next track, previous track). It also knows what sources are available so you can easily switch between them. #### Adding an Instance To create an Airfoil Instance device, click the `New...` button above the devices list. In the resulting Create New Device dialog, select Airfoil Pro as the type and you will see the Add Airfoil Instance... dialog: ![Add Airfoil Instance Image](../images/add_airfoil_instance.png) In the first popup, select the Airfoil instance that you want to add. This popup is dynamically generated based on Airfoil's discovery protocol. If you don't see your Airfoil instance, make sure Airfoil v5 is running on a Mac or Windows computer on the same network as your Indigo server Mac. The plugin can store a variety of images from Airfoil. You can use these images in refreshing image URLs on your control pages. The second field in the dialog is a full path to a directory where the plugin will store the image files. If you have multiple Airfoil instances, make sure you use a different directory since the image files have a fixed name. The plugin will store the following images in this directory: - `albumArt.png` - an image of the currently playing album track if the source supports it (notably, iTunes) - `machineIcon.png` - an icon representing the computer that is running this instance of Airfoil - `machineIconAndScreenshot.png` - a screenshot from the host computer with the machine icon overlaid (though this functionality seems to be missing in Airfoil versions through v5.1.0 - you just get the same image as the machine icon) - `sourceIcon.png` - an icon representing the selected audio source in Airfoil. Leave the field empty if you don't wish for the plugin to store these images. One oversight that there is in the Airfoil API is the ability to know the play state of the source. So the plugin can't know whether iTunes is playing or paused. If you are using the iTunes Indigo plugin, you have this information, but it's in a different device which could make control page design a little tricky. To work around this missing functionality, we allow you to select an iTunes server Indigo device (that you've previously created) that represents the same iTunes server as the one your Airfoil instance is using as its source. The plugin will monitor that iTunes device and update the sourcePlayStatus state so that it mirrors the iTunes device. When you click the "Save" button, the plugin will create your Airfoil Instance device, and it will query Airfoil and get all it's known speakers. It will then create devices for each of those speakers (see the next section for details on those devices). This is what the Edit Device dialog will look like when it's finished: ![Airfoil Edit Group Image](../images/airfoil_edit_group.png) Airfoil instances are actually a group of devices: the instance itself (the first tab), and then multiple speaker devices, one for each speaker device that your instance knows about. We set the name of the Airfoil Instance to the Airfoil name. For each speaker, we name the speaker with this pattern: "Speaker Name (Airfoil Instance Name speaker)" to help you see how the devices relate to each other. We also add a note to each speaker device with a bit more detail about its relationship to its parent Airfoil Instance device. Airfoil Instance devices have the following state changes that you can use in Triggers: ![Airfoil Triggers Image](../images/airfoil_triggers.png) !!! warning Due to changes that Apple has made to their API, the *`Source Status Is Playing`* trigger will no longer fire. This trigger option will be adjusted in a future release. Those states can also be displayed in Control Pages. #### States available to Scripts If you want to use an Airfoil Instance device in a script, here are the state details: | **State Key** | **Value Type** | **Description** | |-----------------------------|-------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------| | *`canConnect`* | boolean | This instance allows remote speakers to connect | | *`canRemoteControl`* | boolean | This instance allows remote control of the source | | *`instanceName`* | string | The name of the instance. On Mac systems, this is the name of the Mac as defined in the Sharing System Preference Panel | | *`protocolVersion`* | string | This is the version of Airfoil's API that this instance uses. v1.5 is the minimum. | | *`sourceAlbum`* | string | The name of the album that the selected source is playing (if sourceMetadataAvailable == True) | | *`sourceArtist`* | string | The name of the artist of the album that the selected source is playing (if sourceMetadataAvailable == True) | | *`sourceBundleId`* | string | The unique identifier of the source | | *`sourceMachineModel`* | string | The model of the computer running Airfoil | | *`sourceMachineName`* | string | The name of the computer running Airfoil (usually the same as the instanceName) | | *`sourceMetadataAvailable`* | boolean | The source supplies metadata (album, artist, track, etc) | | *`sourceName`* | string | The name of the selected source | | *`sourcePlayStatus`* | string (playing, paused, stopped, unavailable, error) | If the source is iTunes and you've selected an iTunes server as the source in this device's config, this will mirror that device's playState | | *`sourceTrackName`* | string | The name of the track that the current source is playing (if sourceMetadataAvailable == True) | | *`status`* | string (disconnected, connected, unavailable) | The status of this device with respect to the Airfoil communication | #### Notes If you change the name of your Mac, you'll need to perform the `Define and Sync...` again - select the correct Airfoil instance in the sync dialog and `Save`. Unfortunately, Airfoil doesn't send out a name change notification so the plugin doesn't know what happened other than the instance it was talking to is now gone. ### Speaker Each Airfoil Instance device has a collection of "Speaker" devices in its group. These are all the various outputs to which Airfoil can direct its source audio. These devices are created automatically when you create your Airfoil Instance and any time a new source gets added to the instance. For instance, if you create an Airfoil Instance and it finds 3 speakers, then at a later time you add a Bluetooth speaker to the Mac that Airfoil is running on, the plugin will automatically create a new speaker device for that Bluetooth speaker. We never automatically delete speakers: if you have a Bluetooth speaker that fails, and you won't ever use it again, you'll need to remove it manually. !!! warning You can't use the `Delete...` button above the device list without deleting ALL the devices in that Airfoil Instance Group. If you need to delete just a single speaker, select the `Plugins->Airfoil Pro->Permanently Delete Speaker` menu item. This will allow you to just delete a single speaker. An unfortunate side effect of how Airfoil handles AirPlay devices, like AppleTVs, is that if you change the name of them it will create a new speaker instance (rather than just replacing it). From the API perspective, it's just another new speaker that was added. What you'll need to do in that case is switch over any triggers, actions, and control pages that use the old speaker to use the newly created speaker, then delete the speaker manually. #### States available to Scripts { #speaker-states-available-to-scripts } If you want to use an Airfoil Instance device in a script, here are the state details: | **State Key** | **Value Type** | **Description** | |----------------------|-----------------------------------------------|---------------------------------------------------------------------------------| | *`longIdentifier`* | string | The unique identifier of the speaker | | *`name`* | string | The name of the speaker in Airfoil (which is different than its name in Indigo) | | *`parentInstanceId`* | number | The device ID of the parent Airfoil Instance | | *`status`* | string (connected, disconnected, unavailable) | The status of the speaker | | *`type`* | string (local, airplay, Bluetooth, group) | The type of speaker | | *`volume`* | number | The volume that the speaker is currently set to. | ## Airfoil Actions The Airfoil plugin provides a variety of actions that allow you to fully manage an Airfoil Instance and Speakers. We'll separate these into Speaker actions and Instance actions. These actions are available on the `Type:` menu in the actions edit dialog under `Device Actions->Airfoil Pro Controls` submenu. ![Action Menu Image](../images/actionmenu.png) For those interested in controlling Airfoil Pro devices from another plugin or script, you will find the details below after a description of each action in the **Scripting details** section. You don't need to know or understand those sections if you're not interested in writing scripts. ### Notes for Script/Plugin Writers If you examine the example scripts below, you'll note that each action call will return a result. Unless otherwise specified, it's just a boolean indicating if the action correctly ran. You may also note that the action calls are inside a try block. If you specify *waitUntilDone=True* in your action call, you may catch an exception if something happened during the action execution. The exception message will explain in a human-readable string what occurred. Finally, at the very end of the list are Script Actions - these are actions that don't really appear in the UI, but will return something useful for your script/plugin. See that section for more details. ### Speaker Actions The things you want to do with speaker actions are pretty simple: control whether a speaker is being used and setting the volume of the speaker. To that end, here are the actions. #### Connect Speaker This action will cause Airfoil to begin broadcasting audio to the specified speaker. ##### Scripting details **Action id**: connect The deviceId is the Indigo ID of the Speaker device. No properties for scripting required. Example: ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( "connect", deviceId=135305663, # ID of Speaker device waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` #### Disconnect Speaker This action will cause Airfoil to stop broadcasting audio to the specified speaker. ##### Scripting details { #disconnect-speaker-scripting-details } **Action id**: disconnect The deviceId is the Indigo ID of the Speaker device. No properties for scripting required. Example: ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( "disconnect", deviceId=135305663, # ID of Speaker device waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` #### Toggle Speaker This action will cause Airfoil to toggle the specified speaker between connected and disconnected. Useful to execute from a single element (i.e. button) from a control page. ##### Scripting details { #toggle-speaker-scripting-details } The deviceId is the Indigo ID of the Speaker device. **Action id**: toggle No properties for scripting required. Example: ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( "toggle", deviceId=135305663, # ID of Speaker device waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` #### Save Current Speaker States This action will save the connection state for each speaker on the selected Airfoil device. This is useful if you need to temporarily change the state of some speakers, but you want to be able to easily, set them back to how they were before the change. You can optionally specify that Airfoil Group states also be saved though this option is usually not very useful since storing the current state of all speakers will usually accomplish the same as restoring a group. ##### Scripting details { #save-current-speaker-states-scripting-details } **Action id**: saveCurrentSpeakerStates The deviceId is the Indigo ID of the Airfoil device. | *`includeAirfoilGroups`* | Optional (default is False) boolean specifying whether to include Airfoil Groups | |--------------------------|----------------------------------------------------------------------------------| Example 1 (no groups): ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( "saveCurrentSpeakerStates", deviceId=135305663, # ID of Airfoil device waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` Example 2 (include groups): ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: # This time, we'll add the includeAirfoilGroups property that will also store group states result = airfoilPlugin.executeAction( "saveCurrentSpeakerStates", deviceId=135305663, # ID of Airfoil device props={"includeAirfoilGroups": True}, waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` #### Restore Saved Speaker States This action will restore speaker states to what was previously saved using the above action. ##### Scripting details { #restore-saved-speaker-states-scripting-details } **Action id**: saveCurrentSpeakerStates The deviceId is the Indigo ID of the Airfoil device. Example 1 (no groups): ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( "restoreSavedSpeakerStates", deviceId=135305663, # ID of Airfoil device waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` #### Set Volume This action will cause Airfoil to set the volume of specified speaker. ##### Scripting details { #set-volume-scripting-details } **Action id**: setVolume The deviceId is the Indigo ID of the Speaker device. | *`volume`* | The volume (0-100) to set the speaker to | |------------|------------------------------------------| Example: ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( 'setVolume', deviceId=135305663, # ID of Speaker device props={'volume': 50}, waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` #### Increase Volume This action will cause Airfoil to increase the volume of specified speaker by the specified amount (defaults to 5). ##### Scripting details { #increase-volume-scripting-details } **Action id**: increaseVolume The deviceId is the Indigo ID of the Speaker device. | *`volume`* | The delta (0-100) increase the speaker's volume by - default is 5 | |------------|-------------------------------------------------------------------| Example 1 (using default delta): ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( 'increaseVolume', deviceId=135305663, # ID of Speaker device waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` Example 2 (specifying delta): ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( 'increaseVolume', deviceId=135305663, # ID of Speaker device props={'volume': 15}, waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` !!! note Because the Airfoil API doesn't directly support increase/decrease, the plugin gets the current volume from the speaker device and calculates the new volume. An unfortunate side effect is that if you send multiple increase/decrease actions in a brief amount of time, the plugin won't yet know that the speaker's volume has changed from the previous command and may not work. So space your increase/decrease commands out a bit to avoid this problem. #### Decrease Volume This action will cause Airfoil to decrease the volume of specified speaker by the specified amount (defaults to 5). ##### Scripting details { #decrease-volume-scripting-details } **Action id**: decreaseVolume The deviceId is the Indigo ID of the Speaker device. | *`volume`* | The delta (0-100) increase the speaker's volume by | |------------|----------------------------------------------------| Example 1 (using default delta): ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( 'increaseVolume', deviceId=135305663, # ID of Speaker device waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` Example 2 (specifying delta): ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( 'decreaseVolume', deviceId=135305663, # ID of Speaker device props={'volume': 15}, waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` !!! note Because the Airfoil API doesn't directly support increase/decrease, the plugin gets the current volume from the speaker device and calculates the new volume. An unfortunate side effect is that if you send multiple increase/decrease actions in a brief amount of time, the plugin won't yet know that the speaker's volume has changed from the previous command and may not work. So space your increase/decrease commands out a bit to avoid this problem. ### Airfoil Instance Actions Actions that you can perform on Airfoil Instance devices relate to the current source. #### Disconnect All Speakers Tell Airfoil to disconnect all speakers. This is useful if you want to connect just the speakers in an Airfoil Group (disconnect everything first then connect the group). ##### Scripting details { #disconnect-all-speakers-scripting-details } **Action id**: disconnectAllSpeakers No properties for scripting required. The deviceId is the Indigo ID of the Airfoil device. Example: ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( 'disconnectAllSpeakers', deviceId=12345678, # ID of Airfoil device waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` #### Change Source This action allows you to change the audio source of the Airfoil instance (as if you changed it by selecting a new source from the popup in the Airfoil app). ##### Scripting details { #change-source-scripting-details } **Action id**: changeSource The deviceId is the Indigo ID of the Airfoil device. | *`sourceGroup`* | One of the following: 'audioDevices', 'recentApplications', 'systemAudio'. You can see what sources are known by selecting the `Plugins->Airfoil Pro->Show Sources` menu item and all source groups and sources will be shown in the event log. You can also call it programmatically (as shown below with the Show Speakers Action/Menu Item) and it will return a dictionary of valid sources. | |-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | *`source`* | The unique identifier of the source. | Example: ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: props = {"sourceGroup": "systemAudio", "source": "com.rogueamoeba.source.systemaudio" } result = airfoilPlugin.executeAction( "changeSource", deviceId=1346588091, # ID of Airfoil device props=props, waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` #### Save Current Source This action will save the currently selected source for the specified Airfoil device. This is useful if you need to temporarily change the source (for instance, to perform some kind of announcement), then switch it back to the previous source. ##### Scripting details { #save-current-source-scripting-details } **Action id**: saveCurrentSource The deviceId is the Indigo ID of the Airfoil device. Example: ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( "saveCurrentSource", deviceId=1346588091, # ID of Airfoil device waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` #### Restore Saved Source This action will restore the previously saved source for the specified Airfoil device. This is useful if you need to temporarily change the source (for instance, to perform some kind of announcement), then switch it back to the previous source. ##### Scripting details { #restore-saved-source-scripting-details } **Action id**: restoreSavedSource The deviceId is the Indigo ID of the Airfoil device. Example: ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( "restoreSavedSource", deviceId=1346588091, # ID of Airfoil device waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` #### Toggle Play/Pause Tell Airfoil to tell the source to toggle play/pause. This only works with some sources and there *aren't* discrete play and pause commands available in the Airfoil API. ##### Scripting details { #toggle-playpause-scripting-details } **Action id**: playPause No properties for scripting required. The deviceId is the Indigo ID of the Airfoil device. Example: ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( 'playPause', deviceId=12345678, # ID of Airfoil device waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` #### Next Track Tell Airfoil to tell the source to go to the next track. This only works with some sources. ##### Scripting details { #next-track-scripting-details } **Action id**: nextTrack No properties for scripting required. The deviceId is the Indigo ID of the Airfoil device. Example: ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( 'nextTrack', deviceId=12345678, # ID of Airfoil device waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` #### Previous Track Tell Airfoil to tell the source to go to the next track. This only works with some sources. ##### Scripting details { #previous-track-scripting-details } **Action id**: prevTrack No properties for scripting required. The deviceId is the Indigo ID of the Airfoil device. Example: ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( 'prevTrack', deviceId=12345678, # ID of Airfoil device waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` ### Airfoil Scripting Actions The following action is available specifically for scripts. #### = getSources This script action will return an indigo.Dict object. The keys are the source groups, the values are lists of dicts with the following keys: friendlyName (the user-friendly name), icon (a binhex'd string that is the source's icon), identifier (the ID used in the source property above). You can unbinhex the icon and save it to a PNG file and store it somewhere for use if you like. **Action id**: getSources No properties are required. The deviceId is the Indigo ID of the Airfoil device. ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( 'getSources', deviceId=12345678 # ID of Airfoil device ) except Exception as e: print(f"Exception occurred: {e}") ``` The *result* that's returned from this call will look something like this: ```text Data : (dict) audioDevices : (list) Item : (dict) friendlyName : Apple USB audio device (string) icon : [SNIP] identifier : AppleUSBAudioEngine:Apple Inc.:Apple USB audio device:241000:2,1 (string) Item : (dict) friendlyName : Built-in Microphone (string) icon : [SNIP] identifier : AppleHDAEngineInput:1B,0,1,0:1 (string) Item : (dict) friendlyName : USB audio CODEC (string) icon : [SNIP] identifier : AppleUSBAudioEngine:Burr-Brown from TI:USB audio CODEC:400000:2,1 (string) recentApplications : (list) Item : (dict) friendlyName : iTunes (string) icon : [SNIP] identifier : /Applications/iTunes.app (string) Item : (dict) friendlyName : iMovie (string) icon : [SNIP] identifier : /Applications/iMovie.app (string) systemAudio : (list) Item : (dict) friendlyName : System Audio (string) icon : [SNIP] identifier : com.rogueamoeba.source.systemaudio ``` That's an indigo.Dict() object. Each group is the key, and the value for each group is a list of "source" indigo.Dict()'s. Each source dict has the following keys: *friendlyName* (the user-friendly name), *icon* (a binhex'd string that is the source's icon - snipped from the above example to save space), *identifier* (the ID used in the source property above). You can unbinhex the icon and save it to a PNG file and store it somewhere for use if you like. You can use the group name and identifier combination in the changeSource action described above. --- EasyDAQ Relay Card (https://docs.indigodomo.com/2025.2/plugins/easydaq_1/) --- # EasyDAQ Relay Card Plugin The EasyDAQ Relay Card Plugin for Indigo integrates several [USB (and IP) controlled relay and digital input/output cards](http://www.easydaq.co.uk/) with Indigo. Using Indigo you can control the digital and relay output channels, execute actions when a digital input changes, and inspect all input and output channels remotely via Control Pages. ![EasyDAQ USB Controlled Relay and DIO Cards](https://www.easydaq.co.uk/image/catalog/EasyDAQ%20Logo%20%28100tint%29.gif){ width=360 } ## Cards Supported Several versions of the EasyDAQ cards are supported: - USB4PRMxN, USB4PRMx, USB4SRMx (4 relays + 4 DIO channels) - USB8VI4DIOSR (8 isolated inputs + 4 relays + 4 DIO channels) - USB8PR2, USB8PR, USB8SR (8 relays) - USB16PRMxN, NET16PRMx (8 relays + 8 relays/DIO + 8 DIO channels) - USB24MxS (24 relays) - USBDIO24 (24 DIO channels) ### Adding Cards to Indigo Each USB controlled card is added to Indigo as a separate Indigo Device. All EasyDAQ relay cards use the FTDI VCP driver, so be sure and [download and install](http://www.indigodomo.com/ftdiurl) it first. Next choose the `File->New Device...` menu item, select `Plugin` from the Type popup control, and `EasyDAQ Relay Card` from the Plugin popup control, and choose the correct Model. Press the `Edit Device Settings...` button to configure the card: ![Easydaq Settings Window Image](../images/easydaq_settings_window.png) The FTDI VCP driver adds the virtual serial port. Note you must have the card plugged into your Mac for the port to be shown in the popup control. Or if you are using a network (IP) based card, you can select the Network Socket connection type and enter the IP and port address for the card. You can define custom labels for all the inputs and outputs, and for some channels, depending on the card model, choose if a digital input/output is being used as an input or an output. ### Controlling Relay and Digital Outputs The output channels are controlled via Indigo actions. The actions can be inside Triggers, Schedules, Action Groups, or assigned to Control Page controls. From the Action panel inside these dialogs choose `Plugin` from the Type popup control, and then choose the Action you want to perform and the target Device: ![Easydaq Action Panel Window Image](../images/easydaq_action_panel_window.png) Some actions have additional options available via the `Edit...` button. For example, the `Change Multiple Outputs` action allows you to control (turn on, turn off, or toggle) all the outputs with a single, and very fast, action: ![Easydaq Action Change Multi Window Image](../images/easydaq_action_change_multi_window.png) ### Triggering Actions on a Digital Input Change Indigo will automatically track all input and output channel states. To trigger an action when a particular channel state changes from OFF to ON (or ON to OFF) choose the `File->New Trigger` menu item, and select `Device State Changed` from the Type popup control. Next, select the Indigo device representing the EasyDAQ card you want to monitor, and then choose which channel change will cause the trigger: ![Easydaq Trigger Device State Change Window Image](../images/easydaq_trigger_devstatechange_window.png) You can then use the Condition panel to add further logic, and the Actions panel to define the action you want executed (ex: turn on lights, send an email, etc.) ### Preventing Floating Input Problems Depending on the EasyDAQ model you are using, you may experience a floating input problem when an input is left in an open state. If you see a continuous stream of changes logged inside Indigo only when an input is in an open state, then you will need to use a [pull-up (or down) resistor](#pull-up-and-pull-down-resistors). ## Scripting Support As with all plugins, actions defined by this plugin may be executed by [Python scripts](../scripting/tutorial.md#scripting-indigo-plugins). Here's the information you need to script the actions in this plugin. **Plugin ID**: com.perceptiveautomation.indigoplugin.easydaq-usb-relay-cards ### Action specific properties #### Turn On Output **Action id**: turnOnOutput Properties for scripting: | *`channelSel`* | this is the channel number to turn on, values depend on the type of card you have | |----------------|-----------------------------------------------------------------------------------| Example: ```python plugin_id = "com.perceptiveautomation.indigoplugin.easydaq-usb-relay-cards" # Get a plugin object given the plugin id: easyDaqPlugin = indigo.server.getPlugin(plugin_id) if easyDaqPlugin.isEnabled(): easyDaqPlugin.executeAction( "turnOnOutput", deviceId=131523919, props={'channelSel':1} ) ``` #### Turn Off Output **Action id**: turnOffOutput Properties for scripting: | *`channelSel`* | this is the channel number to turn off, values depend on the type of card you have | |----------------|------------------------------------------------------------------------------------| Example: ```python plugin_id = "com.perceptiveautomation.indigoplugin.easydaq-usb-relay-cards" # Get a plugin object given the plugin id: easyDaqPlugin = indigo.server.getPlugin(plugin_id) if easyDaqPlugin.isEnabled(): easyDaqPlugin.executeAction( "turnOffOutput", deviceId=131523919, props={'channelSel':1} ) ``` #### Toggle Output **Action id**: toggleOutput Properties for scripting: | *`channelSel`* | this is the channel number to toggle, values depend on the type of card you have | |----------------|----------------------------------------------------------------------------------| Example: ```python plugin_id = "com.perceptiveautomation.indigoplugin.easydaq-usb-relay-cards" # Get a plugin object given the plugin id: easyDaqPlugin = indigo.server.getPlugin(plugin_id) if easyDaqPlugin.isEnabled(): easyDaqPlugin.executeAction( "toggle", deviceId=131523919, props={'channelSel':1} ) ``` #### All Outputs On **Action id**: allOutputsOn No properties for scripting are required. Example: ```python plugin_id = "com.perceptiveautomation.indigoplugin.easydaq-usb-relay-cards" # Get a plugin object given the plugin id: easyDaqPlugin = indigo.server.getPlugin(plugin_id) if easyDaqPlugin.isEnabled(): easyDaqPlugin.executeAction( "allOutputsOn", deviceId=131523919 ) ``` #### All Outputs Off **Action id**: allOutputsOff No properties for scripting are required. Example: ```python plugin_id = "com.perceptiveautomation.indigoplugin.easydaq-usb-relay-cards" # Get a plugin object given the plugin id: easyDaqPlugin = indigo.server.getPlugin(plugin_id) if easyDaqPlugin.isEnabled(): easyDaqPlugin.executeAction( "allOutputsOff", deviceId=131523919 ) ``` #### Change Multiple Outputs **Action id**: changeMultiple Properties for scripting: | *`channelSel#`* | this is the command for the channel and must be one of the following: "turnOn", "turnOff", "toggle" - note that you should have separate properties for each channel, see example below for details | |-----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| Example: ```python plugin_id = "com.perceptiveautomation.indigoplugin.easydaq-usb-relay-cards" # Get a plugin object given the plugin id: easyDaqPlugin = indigo.server.getPlugin(plugin_id) if easyDaqPlugin.isEnabled(): easyDaqPlugin.executeAction( "changeMultiple", deviceId=131523919, props={ 'channelSel1':'turnOn', 'channelSel8':'toggle', 'channelSel17':'turnOff' } ) ``` ## Pull-Up and Pull-Down Resistors { #pull-up-and-pull-down-resistors } Home automation often involves interfacing with contact closure type switches. Some examples include: alarm magnetic reed door/window switches, water float level switches, push button switches, etc. Some input hardware can be directly wired to these switches, such that the INPUT pin/terminal goes directly to one switch wire while the other switch wire is connected to +5V. This hardware, like the Insteon I/O-Linc, has an internal circuit so that it can specifically be used for contact closure type circuits. However, some TTL based input hardware will have a "floating input" problem when the switch is open. In such an open state the INPUT pin on the hardware will be connected to nothing and the TTL circuitry will bounce between showing ON and OFF. You'll know this is a problem because there will be a continuous stream of changes logged inside Indigo when the switch is in the open state. The solution in this case is to use a pull-up resistor (RadioShack will have the 10K resistor needed) so that the INPUT pin is never left in a floating, or unconnected, state: ![Pull Up Resistor Image](../images/pullup_resistor.png) When the closure switch is in the open state, the INPUT pin is pulled up to +5V through the 10K resistor. When the closure switch is in the closed state, INPUT will be forced to GND (+0V). Note the 10K resistor is needed so that there isn't an unloaded (no resistance) short between +5V and GND when the switch is closed. By using a 10K resistor, only a tiny amount of current will flow from +5V to GND when the switch is closed. Note the above circuit will have INPUT pulled to +5V when the circuit is open, and INPUT will be GND when the circuit is closed. If this is the opposite of what you want, then you can use a pull-down resistor circuit instead: ![Pull Down Resistor Image](../images/pulldown_resistor.png) ## Support and Troubleshooting For usage or troubleshooting tips [discuss this device](https://forums.indigodomo.com/viewforum.php?f=93) on our forum. --- Email+ (https://docs.indigodomo.com/2025.2/plugins/email/) --- # Email+ The Email+ plugin is based on the [Better Email plugin](https://www.indigodomo.com/pluginstore/30/), which has been around for many years and is rock solid. With Indigo 2021.2 and the port to the M1 processor, we decided it was time to move away from our very old email solution to something that was newer and had more features. The plugin author graciously allowed us to include it with Indigo. We changed the name to avoid any confusion. **Note**: **this plugin was added in the Indigo 2021.2 installer**. If you were using the [Better Email plugin](https://www.indigodomo.com/pluginstore/30/) from the [Plugin Store](https://www.indigodomo.com/pluginstore/) before you upgraded, the upgrade process should have worked seamlessly, and you shouldn't need to do much. The one thing you may need to change are any [scripts that send emails using the Better Email plugin](https://github.com/FlyingDiver/Indigo-BetterEmail/wiki/Scripting-BetterEmail): you will need to change the id to `com.indigo.email` (see the [Scripting Emails](#scripting-emails) section below for details). The same applies to other plugins that were subscribing to [broadcast messages](#broadcast-messages). Use the [Email+ forum](https://forums.indigodomo.com/viewforum.php?f=360) for questions about this plugin. For users of Indigo 7.5 or earlier, we highly recommend that you use the Better Email Plugin (which we've left in the Plugin Store). You won't be able to install Better Email in Indigo versions later than 7.5 since they are basically the same plugin. ## Email Devices and Usage The plugin provides three types of Indigo devices: you must create instances of the device types below to be able to send and receive emails. ### Sending Emails (SMTP Server devices) The SMTP Server is the device type used for sending emails. To send an email, you first need to create an SMTP device. If you had a previous version of Indigo configured to send emails, the upgrade process will create this server for you based on your prior settings. The name of that server will be `Email+ SMTP Server`. In Indigo, create a new device, in the `Type` popup select **Email+** and for `Model` select **SMTP Server** ![SMTP Server Image](../images/smtp_server.png) You will need to confirm your settings with your email provider. We **highly recommend** using a dedicated email address for Indigo, particularly if you are performing email scans. **NOTE**: it seems that most email providers are now using StartTLS as their encryption method, so if you get an error about *violation of protocol*, you probably need to change it to StartTLS. ### Sending an Email In the Actions tab, you can use the `Notification Actions->Send Email` action to send an email. The format can be either plain text or HTML. Variable (`%%v:VariableIDHere%%`) and device state (`%%d:DeviceIDHere:StateIDHere%%`) substitutions are available in all fields. #### Sending a Plain Text Email When sending plain text email messages, simply enter the appropriate text in the Message field. ![SMTP Send Email Image](../images/smtp_send_email.png) #### Sending an HTML Email ![Send HTML Email Image](../images/2023_1_send_html_email.png) When sending an HTML email, it's your responsibility to ensure that the Message field contains a valid HTML document - we don't attempt to validate it. The plugin supports both simple and more complex HTML constructions. For example, a simple HTML message might look like this: ```text

Heading 1



Some plain text. ``` More complex HTML documents are also supported: ```xml A Title

Heading 2



Some text with some styling applied. ``` There are several good reference sites for using HTML and CSS such as the Mozilla [HTML Reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference) and [CSS Reference](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference). #### Sending Attachments Specify attachments by entering the full path to a file with each file separated by commas. For example, *`/Library/Application Support/Perceptive Automation/files/some_file.txt,/Library/Application Support/Perceptive Automation/files/another_file.txt`* (It's not necessary to escape spaces within the path.) #### Sending Event Log Data In the Actions tab, you can use the `Notification Actions->Send Indigo Log Email` action to send parts of your Event Log lines: ![SMTP Send Log Image](../images/smtp_send_log.png) Variable (`%%v:VariableIDHere%%`) and device state (`%%d:DeviceIDHere:StateIDHere%%`) substitutions are available in all fields. #### Email+ Actions In addition to the `Notification Actions` mentioned above, there are four more Email+ actions to choose from. ##### Poll Email Server Actions 1. You can use the `Email+ Actions->Poll All Email Servers` item to poll all configured SMTP servers to query for new messages. 2. You can use the `Email+ Actions->Poll Email Server` item to poll a specific SMTP server for new messages. ##### Email Queue Actions 1. You can use the `Email+ Actions->Clear All Email Queues` item to clear out all queued emails from all of your SMTP servers. If your email server is having issues, you may find that you want to clear out all pending emails that the plugin has queued up for your email server. 2. You can use the `Email+ Actions->Clear Email Queue` item to clear out the email queue for a specific SMTP server. ### Receiving Emails There are two server types for receiving email: **IMAP**, which is the most fully-featured and is the best option, and **POP**, an older protocol used by older mail providers. #### IMAP Server This is the primary device type used for scanning incoming emails. **Note**: we **highly recommend** using a dedicated email account for Indigo since there is post-processing that can happen, and we don't want accidental email deletions in an account that's used for other things. In Indigo, create a new device, in the `Type` popup select **Email+** and for `Model` select **IMAP Server** ![IMAP Server Image](../images/imap_server.png) When configuring your **IMAP** device, you will need to confirm the settings with your email provider. The defaults for Encryption and Server Port are usually correct, though that is provider specific. Most email providers `Use IDLE`, but if things don't seem to be working you can check with your provider for this setting. There are 3 options for the `After Message is processed field`: - `Leave in INBOX` (default) - this is the safest option if you are using this email address for other things. It can lead to an ever-growing email list, so you'll need to delete messages yourself once you're sure you are done with them. - `Delete Message` - if you are using a dedicated email address for Indigo, this is a good option since it will delete a message after it's been processed. This will keep the inbox from growing uncontrollably. - `Move Message` - this is a compromise of the two options above: it keeps the INBOX clean, but it also keeps all emails if you want to manually manage them. The `Check All` button is really used for testing - it will process all emails regardless of whether they have been processed in the past or not. ##### IMAP Mailbox Naming When using the option to move processed messages to another mailbox, instead of deleting or leaving in the Inbox, you'll need to specify the name of the destination mailbox. Unfortunately, the naming scheme for IMAP mailboxes is server specific. There are two primary traits of the naming scheme that vary between servers. First, some servers require all mailboxes to be relative to the INBOX. Others do not. Second, the delimiter used in the mailbox path is not fixed. Most servers use either `/` or `.`. So, for a top level mailbox called "Processed", the most probable names to put in the destination folder field are: - Processed - INBOX/Processed - INBOX.Processed To provide some hints that might help determine the correct naming, if the plugin logging is set to "Detailed Debugging Messages" it will query the IMAP server for the list of names of the top-level mailboxes. The log will show something like this: Email+ Threaddebug Indigo IMAP: Mailbox list: Email+ Threaddebug Indigo IMAP: Mailbox: (\Drafts \NoInferiors) "/" Drafts Email+ Threaddebug Indigo IMAP: Mailbox: (\HasNoChildren) "/" INBOX Email+ Threaddebug Indigo IMAP: Mailbox: (\NoInferiors) "/" OUTBOX Email+ Threaddebug Indigo IMAP: Mailbox: (\HasNoChildren) "/" Processed Email+ Threaddebug Indigo IMAP: Mailbox: (\Sent \NoInferiors) "/" Sent Email+ Threaddebug Indigo IMAP: Mailbox: (\Junk \NoInferiors) "/" Spam Email+ Threaddebug Indigo IMAP: Mailbox: (\Trash \HasNoChildren) "/" Trash The "/" specifies that the delimiter between name parts is "/". #### POP Server This is an alternative device type used for scanning incoming emails. It is an older protocol but may be the only option you have from your email provider. If you had a previous version of Indigo configured with email scanning, the upgrade process will create this server for you based on your prior settings. The name of that server will be `Email+ POP Server`. In Indigo, create a new device, in the `Type` popup select **Email+** and for `Model` select **POP Server** ![POP Server Image](../images/pop_server.png) Again, if you want to use **POP** you'll need to confirm the settings with your email provider. ### Incoming Email Events There are a few events that can be used to fire triggers in Indigo. In the Trigger dialog, select Email Event: ![Email Event List Image](../images/email_event_list.png) You can use these events to monitor incoming emails (and email errors). You can specify either an IMAP or a POP server as described above. ![Email String Match Event Image](../images/email_string_match_event.png) - `String Match in Email` - this event will allow you to perform exact matches in the subject or body of an email, or the sender of the email. - `RegEx Pattern Match in Email` - this will allow you to use [regular expressions](https://www.w3schools.com/python/python_regex.asp) to match some subset of text in the above 3 fields. - `Server Connection Error` - this will fire a trigger when there is some kind of error in any of the 3 server types above. ## Broadcast Messages Other plugin developers can subscribe to messages from the Email+ plugin when emails are sent or received: ```text MessageType: messageReceived Returns dictionary: { 'messageFrom': , 'messageTo': , 'messageSubject': , 'messageText': } MessageType: messageSent Returns dictionary: { 'messageFrom': , 'messageTo': , 'messageSubject': , 'messageText': } ``` The plugin id for the plugin is `com.indigodomo.email` ## Tips on IMAP Mailbox Naming When using the IMAP device option to move processed messages to another mailbox, instead of deleting or leaving in the Inbox, you'll need to specify the name of the destination mailbox. Unfortunately, the naming scheme for IMAP mailboxes is server specific. There are two primary traits of the naming scheme that vary between servers. First, some servers require all mailboxes to be relative to the INBOX. Others do not. Second, the delimiter used in the mailbox path is not fixed. Most servers use either `/` or `.`. So, for a top level mailbox called "Processed", the most probable names to put in the destination folder field are: - Processed - INBOX/Processed - INBOX.Processed To provide some hints that might help determine the correct naming, if the plugin logging is set to "Detailed Debugging Messages" it will query the IMAP server for the list of names of the top-level mailboxes. The log will show something like this: ```text Email+ Threaddebug Indigo IMAP: Mailbox list: Email+ Threaddebug Indigo IMAP: Mailbox: (\Drafts \NoInferiors) "/" Drafts Email+ Threaddebug Indigo IMAP: Mailbox: (\HasNoChildren) "/" INBOX Email+ Threaddebug Indigo IMAP: Mailbox: (\NoInferiors) "/" OUTBOX Email+ Threaddebug Indigo IMAP: Mailbox: (\HasNoChildren) "/" Processed Email+ Threaddebug Indigo IMAP: Mailbox: (\Sent \NoInferiors) "/" Sent Email+ Threaddebug Indigo IMAP: Mailbox: (\Junk \NoInferiors) "/" Spam Email+ Threaddebug Indigo IMAP: Mailbox: (\Trash \HasNoChildren) "/" Trash ``` The "/" specifies that the delimiter between name parts is "/". ## Scripting Emails You can send email messages from Python scripts in one of two ways. First, you can use the built-in sendEmailTo action defined in the [Indigo Object Model](https://www.indigodomo.com/docs/server_commands#send_email): `indigo.server.sendEmailTo("my.address@example.com", subject="Subject of email", body="Body of email")` This action will use the first SMTP Server device that is found as most users will only have one. However, if you have multiple SMTP devices and/or you want to add CC or BCC address, you want to send HTML emails, you want to add attachments, you can directly script the plugin. The following function can be called from within your scripts. Be sure to put in the correct email address and the deviceID for an Email+ SMTP device. ```python def sendAlertEmail(subject, message): plugin_id = "com.indigodomo.email" plugin = indigo.server.getPlugin(plugin_id) if plugin.isEnabled(): plugin.executeAction( "sendEmail", deviceId=12345678, props={ 'emailTo':'foo@bar.com', 'emailSubject': subject, 'emailMessage': message } ) return sendAlertEmail("Test Alert", "This is only a test") ``` There are additional optional properties you can include: ```python props = { 'emailTo': 'address1, address2', 'emailCC': 'address3, address4', 'emailBCC': 'address5, address6', 'emailSubject': 'Message Subject', 'emailAttachments': 'file1, file2, file3', 'emailFormat': 'plain', # (or 'html') 'emailMessage': 'Message text' } plugin_id = "com.indigodomo.email" plugin = indigo.server.getPlugin(plugin_id) if plugin.isEnabled(): plugin.executeAction( "sendEmail", deviceId=12345678, props=props ) ``` --- Global Property Manager (https://docs.indigodomo.com/2025.2/plugins/globalpropertymanager/) --- # Global Property Manager The Global Property Manager plugin gives users the ability to add custom properties to any Indigo object. This simple plugin allows anyone to add arbitrary properties to any device object. Properties are different than states - they are somewhat hidden bits of information that can be used by Python scripts or other Plugins. For example, if you have a script that needs to be able to connect a device to another Indigo object (like an Action Group), then in the past you'd have to store it somewhere else like a file. With global properties, you can add extra properties to a device that can be accessed by your script. ## Plugin Config The Global Property Manager plugin does not have any configuration settings. ### Using the Plugin ![Global Property Manager Configuration Dialog Image](../images/global_property_manager_config_dialog.png) Before using the features of the Global Property Manager plugin, you must first enable it by going to the Indigo client `Plugins` menu item, selecting the plugin, and then selecting `Enable`. Once the plugin is enabled, all the plugin's features are accessed via the `Manage Global Object Properties...` plugin menu item. Once the dialog is opened, you can manage your custom object properties. 1. To add a property to an existing Indigo object, first select the type of object you want to edit. Use the `Object Type` dropdown menu to select among devices, action groups, variables, triggers, schedules or control pages (devices should be selected by default when the dialog is first opened). You can only add properties to objects that already exist. 2. Select the specific object you want to add a property to from the `Object` dropdown menu. 3. At the bottom of the dialog are the controls to define your property, set its value, and add it to the selected object. You must create at least a `key` name, but the value can be empty at the time the property is created. Key names must be alphanumeric, with no punctuation, and must start with a letter. Key names must also be unique within the object itself (you can add the same key name to other objects). You can use the controls in the center of the dialog to update or delete existing custom properties. You can change a property name, update its value, or delete the property entirely. Editing or deleting a property only affects the selected object. In other words, if you've added a property to multiple objects, you will need to edit each object individually. Changes made using these controls are applied immediately and can not be undone. ## Working With Custom Properties Properties added using the Global Properties Manager plugin are added to an object's `sharedProps` dictionary. Here is an example of how they appear when you print an object's details to the Indigo Events Log. ```text sharedProps : com.indigodomo.indigoserver : (dict) tags : timer, kitchen, ventilation (string) ``` Working with these values in a Python script is very straightforward. ```python dev = indigo.devices[12345678] props = dev.sharedProps tags = props['tags'] indigo.server.log(f"{tags}") ``` You can also modify `sharedProps` (add, update, delete) using the `replaceSharedPropsOnServer()` method. You should always do this by first making a copy of the `sharedProps` dictionary to avoid making inadvertent changes to other plugins' `sharedProps`. !!! warning You should be very careful when using this approach as changes made this way can not be undone. ```python dev = indigo.devices[12345678] props = dev.sharedProps props['tags'] = props['tags'] + ", foobar" dev.replaceSharedPropsOnServer(props) ``` ## Scripting Support Here's the plugin ID in case you need to programmatically restart the plugin: **Plugin ID**: com.indigodomo.indigoserver ## Support and Troubleshooting For usage or troubleshooting tips [discuss this plugin](https://forums.indigodomo.com/viewforum.php?f=406) on our forum. --- NOAA Weather (https://docs.indigodomo.com/2025.2/plugins/noaaweather/) --- # NOAA Weather This is the official weather plugin for Indigo. You may create as many "Weather Station" devices as you like. Each station will have a variety of device states that hold information that can be used in triggers, conditions, and on control pages. **NOTE**: some information may not be available for any given station - if the data isn't available in the NOAA data feed, the value of that particular state will be "- data unavailable -". [NOAA stations](http://www.weather.gov/xml/current_obs/) are primarily Airports in the US, and those tend to have the most data. There are other [NOAA stations](http://www.weather.gov/xml/current_obs/) as well but the data from them tends to be spotty at best. There is also [a map](https://madis-data.ncep.noaa.gov/MadisSurface/) you can use to locate other weather station locations. Why do we use NOAA data, which is **only available in the US**, rather than some other provider (like WeatherUnderground or WeatherBug)? Put simply, they either have severe restrictions on what commercial users can do with the data, or they require licensing fees. Neither of which we're currently prepared to deal with. NOAA data is completely free and unencumbered. You may have heard that Google has a weather API - which is sorta true. It's not a published API, and we're not big fans of using unpublished APIs (they tend to break with no warning). So, for now, NOAA is the best solution for our official weather plugin. The data quality is very good and is likely good enough for many of your purposes. You can also search the User Contribution Library for other [weather solutions.](http://www.indigodomo.com/library/index.php?keywords=weather) ## Plugin Config The plugin's config dialog has a setting to allow you to configure the display of temperature information in the Indigo UI to one of four settings: - Degrees F - Degrees C - Degrees F (Degrees C) - Degrees C (Degrees F) The second setting in the dialog allows you to turn on extra debugging information in the Event Log. Unless you're trying to debug a problem it's probably best to leave that unchecked. ### Creating a Weather Station Device The NOAA Weather Plugin allows you to create Weather Station devices. To create a new one, switch to the device view and click the `New...` button. This will bring up the device edit dialog. Select `Plugin` from the `Type:` popup. Select `NOAA Weather` from the `Plugin:` popup, and select `Weather Station` from the `Model:` popup. Click on the `Edit Device Settings...` button, and you'll see the Weather Station Config UI. Enter the NOAA station id in the text box at the top. You can click on the "Find NOAA Stations" to have a browser window open to the NOAA webpage where you can start your search. Once you've found the station ID and entered it in the text field, click "Save". If we can successfully contact NOAA and get the station data, we'll close the dialog. If not we'll show you an error indicating that we couldn't get the station's data file. You should try another station if this happens or confirm that you entered the station id correctly. From time to time, the NOAA system may not be responding to API calls and, if you've entered the station ID correctly, you may need to wait and try again later. ### Creating Weather Forecast, Current Conditions, and Weather Alerts Devices There are three additional weather device types that can provide additional weather data -- Alerts, Conditions, and Forecast data. These devices are based on more precise location information -- your lat/long coordinates (or any valid coordinates you provide) -- rather than being linked to a weather station (which may not be nearby). The creation and configuration of these devices is very straightforward. The current conditions device is meant to mimic the original weather station device as closely as possible. Linking your logic to these new device types should work, but if you decide to transition to these new device types, you should confirm they are working the way you expect. ### Weather Station and Current Conditions Device States You can trigger off of various state changes on a Weather Station (or Current Conditions Device) - like when the temperature or wind direction change. Some of these states don't make for good triggers but will provide some nice information on a control page. ![NOAA Trigger States Image](../images/noaatriggerstates.png) Weather Station device types provide you with several device states that you can use in the Trigger dialog: - `Current Condition` - The current condition in a word or two - `Current Condition Icon` - The current condition icon - Indigo ships with "NOAA Condition+.png" and it's children, one for each condition that's returned in this field. So, in the control page editor, select this state and "NOAA Condition+.png" and you'll have a nice conditions icon (images provided by NOAA). - `Dew Point °C` - Dew point in Celsius - `Dew Point °F` - Dew point in Fahrenheit - `Dew Point String` - Dew point in a human-readable string - `Heat Index °C` - Heat index in Celsius - `Heat Index °F` - Heat index in Fahrenheit - `Heat Index String` - Heat index in a human-readable string - `Humidity` - The relative humidity - `Latitude` - Latitude - `Location` - Location in human-readable form - `Longitude` - Longitude - `Observation Date/Time` - The last time NOAA updated the data. This field is in `YYYY-MM-DD HH:MM:SS` so it can be parsed by other software if needed. - `Pressure (inches)` - Pressure in inches - `Pressure (mbar)` - Pressure in millibars - `Quality Codes` - For scripters to account for the relative quality of the various weather observations (JSON). [See below for what the codes mean.](#quality-codes) - `Temperature °C` - Temperature in Celsius - `Temperature °F` - Temperature in Fahrenheit - `Temperature String` - Temperature in a human-readable string (i.e. "98.0 F (36.7 C)") - this is the state shown in the "State" column for a Weather Station device - `Time Zone` - The timezone as it applies to the observed location. - `Visibility` - Visibility in miles - `Wind Degrees` - Wind direction in degrees - `Wind Direction` - Wind direction description in something close to human-readable form - `Wind Knots` - Wind speed in knots - `Wind MPH` - Wind speed in miles per hour - `Wind String` - A description of the wind in human-readable form ### Weather Alert Device States With Weather Alert Devices, the device's states will be blank unless there are active alerts. If there are active alerts, information on up to five alerts will be available. This information is provided for informational purposes only and should not be relied upon for the purposes of personal safety. ### Weather Forecast Device States There are several device states available with the Weather Forecast Device, each having an index value keyed to individual days. Up to 14 days' worth of information may be available. ## Quality Codes The NOAA Weather plugin includes a `qualityCodes` state. This state includes information (from NOAA) on the perceived quality of select data elements. An example `qualityCodes` state value is: {"temperature": "V", "dewpoint": "V", "windDirection": "Z", "windSpeed": "Z", "windGust": "S", "barometricPressure": "V", "seaLevelPressure": "V", "visibility": "C", "precipitationLastHour": "C", "precipitationLast3Hours": "Z", "precipitationLast6Hours": "Z", "relativeHumidity": "V", "windChill": "V", "heatIndex": "V"} The various codes are as follows: | MADIS QC Information - Surface QC Data Descriptor Values | | |----------------------------------------------------------------------------------------------|---------------------------------------------------| | No QC available | | | Z | Preliminary, no QC | | Automated QC checks | | | C | Coarse pass, passed level 1 | | S | Screened, passed levels 1 and 2 | | V | Verified, passed levels 1, 2, and 3 (BEST) | | X | Rejected/erroneous, failed level 1 | | Q | Questioned, passed level 1, failed 2 or 3, where: | | Quality Levels | | | level 1 = validity | | | level 2 = internal consistency, temporal consistency, statistical spatial consistency checks | | | level 3 = spatial consistency check | | This information will give you some additional granularity with respect to how much you can trust the data. Accessing this data using a script is pretty straightforward. import json dev = indigo.devices[12345678] codes = dev.states['qualityCodes'] quality_codes = json.loads(codes) temp_quality = quality_codes['temperature'] if temp_quality in ("X", "Q"): indigo.server.log("don't trust") else: indigo.server.log("trust") ## Weather Icons ### Weather Station and Current Condition Devices You can link to the current condition icons in the "traditional" way. On a control page, select **Display Device State** and **Current Condition Icon**. Then select **As Image** and choose **NOAA Condition+.png**. This should work for both the Weather Station and Current Condition device types. #### Forecast Devices For Forecast icons, the reference is a bit different. NOAA provides a detailed description of the icon like, `/icons/land/day/tsra,30?size=medium`. The way to display these images is to link to them directly via the NOAA API itself. Select Display Refreshing Image URL, set the icon size you want (medium is 86x86) and then in the URL field, enter a URL which is a combination of text and a device state substitution. The text part is `https://api.weather.gov` and then append the substitution string for the state you want like `%%d:123456789:icon_01%%`. So the full URL would be: `https://api.weather.gov%%d:123456789:icon_01%%` (replacing 123456789 with your device ID and then the icon you want like icon_01, icon_02, etc.) You can also construct refreshing image URLs for the current conditions icons, but the construction is a little different. The full URL for those would be something like, `https://api.weather.gov/icons/land/day/%%d:123456789:currentConditionIcon%%`. If you want to have both day and night versions, you'd need to create a variable value that is equal to `day` when Indigo's `isDaylight` variable is true and equal to `night` when `isDaylight` is false. Then add a variable substitution for that part of the URL, like: `https://api.weather.gov/icons/land/%%v:12345678%%/%%d:123456789:currentConditionIcon%%`. For all these URLs, there's no need to refresh the images more than once every half hour or so because the NOAA plugin weather devices don't update more frequently than that on their own. ## Scripting Support Here's the plugin ID in case you need to programmatically restart the plugin: **Plugin ID**: com.perceptiveautomation.indigoplugin.NOAAWeather ## Support and Troubleshooting For usage or troubleshooting tips [discuss this plugin](https://forums.indigodomo.com/viewforum.php?f=97) on our forum. --- SQL Logger (https://docs.indigodomo.com/2025.2/plugins/sql_logger/) --- # SQL Logger Plugin The SQL Logger Plugin for Indigo automatically logs device state changes, variable value changes, and event log entries to either [PostgreSQL](http://www.postgresql.org/) or [SQLite](http://www.sqlite.org//). This allows Indigo to integrate with other applications or services, and allows for historical data recording. You can, for example, use PHP to dynamically generate graphs or charts of device states (like temperature) stored in a PostgreSQL database. By default, OS X 10.5 and higher includes the libraries needed to use SQLite, which makes using the SQLite option fast. Although more complicated, we have also put together basic instructions for using the more powerful PostgreSQL database server. ## Configuring SQL Logger with SQLite Choose the `Plugins->SQL Logger->Configure...` menu item, then select `SQLite` as the database type. By default, Indigo will create a SQLite database file inside the logs folder: `/Library/Application Support/Perceptive Automation/Indigo 6/Logs/indigo_history.sqlite` ### Configuring SQL Logger with PostgreSQL PostgreSQL is not included with the Mac OS X install (note it is included on more recent OS X Server installs), but it is free and there are some package installers available. Here are the basic steps for installing it: - Download a [PostgreSQL installer](https://www.postgresql.org/download/macosx/). Note there are other installers available elsewhere as well. - Add the path to the PostgreSQL binary to your bash profile file. From the Terminal copy/paste: `echo 'export PATH=$PATH:/Library/PostgreSQL/bin' >> ~/.bash_profile` - Next, open the System Preferences and choose the PostgreSQL Server icon that was added. You should now be able to start the server. Note that some installers seem to include a Server Manage.app application, but they may not work correctly. However, the panel in the System Preferences does appear to work. - From the command line you can now try to connect to the server via: `psql -U postgres` - Next, select the `Plugins->SQL Logger->Configure...` menu item, then select `PostgreSQL` as the database type. By default, Indigo will connect to the PostgreSQL server running on the same Mac (`127.0.0.1`) using the default PostgreSQL username of `postgres`, and will automatically create a new database named `indigo_history`. Your PostgreSQL install might also support local connections directly using the host name `/var/pgsql_socket/.s.PGSQL.5432`. ### Logging Options From the SQL Logging configuration dialog (`Plugins->SQL Logger->Configure...` menu item), you can choose which information the plugin should store in the database and specify if older data should automatically be pruned: ![Plugin SQL Logger Configuration Image](../images/plugin_sqllogger_config1.png) Automatically pruning data from the tables will help keep the database size more manageable, but you can turn the option off if you want to manually clean up the database. Auto deleting unused tables will have the plugin remove any tables for devices or variables that are not defined in the current Indigo database. However, note that this means that if you switch Indigo database files then the device and variable history stored for the previous database will automatically be deleted from the SQLite/PostgreSQL databases. So turn these options off if you use multiple Indigo databases. ### Database Table Format Indigo creates a unique table for every device and variable to track its state/value history. Example database table names include: `device_history_9734822, device_history_12452348, device_history_8734522` and `variable_history_9872345, variable_history_3411246` To find the specific device or variable IDs used in the table name, right-click on the device (or variable) inside Indigo and choose the `Copy ID` menu item. Note the SQL Logger also writes to the Event Log any time it creates a new table and shows what table name was created for a specific device or variable. Columns are automatically created for the tables for every state used by that particular device. For example, here is the device history table for a dimmer switch, `device_history_9734822`: | ts | brightnesslevel | onoffstate | |---------------------|-----------------|------------| | 2012-05-16 16:00:32 | 0 | f | | 2012-05-16 16:01:44 | 74 | t | | 2012-05-16 16:01:44 | 29 | t | | 2012-05-16 16:44:46 | 0 | f | | 2012-05-16 16:45:03 | 29 | t | | 2012-05-16 16:45:35 | 0 | f | | 2012-05-16 18:20:09 | 28 | t | | 2012-05-16 19:01:57 | 29 | t | | 2012-05-16 19:15:12 | 38 | t | | 2012-05-16 19:15:13 | 48 | t | And here is the table created for a temperature humidity sensor, `device_history_12452348`: | ts | temperature | humidity | |---------------------|-------------|----------| | 2012-05-16 16:00:32 | 73 | 85 | | 2012-05-16 16:01:44 | 74 | 85 | | 2012-05-16 16:01:44 | 73 | 85 | | 2012-05-16 16:44:46 | 72 | 86 | | 2012-05-16 16:45:03 | 71 | 87 | | 2012-05-16 16:45:35 | 68 | 87 | | 2012-05-16 18:20:09 | 65 | 88 | | 2012-05-16 19:01:57 | 67 | 87 | | 2012-05-16 19:15:12 | 68 | 86 | | 2012-05-16 19:15:13 | 69 | 86 | Using SQL you can query the table for all the defined columns. Variable tables are similarly created -- one table per variable. However, they always have the same columns: `ts` and `value`. Here is an example table tracking a variable for alarmMode: | ts | value | |---------------------|----------| | 2012-05-16 16:00:32 | Idle | | 2012-05-16 16:01:44 | Arm Home | | 2012-05-16 16:01:44 | Idle | | 2012-05-16 16:44:46 | Arm Away | Lastly, a single table is created to track all event log entries, `eventlog_history`. ### Example Queries Below are some example SQLite and PostgreSQL queries. Note that the table names below are examples, and must be modified to match your unique table names. #### PostgreSQL Queries PostgreSQL query to retrieve all event log history: `psql indigo_history postgres -c "SELECT * FROM eventlog_history;"` PostgreSQL query to retrieve all device history for a specific device: `psql indigo_history postgres -c "SELECT * FROM device_history_692773228;"` PostgreSQL query to retrieve the timestamp, rain rate and rain total from an Oregon Scientific rain sensor: `psql indigo_history postgres -c "SELECT ts, rainrate, raintotal, currentdaytotal FROM device_history_849210623;"` #### SQLite Queries For SQLite queries, first change to the directory in which the database file resides: `cd /Library/Application\ Support/Perceptive\ Automation/Indigo\ 6/Logs/` SQLite query to retrieve all event log history: `sqlite3 -header -column indigo_history.sqlite "SELECT * FROM eventlog_history;"` SQLite query to retrieve all device history for a specific device: `sqlite3 -header -column indigo_history.sqlite "SELECT * FROM device_history_692773228;"` SQLite query to retrieve the timestamp, temperature, and humidity from an Oregon Scientific sensor: `sqlite3 -header -column indigo_history.sqlite "SELECT datetime(ts,'localtime'), temperature, humidity FROM device_history_167703743;"` Note that when selecting the timestamp from a SQLite table you must use the notation `datetime(ts,'localtime')` so that the internally stored GMT time is translated to your local time. ## Events ![SQL Logger Event Image](../images/plugin_sqllogger_event.png) The SQL Logger provides a useful event (even if it's not logging anything): `Error in Event Log`. This event will fire whenever an error (generated by the Indigo Server, by plugins, or both) appears in the event log. You can perform any actions (like sending an email, etc.). `Indigo Internal Errors` are errors generated by the IndigoServer directly - so errors from Insteon and X10 devices, errors with the built-in actions/events (sending emails), etc. These do not include errors that plugins generate or errors that are inserted from scripts. To use the `Specific Type` event type, you'll need to use the error type that's shown in the event log window. The error type is the beginning part of an event log line. Each event log entry has a beginning part, then a series of spaces, then the message. The beginning part is the event type. Error event types will always end in the word "Error". Here are some examples: ```text Aug 9, 2012 3:38:10 AM My Type Error this is an error from a script NOAA Weather Plus Error Error parsing XML from NOAA for device Weather Forecast: not well-formed (invalid token): line 1, column 111 ``` In the configuration dialog for `Error in Event Log`, when you select `Specific Type` from the `Errors to monitor for:` popup, a text field with the label `Event Type` will show. You'll enter the text shown above - so for instance if you want to monitor for the first error, you'd enter "My Type Error" in the text field (notice no spaces/tabs before or after). Then, every time an error of that type is detected by the SQL Logger plugin, it will fire that trigger. This is primarily useful for plugin errors although if you have scripts that generate errors it could be used for those as well. For plugins (unless the developer decides otherwise), errors will be generated using the plugin's name with " Error" appended. You can enter text in the String to Match field and the action will attempt to match it against the text in the log message. For instance, entering *Office Lamp* in the field will cause the event to fire only if "Office Lamp" is in the field (case-sensitive). You can also specify a regular expression for more advanced text matching. ## Scripting Support Here's the plugin ID in case you need to programmatically restart the plugin: **Plugin ID**: com.perceptiveautomation.indigoplugin.sql-logger ## Support and Troubleshooting For usage or troubleshooting tips [discuss this plugin](https://forums.indigodomo.com/viewforum.php?f=98) on our forum. --- Timers and Pesters (https://docs.indigodomo.com/2025.2/plugins/timersandpesters/) --- # Timers and Pesters As the name implies, this plugin implements a very simple timer device object that works almost exactly like a manual kitchen timer (if the kitchen timer had the ability to pause and resume). Pesters are little mini schedules that can cycle for some fixed number of times. They are lightweight versions of Indigo [Schedules](../user/concepts/schedules.md#schedules). **Note**: make sure that the plugin is enabled or the options below will not be available. ## Timers Timers are very simple to use - they're pretty much like kitchen timers. The notable difference is that timers in Indigo are created with their start/run time as part of the timer device. This means when you start one it will use the time specified in the timer itself (this can be changed later - see the actions below). So, first, create a timer device. ## Create a Timer ![Plugin Timers New Timer Image](../images/plugin_timers_new_timer.png) A timer is like any other device in Indigo - it shows up in the device list, it has states, etc. 1. Select `DEVICES` (or one of its subfolders) from the [Outline View](../user/mac-client/home-window.md#outline-view) 1. Click on the `New...` button 1. In the resulting `Create New Device` dialog, select `Timers and Pesters` from the `Type:` popup 1. Select `Timer` from the `Model:` menu 1. In the resulting `Configure Timer` dialog, specify the amount of time the timer will default to when started and the time increments 1. Click `Save` 1. Name the timer something useful and close the `Create New Device` dialog The timer device you created has 6 device states: - `Timer Start Value` - the default value of the timer when the `Start Timer` action is selected. It's set when you create a timer device and may be modified either via the device dialog or by using the `Set Timer Start Value` action. - `Timer Status String` - a human-readable string representing the state of the timer. This is what's shown in the "State" column in the device table and can be displayed in a control page. The format is this: "Active with D:HH:MM:SS left", "Paused with D:HH:MM:SS left", "Inactive". - `Timer Status` - the status of a timer in a way that's useful for image selection, scripts, etc. Values are one of: "inactive", "active", "paused". - `Time Left in Seconds` - the amount of time remaining in seconds for the timer. Inactive timers will have a value of 0. - `Time Left in Minutes` - the amount of time remaining in minutes for the timer. Inactive timers will have a value of 0. - `Time Left in Hours` - the amount of time remaining in hours for the timer. Inactive timers will have a value of 0. - `Time Left in Days` - the amount of time remaining in days for the timer. Inactive timers will have a value of 0. ## Use a Timer There are two things you need to do to use a timer: control the timer (start, stop, pause, etc.) and trigger when a timer expires (when it runs out of time naturally as opposed to being stopped intentionally). ### Timer Actions ![Plugin Timers Actions Image](../images/plugin_timers_actions.png) To operate/control the timer, you use the following actions (also available interactively from the plugin's submenu): - `Start Timer` - use this action to start a timer. The timer device will automatically start counting down and the state will change. When the countdown completes, the status will be set to "inactive" and any triggers that are triggering off of the `Timer Expired` event for this timer will be fired. The action will only start the timer when it's inactive and will be ignored when in any other state. - `Restart Timer` - use this action to restart a timer using the default time set for the timer. The action will always work regardless of the timer's current state. - `Pause Timer` - as the name implies, this action will pause the selected timer. The amount of time will not be modified so that you can resume the timer at will. The action will only pause an active timer and will be ignored when the timer is in any other state. - `Resume Timer` - use this action resume a previously paused timer. The action will only resume a paused timer and will be ignored when the timer is in any other state. - `Stop Timer` - use this action to stop a timer prematurely. The state becomes "Inactive" and all the time left states will be set to 0. It will **not** cause any `Timer Expired` triggers to fire. The action will be ignored if the timer is already inactive. - `Set Timer Start Value` - use this action to set the default timer value without actually starting the timer. If the timer is running when it has its start value changed, it will stop running. ### Timer Event ![Plugin Timers Events Image](../images/plugin_timers_events.png) There is also one custom event that you can use in a Trigger: `Timer Expired`. This allows you to easily know when a timer runs out of time versus when it's explicitly stopped (by the `Stop Timer` action described above). This is synonymous to when a kitchen timer starts beeping/ringing: if it runs out of time it rings but if you expressly turn it off then it doesn't. To create a timer expired trigger: 1. Select `TRIGGERS` (or one of its subfolders) from the [Outline View](../user/mac-client/home-window.md#outline-view) 1. Click on the `New...` button 1. In the resulting `Create New Trigger` dialog, select `Timers and Pesters Event` from the `Type:` popup 1. Select `Timer Expired` from the `Event:` menu 1. In the resulting `Configure Timer Expired` dialog, specify the timer to watch 1. Click `Save` 1. Add any [Conditions](../user/concepts/conditions.md#conditions) you need 1. Add all the [Actions](../user/concepts/actions.md#actions) you want to perform when the timer expires 1. Name the trigger something useful and close the `Create New Trigger` dialog ## Timer Uses So, what else would you do with the timer aside from triggering off of its expiration? You can display the various states on a control page and use those states in triggers and conditions as well. You could also show the countdown on a control page if that information is useful. You could even have an audio countdown by speaking the time left on the timer. ## Pesters ![Plugins Timers and Pesters Actions Image](../images/plugin_timers_pester_actions.png) Another very useful feature of this plugin is the "Create Pester" action. This action will create a little mini timer which will repeat a specified number of times, executing an action group each time through and will execute an optional action group at the end of its final occurrence. So - say you want to have your computer announce that it's time to take out the trash. However, if you're like me, once is never enough - I need to be nagged about it several times. Create a pester that repeats every 30 seconds for 5 times (so it doesn't go forever) that executes an action group that tells you to take out the trash. The final time can send an SMS to your phone. Pesters can be thought of as transient schedules - they only live for a limited amount of time. You give a pester a name so that you can later cancel it, or you can cancel all pesters. ## Scripting Support As with all plugins, actions defined by this plugin may be executed by [Python scripts](../scripting/tutorial.md#scripting-indigo-plugins). Here's the information you need to script the actions in this plugin. **Plugin ID**: com.perceptiveautomation.indigoplugin.SimpleTimer ## Action specific properties ### Start Timer **Action id**: startTimer No properties for scripting required. Example: ```python t_id = "com.perceptiveautomation.indigoplugin.timersandpesters" # Get a plugin object given the plugin id: timerPlugin = indigo.server.getPlugin(t_id) if timerPlugin.isEnabled(): timerPlugin.executeAction("startTimer", deviceId=1604521627) ``` #### Pause Timer **Action id**: pauseTimer No properties for scripting required. Example: ```python t_id = "com.perceptiveautomation.indigoplugin.timersandpesters" # Get a plugin object given the plugin id: timerPlugin = indigo.server.getPlugin(t_id) if timerPlugin.isEnabled(): timerPlugin.executeAction("pauseTimer", deviceId=1604521627) ``` #### Restart Timer **Action id**: restartTimer No properties for scripting required. Example: ```python t_id = "com.perceptiveautomation.indigoplugin.timersandpesters" # Get a plugin object given the plugin id: timerPlugin = indigo.server.getPlugin(t_id) if timerPlugin.isEnabled(): timerPlugin.executeAction("restartTimer", deviceId=1604521627) ``` #### Resume Timer **Action id**: resumeTimer No properties for scripting required. Example: ```python t_id = "com.perceptiveautomation.indigoplugin.timersandpesters" # Get a plugin object given the plugin id: timerPlugin = indigo.server.getPlugin(t_id) if timerPlugin.isEnabled(): timerPlugin.executeAction("resumeTimer", deviceId=1604521627) ``` #### Stop Timer **Action id**: stopTimer No properties for scripting required. Example: ```python t_id = "com.perceptiveautomation.indigoplugin.timersandpesters" # Get a plugin object given the plugin id: timerPlugin = indigo.server.getPlugin(t_id) if timerPlugin.isEnabled(): timerPlugin.executeAction("stopTimer", deviceId=1604521627) ``` #### Set Timer Start Value **Action id**: setTimerStartValue Properties for scripting: | *`timer`* | the ID of the timer device | |----------------|----------------------------------------------------------------------------------------------------------------------| | *`amount`* | a positive integer representing the initial countdown amount | | *`amountType`* | one of the following that represents the units of measure of the amount field: "seconds", "minutes", "hours", "days" | Example: ```python t_id = "com.perceptiveautomation.indigoplugin.timersandpesters" # Get a plugin object given the plugin id: timerPlugin = indigo.server.getPlugin(t_id) if timerPlugin.isEnabled(): timerPlugin.executeAction( "setTimerStartValue", deviceId=1604521627, props={ 'amount':'30', 'amountType':'minutes'} ) ``` ## Support and Troubleshooting For usage or troubleshooting tips [discuss this plugin](https://forums.indigodomo.com/viewforum.php?f=99) on our forum. --- Alexa (https://docs.indigodomo.com/2025.2/plugins/alexa/) --- # Alexa [Alexa](https://www.amazon.com/b?node=21576558011) is a smart assistant voice system from Amazon which is [integrated into many devices](https://www.amazon.com/b?node=15443147011), including the [Echo devices from Amazon](https://www.amazon.com/smart-home-devices/b/?ie=UTF8&node=9818047011&ref_=sv_devicesubnav_1), sound bars and smart speakers, watches, thermostats, TVs, headphones, etc., from a variety of manufacturers. Amazon has enabled integration with third parties like Indigo to integrate smart home devices so that you can use voice commands to control them (aka a *Smart Home Skill*). **In English-speaking regions**, we've also created a way to execute action groups and have the value of variables read to you (aka a *Custom Skill*). **Note**: **this capability was added in Indigo 2021.1** so if you are using an older version you will need to upgrade. If your Indigo Up-to-Date Subscription is active, and **you have your Indigo Reflector configured and working**, you just need to install **Indigo 2021.1** or later. You also **must have** OAuth enabled in the [Start Local Server dialog](../../user/getting-started/installation.md#starting-indigo-server) for Alexa to work. ## In This Section - **[Smart Home Skill](smart-home-skill.md)** — voice control of devices. - **[Custom Indigo Skill](custom-skill.md)** — run action groups and read variables by voice. - **[Plugin Operations](operations.md)** — publishing devices and managing what Alexa sees. - **[Migrating from the Alexa-Hue Bridge](migrating.md)** — moving off the legacy plugin. - **[Troubleshooting](troubleshooting.md)** — when discovery or linking misbehaves. --- Custom Indigo Skill (https://docs.indigodomo.com/2025.2/plugins/alexa/custom-skill/) --- # Custom Indigo Skill !!! warning "NOTE" **the Custom Skill is available in English-speaking regions only**. When you enable the **Indigo Smart Home Skill** in the Alexa app, you also gain access to some custom functionality that's specific to Indigo. We enable you to hear the value of a variable (complete with speech markup), hear a list of all of your variables, and run Action Groups. Custom skill requests require that you preface your requests by saying *Alexa, tell Indigo* or *Alexa, ask Indigo*. These are called **invocations** and are needed so that Alexa can know where to direct the request. It's not needed for Smart Home Skills (ones that deal with Indigo devices as described above as well as devices from other skills) because Alexa knows details about each individual device and where to send the request based on that information. !!! warning "NOTE" Unfortunately, the invocation for the skill is currently different in different regions. - US, UK - "indigo" is the invocation - CA, AU - "indigo home" is the invocation We are attempting to get Amazon to help us correct this, but we are unsure if it's going to be possible or not at this time. ## Speaking the Value of a Variable !!! note "Note" When naming variables for use with Alexa, you should use underscores_to_separate_words. That is how the plugin will map separate words from Alexa onto variables (which can't contain spaces). To hear the value of a variable, just ask Indigo for it. There are a variety of ways to ask, here are a few (variable names are in quotes, underscores are treated as spaces): - *Alexa, ask Indigo to get "current_weather_conditions"* - *Alexa, ask Indigo to look up "current_weather_conditions"* - *Alexa, ask Indigo to read "current_weather_conditions"* - *Alexa, ask Indigo to say "current_weather_conditions"* - *Alexa, ask Indigo to speak "current_weather_conditions"* - *Alexa, ask Indigo to tell me "the_status_of_the_house"* - *Alexa, ask Indigo for the value of variable "current_weather_conditions"* - *Alexa, ask Indigo the value of "current_weather_conditions"* - *Alexa, ask Indigo the current value of "weather_conditions"* You can also use *tell Indigo* interchangeably with *ask Indigo*. The default response (in the default voice) will be: *The value of variable "current weather conditions" is "mostly cloudy"* You can adjust how Alexa responds in a couple of ways: - You can use [Speech Synthesis Markup Language (SSML)](https://developer.amazon.com/en-US/docs/alexa/custom-skills/speech-synthesis-markup-language-ssml-reference.html) to mark up the text in a variable to customize how Alexa reads back the value. Any variable value that begins with a less than sign (<), which we use as a key that the text contains SSML, will be read exactly as specified in the variable without any additional words (i.e. *The value of variable* won't be prepended). If your markup text doesn't naturally begin with a markup tag, wrap the entire string in `` tags. - Variable values that contain the device (%%d:deviceId:deviceState%%) or variable (%%v:variableId%%) markup values will be correctly substituted by Indigo. - You may also specify in the plugin's config that all variable values should be read exactly as they are stored without the added verbiage. ### Get a List of Variables You can hear a list of your variables as well. Here are a variety of ways to ask: - *Alexa, ask Indigo to get my variable list* - *Alexa, ask Indigo to list my variables* - *Alexa, ask Indigo to list all variables* - *Alexa, ask Indigo what variables are available* You can also use *tell Indigo* interchangeably with *ask Indigo*. ### Execute an Action Group You can tell Indigo to execute an action group. Here are a variety of ways to ask (action group names are in quotes): - *Alexa, tell Indigo to arm "the kitchen zone"* - *Alexa, tell Indigo to do "my favorite thing"* - *Alexa, tell Indigo to execute "toggle music"* - *Alexa, tell Indigo to launch "rocket"* - *Alexa, tell Indigo to make "the house secure"* - *Alexa, tell Indigo to perform "routine maintenance"* - *Alexa, tell Indigo to play "my favorite playlist"* - *Alexa, tell Indigo to reset "the alarm"* - *Alexa, tell Indigo to restart "the laundry timer"* - *Alexa, tell Indigo to restore "the standard speaker set"* - *Alexa, tell Indigo to run "routine maintenance"* - *Alexa, tell Indigo to start "the laundry timer"* - *Alexa, tell Indigo to set "the playlist to classic rock"* Action group names can contain letters, numbers, and spaces only. If you say "playlist eighties music", the action group name will need to be "playlist 80s music". If you are unsure how to name an action group, just attempt to execute it with one of the above utterances. If Indigo doesn't find an action group matching what you said, it will log it to the Event Log window like this: `Alexa Error an action group named 'kitchen zone' does not exist in your indigo server` That will tell you the text that Alexa sent to the plugin, so you can name your action group exactly what's in single quotes and the next time it will work. !!! note What you say before the action group name will work for any action group name. We picked the above examples only because they are normal complete English sentences, but you could just as well say *Alexa, tell Indigo to arm "the laundry timer"* and the effect would be the same as saying *Alexa, tell Indigo to start "the laundry timer"*. You can also use *tell Indigo* interchangeably with *ask Indigo*. --- Migrating from Alexa-Hue Bridge (https://docs.indigodomo.com/2025.2/plugins/alexa/migrating/) --- # Migrating from the Alexa Hue Bridge plugin If you are using the Alexa-Hue Bridge plugin, you will definitely want to read through this section. We know there are users that have relied on the Alexa-Hue Bridge plugin even though it requires Echo hardware that is no longer available. It was a great stop-gap, and we really appreciate everyone who contributed to maintaining it through the years, particularly forum user @Autolog. If you are migrating, we highly recommend that you do a full switch rather than try to use both while switching. While it's possible to use both, doing a full switch will help you avoid a variety of issues, including Alexa caching, device name conflicts, etc. ## Full Switch Doing a full switch is pretty simple, just follow these steps: 1. Disable the Alexa-Hue Bridge plugin. 1. In the [Alexa website](https://alexa.amazon.com/spa/index.html#appliances), remove all devices. The simplest way is to click the `Remove All` button at the bottom. Note, if you are using other smart home skills, using that button will also cause those devices to be forgotten, so when you do a discover later you'll need to perform any steps needed to make discover work on for that skill. 1. In Indigo, follow the directions above in the [Making a Device Available in Alexa](../alexa/smart-home-skill.md#making-a-device-available-in-alexa) to publish your devices. 1. Once you have all your devices published, you can confirm that they are all published by selecting the `Plugins->Alexa->Show Device Publications` menu item and it will print the list of publications in the Event Log window. 1. In the Alexa app, website, or using an Alexa enabled device, click/touch the Discover button or say "discover devices". This should make all of your devices available in Alexa. Whenever you ask an Alexa device to discover, you will see the following Event Log line followed by a summary of publications: `Alexa Alexa discovery request received, assembling reply...` ### Partial Switch If you want to attempt to switch one at a time, the process is more complicated and somewhat error-prone. The general process is: 1. Disable the device from the Alexa-Hue Bridge plugin (see [the docs for the plugin](https://github.com/IndigoDomotics/alexa-hue-bridge/wiki) for details). This is very critical in order to avoid complications/confusion later. 1. In the [Alexa website](https://alexa.amazon.com/spa/index.html#appliances), remove that specific device. 1. In Indigo, follow the directions above in the [Making a Device Available in Alexa](../alexa/smart-home-skill.md#making-a-device-available-in-alexa) to publish your device. Be sure to confirm/select the appropriate subtype. 1. In the Alexa app, website, or using an Alexa enabled device, click/touch the Discover button or say "discover devices". Hopefully, Alexa will find your device and it will work properly. Unfortunately, sometimes that doesn't work. We believe that there are some caching issues within the Alexa environment where removing a device doesn't fully remove a device. If you experience this, you may need to remove the device again and wait a while before rerunning discovery. It may even require that you disable the Alexa plugin, rerun discovery, then enabling the Alexa plugin, and running discovery again. We haven't been able to find the silver bullet for this so it's a bit of trial and error. --- Plugin Operations (https://docs.indigodomo.com/2025.2/plugins/alexa/operations/) --- # Alexa Plugin operations ## Plugin Startup When the plugin starts up, you will see a list of what's published to Alexa in the Event log. It will look something like this: ```text Started plugin "Alexa 2024.1.0" Alexa Finding devices to publish to Alexa... Alexa ... '010 - Smart Fan Control (14287)' published as 'office fan' Alexa ... '016 - Plug-In Appliance Module (ZL-PA-100)' published as 'office lamp' Alexa ... '038 - Lamp Module (AD130)' published as 'blinds' Alexa ... 'automatic door' published Alexa ... 'FanLinc - Fan' published as 'ceiling fan' Alexa ... 'FanLinc - Light' published as 'fan light' Alexa ... 'Fortrezz Strobe' published as 'strobe' Alexa ... 'Hue Bulb' published as 'office bulb' Alexa ... 'Insteon Dimmer' published as 'valve' Alexa ... 'Insteon On/Off' published as 'back door' Alexa ... 'Insteon Thermostat' published as 'thermostat' Alexa ... 'Kasa Plug' published as 'garage door' Alexa ... 'Office Siren' published Alexa ... 'outlet' published Alexa ... 'Simple Virtual On/Off' published as 'simple switch' Alexa A total of 15 devices are currently published to Alexa Alexa Warning If you can't control a device, rerun discover from your Alexa device. ``` ### Showing Publications You can also select the `Plugins->Alexa->Show Device Publications` menu item to show this list with more details and sorted by the Alexa name: ```text Alexa Currently published devices (Alexa name first if different than Indigo name): Alexa 'Office Siren' ('Office Siren') - Type: RelayDevice - Subtype: Siren Alexa 'automatic door' ('automatic door') - Type: RelayDevice - Subtype: Door Controller Alexa 'back door' ('Insteon On/Off') - Type: RelayDevice - Subtype: Lock Alexa 'blinds' ('038 - Lamp Module (AD130)') - Type: DimmerDevice - Subtype: Blind Alexa 'ceiling fan' ('FanLinc - Fan') - Type: SpeedControlDevice - Subtype: None Alexa 'fan light' ('FanLinc - Light') - Type: DimmerDevice - Subtype: Dimmer Alexa 'garage door' ('Kasa Plug') - Type: RelayDevice - Subtype: Garage Controller Alexa 'office bulb' ('Hue Bulb') - Type: DimmerDevice - Subtype: Color Bulb Alexa 'office fan' ('010 - Smart Fan Control (14287)') - Type: DimmerDevice - Subtype: Fan Alexa 'office lamp' ('016 - Plug-In Appliance Module (ZL-PA-100)') - Type: RelayDevice - Subtype: Plug-In Alexa 'outlet' ('outlet') - Type: RelayDevice - Subtype: Outlet Alexa 'simple switch' ('Simple Virtual On/Off') - Type: RelayDevice - Subtype: Switch Alexa 'strobe' ('Fortrezz Strobe') - Type: RelayDevice - Subtype: Plug-In Alexa 'thermostat' ('Insteon Thermostat') - Type: ThermostatDevice - Subtype: None Alexa 'valve' ('Insteon Dimmer') - Type: DimmerDevice - Subtype: Valve ``` ### Discovery Requests from Alexa When the plugin receives a discover request from the Alexa servers, you will see something similar to this in the Event Log: ```text Alexa Alexa discovery request received, assembling reply... Alexa ...'ceiling fan' ('FanLinc - Fan') - Type: SpeedControlDevice - Subtype: None Alexa ...'simple switch' ('Simple Virtual On/Off') - Type: RelayDevice - Subtype: Switch Alexa ...'upstairs siren' ('Fortrezz Siren') - Type: RelayDevice - Subtype: Siren Alexa ...'outlet' - Type: RelayDevice - Subtype: Outlet Alexa ...'fan light' ('FanLinc - Light') - Type: DimmerDevice - Subtype: Dimmer Alexa ...'office fan' ('010 - Smart Fan Control (14287)') - Type: DimmerDevice - Subtype: Fan Alexa ...'back door' ('Insteon On/Off') - Type: RelayDevice - Subtype: Lock Alexa ...'automatic door' - Type: RelayDevice - Subtype: Door Controller Alexa ...'Office Siren' - Type: RelayDevice - Subtype: Siren Alexa ...'thermostat' ('Insteon Thermostat') - Type: ThermostatDevice - Subtype: None Alexa ...'blinds' ('038 - Lamp Module (AD130)') - Type: DimmerDevice - Subtype: Blind Alexa ...'office lamp' ('016 - Plug-In Appliance Module (ZL-PA-100)') - Type: RelayDevice - Subtype: Plug-In Alexa ...'office bulb' ('Hue Bulb') - Type: RelayDevice - Subtype: Plug-In Alexa ...'strobe' ('Fortrezz Strobe') - Type: RelayDevice - Subtype: Plug-In Alexa ...'garage door' ('Kasa Plug') - Type: RelayDevice - Subtype: Garage Controller Alexa ...'outside lights' ('Outdoor Appliance Module (45604)') - Type: RelayDevice - Subtype: Plug-In Alexa ...'valve' ('Insteon Dimmer') - Type: DimmerDevice - Subtype: Valve Alexa Found 17 devices to publish, replying ``` ### Command Requests from Alexa When an Alexa command is received, you will see something similar to this in the Event Log: ```text Alexa turning on 'Hue Bulb' Sent Hue Lights "Hue Bulb" on to 100 at ramp rate 2.0 sec. This will allow you to easily see that the change was the result of an Alexa request. This will show for every change that the Alexa plugin makes. ``` --- Smart Home Skill (https://docs.indigodomo.com/2025.2/plugins/alexa/smart-home-skill/) --- # Smart Home Skill !!! Note The Alexa skill is currently available in the Alexa skill stores for most of the regions in which we sell Indigo (US, Canada, UK, the Netherlands, Australia, New Zealand, France, Germany, Spain, Italy). We have implemented a skill which you can enable that will provide standard smart home device control. This enables an Alexa user to control devices in the exact same way regardless of how that device is connected to Alexa. This section will give you an overview of the device types in Indigo that you can publish to Alexa for control. We don't automatically publish your Indigo devices for a variety of reasons, but primarily as a security measure. You must make an explicit decision to enable voice control of a device. You must have your Indigo Reflector activated in order to proceed. You enable the Indigo Skill in the Alexa app (this is the iOS App as of June 2021, it may change): 1. Open the Alexa App. 1. Go to the Devices list and click the link to Your Smart Home Skills. 1. Click Enable Smart Home Skills. 1. Tap the Search icon. 1. Search for "Indigo Smart Home". You should see a skill named **Indigo Smart Home Skill** in English-speaking regions and **Indigo Smart Home** everywhere else, with an icon that matches the logo of the Mac Client. 1. Add the skill. 1. Link the skill to your Indigo Account & license (currently by clicking/tapping the Settings button). 1. Log in using your Indigo Account username and password. 1. On the next page (the authorization page), if you have multiple Indigo Licenses, make sure you have the correct one selected (you must have an active Indigo Up-to-Date subscription); you can link one license to one Alexa (Amazon) account. If you see something else or don't see a license you were expecting to see, check the [Account Linking Issues](troubleshooting.md#account-linking-issues) troubleshooting section below for help. ## Alexa Store Links Here are direct links to the skills in their respective Alexa stores: - [Australia](https://www.amazon.com.au/dp/B097GBHBZG) - [Canada](https://www.amazon.ca/dp/B097GBHBZG) - [France](https://www.amazon.fr/dp/B09DCZDRRS) - [German](https://www.amazon.de/dp/B09DCZDRRS) - [Italy](https://www.amazon.it/dp/B09DCZDRRS) - [Spain](https://www.amazon.es/dp/B09DCZDRRS) - [United Kingdom](https://www.amazon.co.uk/dp/B097GBHBZG) - [United States](https://www.amazon.com/dp/B097GBHBZG) ## Making a Device Available in Alexa To make a device available to Alexa, you must explicitly publish it. Use the`Plugins->Alexa->Manage Device Publications...` menu item to open the publication dialog: ![Alexa Manage Publications Image](../../images/alexa_manage_publications.png) In this dialog, you will select a device from the popup. Note that the popup is divided into two sections: ![Alexa Manage Device List Image](../../images/alexa_manage_device_list.png) The top part of the list are Indigo devices that can be published to Alexa, but haven't yet. If you select one of these devices, the dialog will show you the appropriate options for that device. For all devices, you can specify an Alternate Name which will be used in Alexa when you operate it (i.e. Alexa, turn on *office lamp*). If you leave this field blank, the actual Indigo device name will be used. Note that Alexa device names can only contain letters, numbers, and spaces. Some devices will also show a Type popup: ![Alexa Type Popup Image](../../images/alexa_type_popup.png) You will use this popup to tell Alexa more specifically what kind of device it is. We will attempt to look at various other characteristics of the device to select what we believe is the appropriate type, but you may select any from the list, and we will relay that information to Alexa so you can control it using appropriate terminology (see below for specifics). The bottom part of the list are devices that you have already published to Alexa. If you select one of these devices, you can edit or unpublish the device. !!! warning Be sure to click the Save button before moving on or your changes won't be saved. Also, if you make any changes, **you will need to rerun discovery from an Alexa device or the Alexa app before those changes will be reflected**. As a reminder, you must either click the discover devices button in the Alexa apps or ask "Alexa, discover devices" of an Alexa enabled device. ## Device Types Supported The following device types are supported: - On/Off devices (sometimes referred to as relay) - simple appliance control plug-in modules, outlets, and switches are the most common type. This also includes Locks and Garage Doors. - Dimmer devices - dimmer switches and plug-in modules are the most common, though in Indigo there are a variety of other device types that Indigo sees as dimmers: Blinds/Drapes, Fans, Bulbs (including color), etc. - Thermostats - Fans - Indigo natively only supports the Insteon FanLinc fan as a proper fan device (with the right controls) - Z-Wave fan controllers are currently implemented as dimmers, but they will work as fans in Alexa if configured correctly. ### On/Off Devices This type of device has a boolean value, most often on/off or open/closed. To control from Alexa, you use phrases like: - *Alexa, turn on office lamp* - *Alexa, turn off bedroom fan* - *Alexa, open garage door* When you select an Indigo device that is a standard On/Off device to publish, you will get a popup that will help us tell Alexa how to control your device: ![Alexa Relay Subtypes Image](../../images/alexa_relay_subtypes.png) We will take a guess at what the specific type of the device is, but we won't always guess correctly. For instance, if there isn't anything about your device that Indigo can determine, it will just automatically select `Switch`. You can override that setting however to make the device best match what it does. The options are: - Door Bell - as of this release, this will only allow you to turn on/off the doorbell, not accept ring events. We will look at adding that in a future release. - Door Controller - if you have a device that physically operates a door (but not a garage door), you can use this device type. You can then say things like: - *Alexa, open the front door* - *Alexa, what's the status of the front door* - Garage Controller - this is exactly what you think it is. You will be able to open/close (raise/lower) your garage door. Alexa uses this control type as a more secure option. When Alexa first discovers a garage door, it will not allow you to control the door by voice. Rather, it will tell you to control it manually or go to the Alexa app and configure the door for use with voice control. In the settings for the door in the Alexa app, you will be able to enter a 4 digit PIN for extra security. When you ask Alexa to open or raise the door, you will be prompted for your PIN code. If you have an automatic door controller (specified above) and want the extra security of a PIN code, you can select Garage Controller as well, and it should work just like a garage door. PIN codes are set per device so each can be different. You can say things like: - *Alexa, open the garage door* - *Alexa, is the garage door open* - Lock - similar to a garage door, a lock device will need to have a PIN assigned for it in the Alexa app. Once that's done, when you attempt to unlock the door (*Alexa, unlock the back door*), it will prompt you for the PIN. You can say things like: - *Alexa, lock the back door* - *Alexa, is the back door locked* - All the rest of the types will simply respond to standard on/off commands. There is currently no distinction other than the icon that shows up in the Alexa app. You can say things like: - *Alexa, turn on bathroom exhaust fan* - *Alexa turn off desk lamp* ### Dimmer Devices This type of device is most often a dimmable load, though there are some other options. Most of these device types will also respond to on/off commands like Relay Devices above. To control from Alexa, you use phrases like: - *Alexa, brighten office lamp to 35%* - *Alexa, dim office lamp by 15%* If the devices support color and/or white temperature, you can use phrases like: - *Alexa, set color of office bulb to red* (color devices) - *Alexa, set office bulb to daylight* (white color temperature) - *Alexa, make office bulb warmer* (white color temperature) For white temperatures, the following table maps the names that Alexa expects to the color temp in kelvin: | **Shades of White** | **Temperature in Kelvin** | |--------------------------|---------------------------| | warm, warm white | 2200 | | incandescent, soft white | 2700 | | white | 4000 | | daylight, daylight white | 5500 | | cool, cool white | 7000 | When you select an Indigo device that is a dimmer device to publish, you will get a popup that will help us tell Alexa how to control your device: ![Alexa Dimmer Subtypes Image](../../images/alexa_dimmer_subtypes.png) We will take a guess at what the specific type of the device is, but we won't always guess correctly. For instance, if there isn't anything about your device that Indigo can determine, it will just automatically select `Dimmer`. You can override that setting however to make the device best match what it does. The options are: - `Blind` - use this type if your dimmer device actually controls blinds, shades, or drapes. You can say things like: - *Alexa, raise the blinds to twenty-five percent* - *Alexa, set the drapes to fifty percent* - *Alexa, close the shades* - *Alexa, what is the status of the shades* - `Fan` - use this type if your device actually controls a fan (as of this release Z-Wave fan controllers are treated as dimmers in Indigo). Fan devices from Indigo in Alexa will support 4 modes: **Off**, **Low**, **Medium**, **High**. You can say things like: - *Alexa, set ceiling fan to medium* - *Alexa, set ceiling fan to highest* - *Alexa, ceiling fan speed* - *Alexa, turn off ceiling fan* - `Valve` - use this type if your device controls a valve, or really any device that has a 0-100% range. You can say things like: - *Alexa, set the valve to thirty percent* - *Alexa, increase valve by ten percent* - *Alexa, turn off the valve* - All the rest will support standard on/off and dim/brighten. If the device supports color and/or white color temperature, those commands will be added to the standard on/off and dim/brighten commands (see the examples above). There is currently no distinction other than the icon that shows up in the Alexa app. ### Indigo Fan Devices Indigo has a native fan device type. Currently, the only built-in device using this type is the Insteon FanLinc. There are some other plugins which also support this device type. If you select a device of this type the only option will be the Alternative name as there are no other options. You can say things like: - *Alexa, set ceiling fan to medium* - *Alexa, set ceiling fan to highest* - *Alexa, increase ceiling fan speed* - *Alexa, turn off ceiling fan* ### Indigo Thermostat Devices Any Indigo thermostat device can be added to Alexa. Schedule/program mode isn't supported on thermostats that offer that feature. **NOTE**: the Alexa implementation for thermostats is quite limited as it only fully supports thermostats that are in either heat or cool mode. In North America, most thermostats stay in *auto* mode, which allows (at least) two setpoints to be active at the same time to call for heat or cool depending on the temp. Alexa's support for *auto* mode is fundamentally read-only: you can't adjust either setpoint while in *auto*. Further, the error that Alexa will respond with implies that you have to manually set the mode on the thermostat itself to either *heat* or *cool* in order for you to adjust setpoints. This is incorrect in that you can say to Alexa "set the thermostat to heat", and that will correctly change mode from *auto* to *heat* (same applies to *cool*). So, with that warning aside, you can say things like: - *Alexa, set the thermostat to cool* - *Alexa, what is my thermostat set to* - *Alexa, turn off the heat* (**Warning**: this will turn the thermostat off regardless of mode) - *Alexa, set the AC to seventy-five* - *Alexa, make it warmer in here* --- Troubleshooting (https://docs.indigodomo.com/2025.2/plugins/alexa/troubleshooting/) --- # Alexa Troubleshooting Because this integration is made up of a variety of parts, and because Alexa itself can talk to multiple smart home skills as well as allow the definition of custom "routines", there are a variety of places where things can go wrong. This section will hopefully cover many of those scenarios. If you don't find an answer to your problem in this section, post a detailed description of your issue and the steps you have taken (and any relevant Event Log entries) to the [Alexa plugin support forum](https://forums.indigodomo.com/viewforum.php?f=359). ## Device Caching The first and foremost issue that users experience with Alexa, device discovery and device changes, is that Amazon caches device definitions, and any changes (additions, changes, deletions) may take minutes to complete. Sometimes the changes don't propagate throughout their various caches at all. So, when making any changes, it's always a good idea to wait for maybe 10 minutes between any changes that you make. For instance, if you change the name of a device, run discovery as advised below, but wait for 10 minutes before looking for the change in the Alexa app or trying to control the device using the new name. Usually, when adding a new device, it happens pretty quickly. However, not always, and especially if it's combined with a change in another device. This seems not only to slowly propagate the change, but also slow the addition of a new device. If you want to delete a device (or all devices to start over), this seems to be the most problematic scenario for their caching scheme. You will want to wait 10 minutes to make sure that the deletion actually occurs before doing anything else. Users have reported that doing a Remove All from the Alexa website will continually fail and that the way to accomplish it is to delete a few at a time. While we haven't confirmed this behavior, it would not surprise us given all the caching issues we've experienced and read about. ## Account Linking Issues When you enable the Indigo Smart Home Skill in the Alexa app, you're required to link it to a [license in your Indigo Account](https://www.indigodomo.com/account/codes). You'll automatically be forwarded to the login page for your Indigo Account: log in using your normal credentials. You will then be forwarded to the skill authorization page which will have a popup that contains all of your licenses. Most users will only have a single license, but some will have multiple. You can only control a single Indigo Server from any given Alexa account. If you see an error page saying that you don't have any available licenses, this could be a result of a couple of things: 1. You have previously linked your license to Alexa but haven't revoked it on the [Authorizations page in your Indigo Account](https://www.indigodomo.com/account/authorizations). This may happen if you disable the Indigo Skill then attempt to re-enable it. Click on the Revoke button next to the Alexa authorization for your license to revoke the authorization then try linking again. 1. Your Indigo Up-to-Date subscription has expired. To use Alexa (and similar types of integrations), you need to have an active Indigo Up-to-Date subscription and your reflector must be active. 1. Your Indigo License doesn't have an active/working Indigo Reflector (included with your UTD subscription). To use Alexa (and similar types of integrations), you need to have an active Indigo Up-to-Date subscription and [your reflector must be active](https://www.indigodomo.com/docs/reflectors). 1. You may also see this error if Alexa has had some issue talking to your Indigo Server. We don't know exactly why this happens, but the solution is the same as #1 above - revoke and relink and it should continue working. If you have repeated link failures, check to see if your internet connection has had any periodic issues as this can cause Alexa to forget about its link authentication. Also, if you have more than around 50 devices publishes, this seems to exacerbate the problem. We recommend that you keep the number of devices published to a smaller reasonable number that really need voice control. ## Changing a Published Device in Indigo ### Changing the Indigo Name If you specified an alternate name for a device when publishing it, then changing the Indigo name won't make any difference, and you won't need to do anything. If, however, you didn't specify an alternate name, then Indigo will use the Indigo device name. If you change it, then you will need to rerun discovery in the Alexa app or on an Alexa-enabled device. If discovering by voice command, Alexa will say that it couldn't find any new devices (which is technically correct), but the device will now respond to the new name. If it doesn't then the most likely scenario is that the new name conflicts with another device Alexa knows about. Check the list of devices in the Alexa app to ensure that there isn't a duplicate name. Also, when you check the list, make sure that the old device name is no longer in the list. If it is, confirm that you changed the name (and that you didn't specify an alternate name) then rerun discovery. Also, remember our discussion of device caches above: give Alexa at least 10 minutes for changes to propagate throughout their device caches. If you've made a change and waited, and it's still not responding, one other possibility is that you have a routine defined in Alexa with the name or a similar name - that may cause conflicts when Alexa attempts to determine what it is you're asking. ### Changing the Alternate (Alexa) Name If you change the alternate name, you will need to rerun discovery. If the new name doesn't work, try the troubleshooting tips in the [Changing the Indigo Name](../alexa/troubleshooting.md#changing-the-indigo-name) section just above this one. ### Changing the Device Type If you edit a device and change the type, protocol, or anything that changes the nature of the device, you will most likely want to follow this procedure: 1. In the Alexa app, remove the device. 1. Go back to the [Publication Dialog](../alexa/smart-home-skill.md#making-a-device-available-in-alexa), select the device, and make sure that you are satisfied with the subtype (or change it as necessary). 1. Save any changes. 1. Rerun discovery in Alexa. It should say that it has found a new device (since you deleted it first) and you should now be able to control it based on the new type. Note that changing protocol might not require the process above (an Indigo dimmer device works the same no matter the protocol), but we have found that Alexa caches information about devices and just doing a discover after changing may not be enough to force Alexa to reset the device cache. ## General Issues Alexa uses some sophisticated caching mechanisms throughout their hosted systems in order to optimize performance/responsiveness. Unfortunately, sometimes that caching mechanism can lead to odd and misleading issues. Sometimes when you make changes (initial publishing, updating, removing publications) it can take a while for the change to propagate throughout their systems. The Indigo skill, which is hosted by Amazon (a requirement), does no caching of devices. The Alexa plugin does some local caching, but that has nothing to do with how Alexa interprets what you say and converts it into the command it sends to the plugin. The very first thing you want to check when troubleshooting any Alexa issues is the Event Log window. You will see various warnings and errors that will help you determine if there are issues. Those errors may help you to determine where to go next. First, ensure the following: 1. Make sure that you have [enabled the Indigo Skill and linked it to your Indigo Account & license](smart-home-skill.md) successfully 1. Make sure that your [Indigo Up-to-Date subscription is active](https://www.indigodomo.com/account/codes/). 1. Ensure that your Indigo Reflector is configured and connected. Check this by hitting your reflector URL in a browser: https://YOURREFLECTORNAME.indigodomo.net/ Here are a few things to help you diagnose issues. ### Alexa can't find a device 1. Make sure that you have [enabled the Indigo Skill and linked it to your Indigo Account](smart-home-skill.md) & license successfully 1. Watch the Event Log window for [discovery requests](../alexa/operations.md#discovery-requests-from-alexa) - If you don't see any discovery requests: - Make sure that your reflector is up and running - Make sure your Indigo Up-to-Date subscription hasn't lapsed - Make sure you have OAuth enabled in the [Start Local Server dialog](../../user/getting-started/installation.md#starting-indigo-server) - If you see a discovery request: - Make sure that the device you are publishing is in the list. Take note of the names of your published devices to ensure that they are unique - if you publish two devices with the same name Alexa will ignore or both of them. - If the above step is fine, then make sure that you aren't using a name that's used by a device in some other smart home skill (some users use the Hue skill to directly control Hue lights, if you have the Alexa-Hue Bridge plugin enabled that may also be publishing a device with the same name). - Make sure that you are giving Alexa enough time to update its caches - 10 minutes after a discovery is the recommended wait time. - If you have a slow or unreliable internet connection, Alexa can time out a request rather quickly and will speak some error message. If you see inconsistent behavior, this may be a hint that there is some kind of internet connection issue between the Alexa hosted servers and your Indigo Server (see below for more details). ### Alexa can't control a device Some steps to try when Alexa says it can't find a device: 1. Make sure you are clearly saying the device name. Alexa can sometimes hear something slightly different than what you're saying. 1. Make sure you have the device published in the plugin. You can do this by selecting the `Plugins->Alexa->Show Device Publications` menu item. Verify that you are saying the name that is published to Alexa if it's different than the name of the device in Indigo. 1. Rerun discovery from the Alexa app for an Alexa device. 1. Look in the Event Log window for errors when trying to control the device. ### Alexa can't speak a variable value Some steps to try when Alexa has a problem speaking the value of a variable: 1. Make sure that the value doesn't contain any [SSML markup](https://developer.amazon.com/en-US/docs/alexa/custom-skills/speech-synthesis-markup-language-ssml-reference.html) symbols by themselves, like <, >, /, etc. Alexa will just throw a very unhelpful error (*"Sorry, I'm having trouble accessing your Indigo Skills skill right now"*) when it thinks that the string contains malformed SSML. 1. Make sure that the value of the variable is less than 8000 characters - that's the speech output limit for Alexa. ### Alexa can't speak the variable list Some steps to try when Alexa has a problem speaking the variable list: 1. If you have too many variables, you may get the error *“There is a problem with the requested skill response”*. The issue is that the speech output sent to Alexa can't be longer than 8000 characters, so if you have a lot of variables or lots of variables with long names you may run into this situation. The next release of the plugin will only speak variables that have the "Remote Display" flag set for them, so you will be able to exclude variables using that mechanism without deleting them. ### Alexa can't speak the variable of a variable If the value of your variable contains an ampersand (&) or perhaps other special characters, you should replace them with the actual english word (and). Alternately, you can probably HTML encode the character as well (&). ### Alexa says there are issues when you try various things Alexa skills are hosted on their servers, and must communicate with your Indigo server through your reflector. If you have a poor internet connection, you may see some odd issues: not being able to discover, errors when you ask Indigo for things even though it appears on the Indigo side that they have happened, etc. Their API is very picky about response times, as slow response times is a poor user experience. This does mean, however, that anyone with slow connections (Satellite) or unreliable internet connections will experience various error messages from Alexa. Unfortunately, there is nothing we can do about this issue. ### If All Else Fails = If every other troubleshooting step has been taken, and you have multiple Indigo licenses, contact support (mentioning that you have multiple licenses) so we can more quickly determine if this is related to your problem. --- Scripting Indigo (https://docs.indigodomo.com/2025.2/scripting/) --- # Scripting Indigo Everything in Indigo you can do from the user interface — and a good deal you can't — can be done from Python. Scripts run in the **Script Editor** (Plugins → Open Scripting Shell), embedded inside triggers, schedules, and action groups, or as external script files. No plugin development required. ## Where to start If you're new to scripting Indigo, work through the [Scripting Tutorial](tutorial.md) — it builds up from one-line device commands to scripting third-party plugins. Then read [IOM Concepts](iom-concepts.md) to understand how the Indigo Object Model represents your devices, triggers, schedules, action groups, and variables in Python. ## Guides - [Python Packages](guides/python-packages.md) — what ships with Indigo's bundled Python and how to install additional packages. - [Python Version Conflicts](../user/troubleshooting/python-conflicts.md) — if scripts behave differently inside and outside Indigo. ## IOM Reference The complete reference for every IOM class and command namespace: [Actions](reference/actions.md), [Action Groups](reference/action-groups.md), [Devices](reference/devices/index.md), [Device Subclasses](reference/device-subclasses/index.md), [Folders](reference/folders.md), [Schedules](reference/schedules.md), [Triggers](reference/triggers.md), [Variables](reference/variables.md), plus [Server Properties & Commands](reference/server-commands.md), [Insteon Commands](reference/insteon-commands.md), [X10 Commands](reference/x10-commands.md), and [Event Data Path Specifiers](reference/event-data-paths.md). These pages document the *scripting* (Python) view of Indigo's objects. For what these objects mean and how to use them from the UI, see the [Concept Overview](../user/concepts/index.md) in the User Guide. ## Related Building a full plugin instead? The [Plugin Development](../plugin-dev/index.md) section builds on everything here. Integrating an external system over HTTP or WebSockets? See [Integration APIs](../api/index.md). --- IOM Concepts (https://docs.indigodomo.com/2025.2/scripting/iom-concepts/) --- # Indigo Object Model Reference !!! abstract "In this guide" This guide is meant to provide specific reference information about the Indigo Object Model (IOM) as used by scripters writing embedded scripts as well as developers writing Server plugins. Scripters can be Developers and vice versa - we just wanted to define the different potential uses for IOM. ## About this Reference Guide The IOM is divided up based on the major object types: [Devices](reference/devices/index.md), [Triggers](reference/triggers.md), [Schedules](reference/schedules.md) *(not yet implemented)*, [Action Groups](reference/action-groups.md) *(not yet fully implemented)*, [Variables](reference/variables.md), and [Folders](reference/folders.md). There is also a utility base class (and subclasses), [Action](reference/actions.md) *(not yet complete)*, that's used with the major object types that support actions. For ease of discussion, any Python program that uses the object model will be referred to as a “script” throughout this document. From a style perspective, anything that’s in `code text` is generally considered to be Python code and represents the exact code needed. You'll also see code blocks like this: ```text # some code here # that does something ``` Some conventions of the Python API: all values are represented in camelCase, where words aren’t separated but each starts with an uppercase letter. The first character of all class names are uppercase (`DimmerDevice`) and the first character of class properties are lowercase letter (`folderId`). Constant enumerations start with a lowercase “k” (`kEnumerationName`), and each enumerated value begins with an uppercase letter (`kEnumerationName.EnumeratedValue`). !!! warning "A note about version numbers" As we've revised the API and added features, we've incremented the API version number. Up until Indigo 5 v5.1.1, the API remained 1.0 and the documents were just updated with new features (which could lead to incompatible features if using an older release of Indigo). As of Indigo 5 v 5.1.2, we've begun incrementing the API minor version number (1.X) whenever we add new features. The major number (X.0) will only be incremented when we do something that will break backwards compatibility or when there are major changes. See the [API version chart](https://www.indigodomo.com/indigo/api_version_chart.html) to see which API versions were released in which Indigo version. If any of the API tables in the documentation don't have a version number you can assume that the feature is available in API version 1.0 and later. Definitions of terms used throughout this manual are listed below: | Term | Meaning | | --- | --- | | Action Group | An action (or collection of actions) that can be used by multiple Schedules, Triggers, and Action Groups. This allows you to change the collection in a single place rather than editing multiple Schedules, Triggers, etc., that require identical actions to be executed. | | Developer | A person who writes Server Plugins. | | Direct Parameter | The first parameter to a command, although it may be optional. Any further parameters to a command must have a name specified. | | Event | Some external plugin defined event (outside Indigo) that is used to instruct the IndigoServer to execute a Trigger. | | Protocol | Any built-in home automation technology that Indigo supports (Insteon, X10, etc.). This does not include plugins that might implement their own protocol. | | Schedule | An action (or collection of actions) that will be executed based on some temporal settings - dates and times. When the appropriate time and date is met, IndigoServer evaluates any conditions specified in the schedule in order to decide if the actions associated with the event should execute. In previous versions of Indigo, these were referred to as Time/Date Actions. | | Script | Any section of code using the IOM regardless of language being used. | | Scripter | A person who writes embedded scripts. | | Trigger | An action (or collection of actions) that will be executed based on some input event - a device state change, an event from a plugin, an email received, etc. When the event occurs, IndigoServer evaluates any conditions specified in the trigger in order to decide if the actions associated with the event should execute. | ## Indigo Object Model (IOM) Overview ### Introduction The Indigo Object Model (IOM) is a collection of objects and methods that allow scripts to interact with objects in Indigo. From an architectural perspective, the most fundamental design point that needs to be understood is that the host process that executes python scripts is a separate process from the IndigoServer process - which is where the objects actually reside. We do this so that a single script or plugin can't bring down the entire IndigoServer. So, when you get an object, it’s actually **a copy of the object**, not the real object. We could have implemented a distributed object model with object locking, etc., but that would have added significant complexity. Instead, we decided to create some simple rules that you need to follow in order to use the IOM correctly: For Scripters and Developers: - To create, duplicate, delete, and send commands to an object (i.e. turnOn, nextZone, displayInRemoteUI, moveToFolder, etc.), use the appropriate command namespace and a reference to the object rather than altering the object directly (you can see the list of objects and their command name spaces in the table below) - To modify an object's definition (name, description, etc.), get a copy of it, make the necessary changes directly to the object, then call `myObject.replaceOnServer()` - To update a copy of an object that you previously obtained, call the object's `myObject.refreshFromServer()` method. For Developers: - To update a plugin's props on an object on the server, call `myDevice.replacePluginPropsOnServer(newPropsDict)` rather than try to update them on the local device - To change a device's state on the server, use `myDevice.updateStateOnServer(key="keyName", value="Value")` A couple of notes to help you understand these rules. First, the IOM allows [Server Plugins](../plugin-dev/guide.md#indigo-server-plugins) to attach arbitrary attributes to any object. We call these plugin properties, or just props. They are read-write for a Server Plugin, but are readonly for all Python scripts. Second, as discussed in the [Plugin Developer's Guide](../plugin-dev/guide.md), a [Server Plugin](../plugin-dev/guide.md#indigo-server-plugins) may define multiple device types. All devices have some state (or states): on/off, paused/playing/stopped, heating/cooling/off, etc. These states are used in Triggers and shown on Control Pages. Your Server Plugin will need to be able to tell the server whenever a state changes for one of its devices. That's what the last rule above is referring to. See the [Devices](reference/devices/index.md) description for more information and examples. We’ve divided the commands into name spaces such that if the command applies to a specific class type, it’s put into a namespace that matches the type. Here is a chart that maps the classes to their associated namespace: | Class | Command namespace | Notes | | --- | --- | --- | | **[Devices](reference/devices/index.md)** | | | | [Device](reference/devices/base-class.md#device-base-class) (indigo.Device) | [indigo.device.*](reference/devices/base-class.md#commands-indigodevice) | for commands and properties that apply to all device subclasses | | [DimmerDevice](reference/device-subclasses/dimmer.md#dimmerdevice) (indigo.DimmerDevice) | [indigo.dimmer.*](reference/device-subclasses/dimmer.md#commands-indigodimmer) | manipulate a dimmer | | [InputOutputDevice](reference/device-subclasses/multiio.md#multiiodevice) (indigo.MultiIODevice) | [indigo.iodevice.*](reference/device-subclasses/multiio.md#commands-indigoiodevice) | manipulate an I/O device | | [SensorDevice](reference/device-subclasses/sensor.md#sensordevice) (indigo.SensorDevice) | [indigo.sensor.*](reference/device-subclasses/sensor.md#commands-indigosensor) | manipulate a sensor (motion, etc) | | [RelayDevice](reference/device-subclasses/relay.md#relaydevice) (indigo.RelayDevice) | [indigo.relay.*](reference/device-subclasses/relay.md#commands-indigorelay) | manipulate a relay, lock, or other 2 state device | | [SpeedControlDevice](reference/device-subclasses/speedcontrol.md#speedcontroldevice) (indigo.SpeedControlDevice) | [indigo.speedcontrol.*](reference/device-subclasses/speedcontrol.md#commands-indigospeedcontrol) | manipulate a speed control/motor device | | [SprinklerDevice](reference/device-subclasses/sprinkler.md#sprinklerdevice) (indigo.SprinklerDevice) | [indigo.sprinkler.*](reference/device-subclasses/sprinkler.md#commands-indigosprinkler) | manipulate a sprinkler device | | [ThermostatDevice](reference/device-subclasses/thermostat.md#thermostatdevice) (indigo.ThermostatDevice) | [indigo.thermostat.*](reference/device-subclasses/thermostat.md#commands-indigothermostat) | manipulate a thermostat | | **Schedule** | | | | `Schedule` (indigo.Schedule) | indigo.schedule.* | manipulate a scheduled event FIXME not yet implemented | | **[Triggers](reference/triggers.md)** | | | | [Trigger](reference/triggers.md#trigger) (indigo.Trigger) | [indigo.trigger.*](reference/triggers.md#commands-indigotrigger) | commands and properties that apply to all trigger subclasses | | [DeviceStateChangeTrigger](reference/triggers.md#devicestatechangetrigger) (indigo.DeviceStateChangeTrigger) | [indigo.devStateChange.*](reference/triggers.md#commands-indigodevstatechange) | manipulate a device state change trigger | | [EmailReceivedTrigger](reference/triggers.md#emailreceivedtrigger) (indigo.EmailReceivedTrigger) | [indigo.emailRcvd.*](reference/triggers.md#commands-indigoemailrcvd) | manipulate an email received trigger | | [Trigger Class](reference/triggers.md#insteoncommandreceivedtrigger) (indigo.InsteonCommandReceivedTrigger) | [indigo.insteonCmdRcvd.*](reference/triggers.md#commands-indigoinsteoncmdrcvd) | manipulate an Insteon command received trigger | | [InterfaceFailureTrigger](reference/triggers.md#interfacefailuretrigger) (indigo.InterfaceFailureTrigger) | [indigo.interfaceFail.*](reference/triggers.md#commands-indigointerfacefail) | manipulate an interface failure trigger | | [InterfaceInitializedTrigger](reference/triggers.md#interfaceinitializedtrigger) (indigo.InterfaceInitializedTrigger) | [indigo.interfaceInit.*](reference/triggers.md#commands-indigointerfaceinit) | manipulate an interface initialized trigger | | [PluginEventTrigger](reference/triggers.md#plugineventtrigger) (indigo.PluginEventTrigger) | [indigo.pluginEvent.*](reference/triggers.md#commands-indigopluginevent) | manipulate a trigger defined by a plugin event | | [PowerFailureTrigger](reference/triggers.md#powerfailuretrigger) (indigo.PowerFailureTrigger) | [indigo.powerFail.*](reference/triggers.md#commands-indigopowerfailure) | manipulate a power failure trigger | | [ServerStartupTrigger](reference/triggers.md#serverstartuptrigger) (indigo.ServerStartupTrigger) | [indigo.serverStartup.*](reference/triggers.md#commands-indigoserverstartup) | manipulate a server startup trigger | | [X10CommandReceivedTrigger](reference/triggers.md#x10commandreceivedtrigger) (indigo.X10CommandReceivedTrigger) | [indigo.x10CmdRcvd.*](reference/triggers.md#commands-indigox10cmdrcvd) | manipulate an X10 command received trigger | | [VariableValueChangeTrigger](reference/triggers.md#variablevaluechangetrigger) (indigo.VariableValueChangeTrigger) | [indigo.varValueChange.*](reference/triggers.md#commands-indigovarvaluechange) | manipulate a variable changed trigger | | **[Action Groups](reference/action-groups.md)** | | | | `ActionGroup` (indigo.ActionGroup) | indigo.actionGroup.* | commands for action groups | | **[Variables](reference/variables.md)** | | | | [Variable](reference/variables.md) (indigo.Variable) | [indigo.variable.*](reference/variables.md#commands-indigovariable) | manipulate a variable | | **Protocol Specific Commands** | | | | [Insteon Specific Commands](reference/insteon-commands.md) | indigo.insteon.* | commands specific to the Insteon protocol | | [X10 Specific Commands](reference/x10-commands.md) | indigo.x10.* | commands specific to the X10 protocol | | **General Commands** | | | | [Commonly used commands and server properties](reference/server-commands.md) | indigo.server.* | for commands and properties that are more general in nature | The next question you probably have is “How do I create a new object in the IndigoServer?”. The simple answer is that most class namespaces have a `create()` method. They work a bit differently based on the class type (for instance, `indigo.device.create()` is used as a factory method to create all types of devices), but it's always named the same. There are also `duplicate()` and `delete()` methods for each. See the individual class pages for details. This will keep the IOM simple and understandable and it avoids significant complexity. In this section we’ll describe the classes exposed to your script so that you can use them for whatever your script needs: checking the value of a variable, the state of a device, the definition of an event or action, etc. There are a few classes that are special. One is `Action` and it’s subclasses (collectively referred to as "actions"). Why are these classes different? Because outside the trigger, schedule, action group, or control page that they’re associated with they aren’t individually addressable in the IndigoServer: for instance, there is no way to identify an action without first identifying the trigger that it’s associated with. So, how do you interact with actions? Directly, as you would any other object. You can create one (which creates it locally), edit it, etc. Once you have the object set up correctly, you can add it to the appropriate local copy of an object then call the object's `replaceOnServer()` method. If you have a local copy of an object and you want to ensure that it's in sync with what's on the server then call the object's `refreshFromServer()` method. Don’t worry if this seems abstract: the examples section for each class page has examples of how to manipulate actions. Another very important concept that we’re introducing along with the IOM is the concept of an identifier, or `id`. Every top-level object in Indigo (action group, device, folder, schedule, trigger, variable) now has a globally unique integer ID associated with it. All commands in the IOM accept the `id` of an object or the object reference itself (along with the name). You should **never** store the object’s name. The `id` is visible in the various lists throughout the UI. You can also select any object in the GUI and right click it to get its contextual menu where you'll find a `Copy ID (123)` item that shows the ID and allows you to copy it to the clipboard. Server plugins that define config UIs that use the built-in lists will get the `id`(s) of the object(s) selected in the lists rather than the name. Now, if you're being very observant, you'll notice something missing: Control Pages. It is our intention to support Control Pages in the IOM, but unfortunately it just didn't make it into v1. Look for it in an upcoming IOM revision. ### IOM Class Hierarchy ![IOM Object Hierarchy Image](../images/iom_object_hierarchy.png) ## IOM Data Types and Object manipulation ### Indigo specific data types Indigo exposes a couple of special classes in Python: `indigo.Dict()` and `indigo.List()`. These are very similar to their Python counterparts (`dict` and `list`). The big difference is that when you're dealing with dictionaries and lists that come from the IndigoServer and that go to the IndigoServer, you'll want to use these rather than the built-in Python types because they are handled natively by Indigo and can automatically be saved to the database and preference files. The behavior of `indigo.Dict` and `indigo.List` is similar but not identical to the native python containers. Specifically, the Indigo containers: - must contain values that are of types: **bool**, **float**, **int**, **string**, **list** or **dict**. Any list/dict containers must recursively contain only compatible values. - do **not** support value access via slicing (we might add this eventually). - always retrieve values (ex: myprops["key"] or mylist[2]) as a copy and **not reference** of the object. - keys can only contain letters, numbers, and other ASCII characters. - keys cannot contain spaces. - keys cannot start with a number or punctuation character. - keys cannot start with the letters XML, xml, or Xml. The behavior that items are always retrieved as values (not references) can lead to some unexpected results when compared to python dicts and lists, especially when multiple levels of container nesting is being used. What this means is that you can set items and append to items, but the items returned from the get iterators are always new copies of the values and not references. For example, consider: ```python c = indigo.List() c.append(4) c.append(5) c.append(True) c.append(False) a = indigo.Dict() a['a'] = "the letter a" a['b'] = False a['c'] = c # a COPY of list C above is inserted (not a reference) print(str(a)) # Because a['c'] has a COPY of list C this will NOT append to instance a: c.append(6) print(str(a)) # no change from previous output! # And because a['c'] returns a COPY of its value this will also NOT append to instance a: a['c'].append(6) print(str(a)) # no change from previous output! ``` But all is not lost. You can just re-assign the nested object to the root container: ```text # Instead you must re-assign a copy to the container. Since we already # appended c with 6 above, we just need to reassign it now: a['c'] = c print(str(a)) # now has a COPY of the updated object, c ``` If you just need to override an existing value in a container (and not append to it), then a more efficient solution is to use our helper method `setitem_in_item`. It avoids creating temporary copies of values. ```text a.setitem_in_item('c', 4, "the number six") print(str(a)) ``` Likewise, it is most efficient to use `getitem_in_item` when accessing nested items since it avoids temporary copies of containers. For example, this creates a temporary copy of the c object before accessing index 4: `a['c'][4] # works, but is slow because a temporary copy of a['c'] is created` Where this returns the same result but is more efficient: `a.getitem_in_item('c', 4) # same result, but fast` #### Converting to native Python collections You can convert an `indigo.Dict` to a python `dict` and an `indigo.List` to a python `list`, by calling the appropriate methods on those instances. Note, this will recursively convert nested objects (which attempting a manual cast will not): ```python python_dict = my_indigo_dict.to_dict() # recursively convert an indigo.Dict instance to a python dict instance python_list = my_indigo_list.to_list() # recursively convert an indigo.List instance to a python list instance ``` Beginning with Indigo 2021.2, you can use a native Python conversion: ```text python_dict = dict(my_indigo_dict) python_list = list(my_indigo_list) ``` ### Built-in objects The IOM supplies the following built-in objects: | Object | Description | | --- | --- | | indigo.devices | All devices in the database | | indigo.triggers | All triggers in the database | | indigo.schedules | All schedules in the database | | indigo.actionGroups | All action groups in the database | | indigo.variables | All variables in the database | These objects are very similar to an indigo.Dict - but have some special abilities and constraints: 1. They are read-only - you can't modify them directly but rather use other methods to change them 1. They contain a "folders" attribute that has all the folders defined for that object type 1. They define a couple of convenience methods that are specialized for their use Let's elaborate on each of these a bit. First, they're read-only in that you can't do something like this: `indigo.devices[123] = someOtherDevice` To change a device, in most cases you get a copy of the device, make the necessary changes, then `replaceOnServer()`. You'll find specific information about each of those in the appropriate section linked below in the [Classes and Commands](#classes-and-commands) section. Each of the objects above corresponds to one of the high-level objects in Indigo. In the UI, you know that you can create folders for each of those object types. To access the folders available for each type, you can reference the "folders" attribute: i.e. `indigo.devices.folders`. This is also a read-only `indigo.Dict` of [folder](reference/folders.md) objects. As with the other top-level classes, you manipulate folders within specific namespaces, described on the [folder class](reference/folders.md) page. Finally, these objects define some convenience methods as well as supporting most of the standard Python [dict](http://docs.python.org/library/stdtypes.html#typesmapping) object methods. The first added method, `getName(elemId),` is a shortcut to get the string name of the item. Normally you would need to get a copy of the object (which would pull the entire object from the IndigoServer) then get the name, but this method is more efficient since it only gets the name from the IndigoServer rather than the whole object. So, an easy way to get a variable folder's name (maybe for logging) would be to do this: `indigo.variables.folders.getName(1234)`. Make sure you use the correct object (`indigo.variables`) so that we'll know where to search for the folder. #### Subscribing to Object Change Events The other additional method is `subscribeToChanges()`. This method doesn't return anything - rather, it tells the IndigoServer that your plugin wants notification of all changes to the specific object type. Use this method sparingly as it causes a significant amount of traffic between IndigoServer and your plugin. What kind of plugins would use this? One good example is a logging plugin - one that's logging activity within Indigo (like our [SQL Logger](../plugins/sql_logger.md)). Another example is a plugin that's doing some kind of scene management - you would probably want to issue a `indigo.devices.subscribeToChanges()` at plugin start so that you'll always be notified when any device changes - you can then determine if your scene "state" has changed and act accordingly. Note that subscribeToChanges() only reports actual changes to devices - so for instance when a light turns ON you'll get a notification. If the light is commanded to turn ON again, you won't get the notification because the device state didn't change. ##### Object Events | Objects that support subscribeTochanges() | | --- | | indigo.actionGroups.subscribeToChanges() | | indigo.devices.subscribeToChanges() | | indigo.triggers.subscribeToChanges() | | indigo.variables.subscribeToChanges() | For example, ```python def deviceUpdated(self, origDev, newDev): # call the base's implementation first just to make sure all the right things happen elsewhere indigo.PluginBase.deviceUpdated(self, origDev, newDev) # do your stuff here - make the network connection if necessary, write the data, etc. ``` ##### Lower-level Events Use the lower-level `subscribeToIncoming()` and `subscribeToOutgoing()` methods in the `indigo.insteon` and `indigo.x10` command spaces to see commands regardless of their effect on device state. These commands are exclusive to `Insteon` and `x10` device classes. | Low Level Commands (Insteon and x10 Only) | | --- | | indigo.insteon.subscribeToIncoming() | | indigo.insteon.subscribeToOutgoing() | | indigo.x10.subscribeToIncoming() | | indigo.x10.subscribeToOutgoing() | For example, ```python ######################################## def x10CommandReceived(self, cmd): self.logger.debug(f"x10CommandReceived: \n{str(cmd)}") if cmd.cmdType == "sec": # or "x10" for power line commands if cmd.secCodeId == 6: if cmd.secFunc == "sensor alert (max delay)": self.logger.info("SENSOR OPEN") elif cmd.secFunc == "sensor normal (max delay)": self.logger.info("SENSOR CLOSED") def x10CommandSent(self, cmd): self.logger.debug(f"x10CommandSent: \n{str(cmd)}") ``` #### Iterating Object Lists For each of the built-in objects, we've also provided some handy iterators that in some cases will allow you to filter. For instance, if you would like to iterate over the list of all devices in Indigo, here's what you would do in Python: ```python for dev in indigo.devices: # do stuff with dev here ``` A note on iteration: when the above loop begins, the list of devices is fixed. So if mid-loop an element is added then it will NOT be iterated (unless the entire loop runs again later). Likewise, if an element is deleted midway through, then the iterator gracefully handles it and just ignores that deleted item (it skips to the next item automatically). So if items are added/removed during an iteration it never throws an exception -- it handles it gracefully but you are not guaranteed to get the new ones. You can also iterate with just the ID of the object: ```python for devId in indigo.devices.iterkey(): # all device id's (rather than the whole device object) ``` For some of the built-in objects, you can filter the results: ```python for dev in indigo.devices.iter("indigo.dimmer"): # all devices will be dimmer devices ``` You can further restrict some filters - for instance, you can specify the device type and the protocol: ```python for dev in indigo.devices.iter("indigo.dimmer, indigo.insteon"): # all devices will be Insteon dimmer devices ``` Or you can iterate just devices defined by custom plugin types: ```python for dev in indigo.devices.iter("self"): # each dev will be one that matches one of our custom plugin devices ``` Here's a list of all device filters: | Filter | Description | | --- | --- | | indigo.zwave | include Z-Wave devices | | indigo.insteon | include Insteon devices | | indigo.x10 | include X10 devices | | com.mycompany.myplugin | include all devices defined by a plugin | | indigo.responder | include devices whose state can be changed | | indigo.controller | include devices that can send commands | | indigo.iodevice | input/output devices | | indigo.dimmer | dimmer devices | | indigo.relay | relay, lock, or other 2 state devices | | indigo.sensor | sensor devices (motion, temperature, etc.) | | indigo.speedcontrol | multi-speed controlled device (fans, motors, etc.) | | indigo.sprinkler | sprinklers | | indigo.thermostat | thermostats | | self | include devices defined by the calling plugin | | self.myDeviceType | include myDeviceType's devices defined by the calling plugin | | com.company.plugin.xyzDeviceType | include xyzDeviceType's defined by another plugin | | props.SupportsOnState | return only devices that support an ON state property | Triggers also have filters: | Filter | Description | | --- | --- | | indigo.insteonCmdRcvd | insteon command received triggers | | indigo.x10CmdRcvd | x10 command received triggers | | indigo.devStateChange | device state changed triggers | | indigo.varValueChange | variable changed triggers | | indigo.serverStartup | startup triggers | | indigo.powerFail | power failure triggers | | indigo.interfaceFail | interface failure triggers - can be used with or without a specified protocol | | indigo.interfaceInit | interface connection triggers - can be used with or without a specified protocol | | indigo.emailRcvd | email received triggers | | indigo.pluginEvent | plugin defined triggers | | self | include triggers defined by the calling plugin | | self.myTriggerType | include myTriggerType's triggers defined by the calling plugin | | com.company.plugin.xyzTriggerType | include all xyzTriggerType's defined by another plugin | So, to iterate over a list of device state change triggers, you would: ```python for trigger in indigo.triggers.iter("indigo.devStateChange"): # each trigger will be a device state change trigger ``` Or to iterate over all triggers defined by our plugin types: ```python for trigger in indigo.triggers.iter("self"): # each trigger will be one that matches one of our custom plugin triggers ``` Unlike devices, however, you may only use a single trigger filter - anything else will result in an empty list. There is a special filter for variables as well. To iterate over the variable list, you would probably guess this: ```python for var in indigo.variables: # all variables ``` And you'd be correct. You can also just iterate through the writable variables: ```python for varName in indigo.variables.iter("indigo.readWrite"): # you'll only get readwrite variables ``` ### Common Python Exceptions All IOM Commands can throw exceptions if there is a missing or incorrect parameter, or if there is a runtime problem that prevents the command or request from completing successfully. Common exceptions that may be raised include: | Possible Exceptions | | | --- | --- | | `ArgumentError` | a required parameter is missing, or is not the correct type | | `IndexError` | an index parameter value is out-of-range | | `KeyError` | a key parameter (to a dictionary) was not found in the dictionary | | `PluginNotFoundError` | a plugin that you're trying to talk to (on a `create()` perhaps) isn't available (disabled or gone) | | `TypeError` | a parameter had the incorrect runtime type | | `ValueError` | a parameter has a value that is illegal or not allowed | Other exceptions, such as `IOError`, `MemoryError`, `OverflowError`, etc., are also possible although less likely to occur. ## Classes and Commands Indigo provides many classes and commands to work on those classes. You can use standard Python introspection methods to find out information about those classes (these examples are done using the [Scripting Shell](tutorial.md)): ```python # first, let's get an indigo.DimmerDevice like a LampLinc >>> dev = indigo.devices[238621905] >>> dev.__class__ >>> hasattr(dev,'onState') True >>> dir(dev) ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'address', 'brightness', 'buttonGroupCount', 'description', 'deviceTypeId', 'enabled', 'folderId', 'globalProps', 'id', 'lastChanged', 'model', 'name', 'onState', 'pluginId', 'pluginProps', 'protocol', 'refreshFromServer', 'remoteDisplay', 'replaceOnServer', 'replacePluginPropsOnServer', 'states', 'supportsAllLightsOnOff', 'supportsAllOff', 'supportsStatusRequest', 'updateStateOnServer', 'version'] # Next, an indigo.ThermostatDevice >>> thermo = indigo.devices[171495708] >>> thermo.__class__ >>> dir(thermo) ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'address', 'buttonGroupCount', 'coolIsOn', 'coolSetpoint', 'description', 'deviceTypeId', 'enabled', 'fanIsOn', 'fanMode', 'folderId', 'globalProps', 'heatIsOn', 'heatSetpoint', 'humidities', 'humiditySensorCount', 'hvacMode', 'id', 'lastChanged', 'model', 'name', 'pluginId', 'pluginProps', 'protocol', 'refreshFromServer', 'remoteDisplay', 'replaceOnServer', 'replacePluginPropsOnServer', 'states', 'supportsAllLightsOnOff', 'supportsAllOff', 'supportsStatusRequest', 'temperatureSensorCount', 'temperatures', 'updateStateOnServer', 'version'] # Here's a plugin defined device >>> tunes = indigo.devices["Whole House iTunes"] # notice that it only shows the Device base class >>> tunes.__class__ >>> dir(tunes) ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'address', 'buttonGroupCount', 'description', 'deviceTypeId', 'enabled', 'folderId', 'globalProps', 'id', 'lastChanged', 'model', 'name', 'pluginId', 'pluginProps', 'protocol', 'refreshFromServer', 'remoteDisplay', 'replaceOnServer', 'replacePluginPropsOnServer', 'states', 'supportsAllLightsOnOff', 'supportsAllOff', 'supportsStatusRequest', 'updateStateOnServer', 'version'] # You can use the deviceTypeId attribute to get the plugin's defined type >>> tunes.deviceTypeId u'mediaserver' ``` So, from a discovery perspective, the `dir(obj)` method will list all properties and methods for an object. The `hasattr(obj, attr)` will test an object to see if it has a specific attribute. The `obj.__class__` attribute will show you what class the object is. As you explore IOM objects, these methods will help you figure out how to use IOM objects effectively. ## Object Dictionaries An important note about Indigo objects. When you print an object instance to the Events log, you will get all the attributes and properties for that object: ```python >>> dev = indigo.devices[123] >>> indigo.server.log(f"{dev}") address : batteryLevel : None buttonGroupCount : 0 configured : True description : ... >>> indigo.server.log(f"{type(dev)}") <'indigo.Device'> ``` These data are stored as Indigo class instances. In order to send a complete description of the Indigo instance to another process or system (serializing it into JSON, for example), you can convert the Indigo instance to a traditional Python dictionary: ```python >>> dev = indigo.devices[123] >>> dev_dict = dict(dev) >>> indigo.server.log(f"{dev_dict}") {'class': 'indigo.Device', 'address': '', 'batteryLevel': None, 'buttonGroupCount': 0, 'configured': True, 'description': '', ...} >>> indigo.server.log(f"{type(dev_dict)}") ``` Converted objects include all elements of the class instance. All the object types listed below support conversion to Python dictionaries. Here are the major groupings of classes defined in the IOM (the command namespace for each is described with the classes): - [Device](reference/devices/index.md) classes - [Trigger](reference/triggers.md) classes - [Schedule](reference/schedules.md) class FIXME not yet implemented - [Action](reference/actions.md) classes - [Action Group](reference/action-groups.md) class - [Variable](reference/variables.md) class - [Folder](reference/folders.md) class Here are the command namespaces that are independent (to some extent) from the classes they effect: - [Server Commands](reference/server-commands.md) ## Utility Classes and Functions The `indigo.utils` module includes several classes and functions that aren't tied to a specific Indigo object but are helpful when writing scripts and building plugins — a JSON encoder, the `ValidationError` exception, a static-file helper for plugin HTTP replies, email/boolean helpers, the `is_int()` test, and the `indigo.Dict`/`indigo.List` conversion methods. See the [Utility Classes & Functions](reference/utils.md) reference page for the full list. --- Scripting Tutorial (https://docs.indigodomo.com/2025.2/scripting/tutorial/) --- # Plugin Scripting Tutorial !!! abstract "In this guide" A hands-on introduction to scripting Indigo from the interactive Python shell: turning devices on/off, reading and writing variables, iterating the Indigo object model, and executing action groups. Requires Indigo running locally; basic Python familiarity is helpful. Open the shell via `Plugins->Open Scripting Shell`. ## Talking to the Indigo Server The [Indigo Plugin Host (IPH)](../plugin-dev/guide.md#indigo-plugin-host) provides a python scripting and plugin environment, which makes controlling the Indigo Server easy via the high-level python language (the official python website has [great python tutorials](http://docs.python.org/tutorial/)). Every plugin automatically runs inside its own Indigo Plugin Host process, but you can also start the IPH in an interactive mode to directly communicate with the Indigo Server. It is a great way to experiment with Indigo's python environment, which is available to both python scripts run by Indigo and the plugin architecture. Want the ability to just put some Python scripts onto a menu somewhere for easy access? [There's a simple way to do it.](http://www.indigodomo.com/pluginstore/150/) To launch the Terminal utility application (inside *`/Applications/Utilities/`*) in an Indigo shell mode choose the `Plugins->Open Scripting Shell` menu item. Note: you'll want to read through the [introduction section of the Indigo Object Model](iom-concepts.md#introduction), which gives you some fundamental information that you need to successfully script Indigo. ## Example Code Snippets Below are some sample scripts you can copy/paste directly into the IPH window (opened when following the directions above). Keep in mind, these are only examples, so be sure to read the full [Indigo Object Model (IOM) Reference](iom-concepts.md). Note: 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's name** in Indigo's Main Window. By using the ID, you ensure the object will be found even if its name is changed. ### Device Examples #### Turn on the device "office desk lamp": ```python indigo.device.turnOn(1234567890) # Preferred, where the number is the ID of "office desk lamp" # OR the less preferred way because it will break if you rename the device indigo.device.turnOn("office desk lamp") ``` #### Duplicate the device "office desk lamp": ```python indigo.device.duplicate(1234567890, duplicateName="office desk lamp2") # where the number is the ID of "office desk lamp" ``` #### In 4 seconds turn on the device "office desk lamp" for 2 seconds: ```python indigo.device.turnOn(1234567890, duration=2, delay=4) # where the number is the ID of "office desk lamp" ``` #### Turn off all devices: ```python indigo.device.allOff() ``` #### Count the number of device modules: ```python indigo.devices.len() ``` #### Count the number of dimmable device modules: ```python indigo.devices.len(filter="indigo.dimmer") ``` #### Count the number of devices defined by all of our plugin types: ```python indigo.devices.len(filter="self") ``` #### Count the number of irBlaster type devices defined by our plugin: ```python indigo.devices.len(filter="self.irBlaster") ``` #### Get the on state of a device if it has the onState property (uses Python's hasattr() introspection method): ```python lamp = indigo.devices[1234567890] # where the number is the ID of "office desk lamp" if hasattr(lamp, 'onState'): isOn = lamp.onState ``` #### Get the class of a device (uses Python's **class** property): ```python lamp = indigo.devices[1234567890] # where the number is the ID of "office desk lamp" if lamp.__class__ == indigo.DimmerDevice: theBrightness = lamp.brightness ``` #### Turn on a light only if it's been off for longer than 1 minute: ```python from datetime import datetime lamp = indigo.devices[91776575] # ID of "Hallway light" timeDelta = datetime.now() - lamp.lastChanged if not lamp.onState and timeDelta.seconds > 60: indigo.device.turnOn(91776575) # ID of "Hallway light" ``` #### Access a custom device state ```python # get the device dev = indigo.devices[23989834] # Some custom device # access the state through the states property, use the key # that's displayed in the Custom States tile on the main window # when you have a custom device selected print(dev.states["someDeviceStateKey"]) # show all the device states: print(dev.states) ``` ### Variable Examples #### Create a new Indigo variable named fooMonster, change its value multiple times, and delete it: ```python newVar = indigo.variable.create("fooMonster", "default value") indigo.variable.updateValue(newVar, "asleep") indigo.variable.updateValue(newVar, "awake") indigo.variable.delete(newVar) ``` #### Getting a variable object and using its value: ```python myVar = indigo.variables[123] if myVar.value == "true": indigo.server.log("The variable had a value of 'true'") ``` #### Duplicating a variable: ```python indigo.variable.duplicate("fooMonster", duplicateName="fooMonsterSister") ``` #### Using a variable value in an HTTP GET ```python import requests my_var = indigo.variables[1893500335] # always use variable ID rather than name query_args = {"param1": my_var.value} reply = requests.get("http://example.com/foo/bar", params=query_args) ``` #### Setting a variable to Python types Since Indigo variable values are always strings, you have to convert anything that's not a string. It's safest to use the str() method: ```python my_ascii_string = "ASCII String" indigo.variable.updateValue(1234567, value=my_ascii_string) my_unicode_string = "éçø" indigo.variable.updateValue(1234567, value=my_unicode_string) my_integer = 1 indigo.variable.updateValue(1234567, value=str(my_integer)) my_float = 1.0 indigo.variable.updateValue(1234567, value=str(my_float)) my_list = ["one", 2, "three", 4] indigo.variable.updateValue(1234567, value=str(my_list)) my_dictionary = {"first":1, "second":"two", "third":3, "fourth":"four"} indigo.variable.updateValue(1234567, value=str(my_dictionary)) ``` Any Python object that can be converted to a string can then be inserted into an Indigo variable. Note: conversions of this type are often one way: you can't necessarily automatically recreate the Python object from the string created by the str() method. For primitive types like numbers it may work, but for complex Python types like lists and dictionaries it will not. If you want to use Indigo to store a string representation of some complex Python data types, you can use JSON to encode them into strings then later decode them back into their respective Python objects: ```python import json my_list = ["one", 2, "three", 4] my_dictionary = {"first":1, "second":"two", "third":3, "fourth":"four", "my_list": my_list} indigo.variable.updateValue(1234567, value=json.dumps(my_dictionary)) # string will be something like: '{"second": "two", "myList": ["one", 2, "three", 4], "fourth": "four", "third": 3, "first": 1}' my_new_dictionary = json.loads(indigo.variables[1234567].value) #my_new_dictionary is now the same as my_dictionary my_new_dictionary["my_list"] # results in: ['one', 2, 'three', 4] ``` Custom Python classes that you create can implement the *`**str**(self)`* method. Take this simple example: ```python class myCustomClass: def __init__(self): self.a=1 self.b=2 def __str__(self): outputString = f"a:{self.a}, b:{self.b}" return outputString ``` If you have an instance of that class, then you can create a string representation of the class to insert into an Indigo variable: ```python cl = myCustomClass() indigo.variable.updateValue(1234567, value=str(cl)) # the value of the variable in Indigo will look like this: 'a:1, b:2' not including the quotes ``` For further information on Python classes, [check out this great tutorial](http://www.diveintopython.net/object_oriented_framework/index.html). ### Date and Time Examples #### Get the current server time: ```python indigo.server.getTime() ``` #### Calculate the sunset time in 1 week: ```python import datetime one_week = indigo.server.getTime().date() + datetime.timedelta(days=7) indigo.server.calculateSunset(one_week) ``` ### Action Group Examples #### Execute Action Group 12345678: Execute an action group: ```python indigo.actionGroup.execute(12345678) ``` #### Execute Action Group 12345678 with extra event data: Execute an action group, but pass through an arbitrary dictionary of data to actions that support using **event_data**: ```python extra_data = {"a": 1, "b": ["c", 2, 3]} indigo.actionGroup.execute(12345678, event_data=extra_data) ``` ### Log Examples #### Log to the Indigo Event Log window all the attributes/properties of the device "office desk lamp": ```python lamp = indigo.devices["office desk lamp"] indigo.server.log(f"{lamp.name}: \n{lamp}") ``` #### Log to the Indigo Event Log window using different levels of logging ```python import logging indigo.server.log("info log message which will show in black text", level=logging.INFO) indigo.server.log("warning log message which will show in orange text", level=logging.WARNING) indigo.server.log("error log message which will show in red text", level=logging.ERROR) # Equivalent of above server API calls that instead use the plugin instances default logger instance: self.logger.info("info log message which will show in black text") self.logger.warn("warning log message which will show in orange text") self.logger.error("error log message which will show in red text") ``` #### Print the last 5 Event Log entries: ```python logList = indigo.server.getEventLogList(lineCount=5) print(logList) ``` ### Folder Examples #### Iterate over a list of all device folders ```python for folder in indigo.devices.folders: print(f"Folder id: {folder.id} name: {folder.name}, remoteDisplay: {folder.remoteDisplay}") ``` #### Create a trigger folder named "My Triggers" and, if it exists, just return the existing one ```python try: myFolder = indigo.triggers.folder.create("My Triggers") except ValueError as e: if e.message == "NameNotUniqueError": # a folder with that name already exists so just get it myFolder = indigo.triggers.folders["My Triggers"] else: # you'll probably want to do something else to make myFolder a valid folder myFolder = None ``` #### Make a folder visible in remote clients (IWS, Indigo Touch, etc.) ```python indigo.devices.folder.displayInRemoteUI(123, value=True) ``` #### Get the folder containing a device ```python lamp = indigo.devices["office desk lamp"] # An object that's not in a folder will have a folder id of 0, which isn't a valid folder # so we need to make sure it's a valid folder ID first if lamp.folderId != 0: lampsFolder = indigo.devices.folders[lamp.folderId] else: lampsFolder = None ``` #### Event Data Examples Webhook events pass data along the chain that can be easily accessed in embedded or linked scripts. The data is passed first to any ***conditional scripts***. The event data can have many forms, but certain data will be passed regardless of the source of the call. By default, every event dictionary will contain the following: ```json { "event-indigo-id": 1214985350, # the ID of the trigger, schedule, action group, etc "event-type": "Trigger", # the event type - trigger, schedule, action group, etc. "source": "server", # the source of the event (see description below) "timestamp": "2025-08-07T14:32:21", # ISO formatted datetime string } ``` A more robust event dictionary might look like this: ```json { "data": (dict) "event-indigo-id": 1234567890 (integer) "event-plugin-event-id": simpleWebhook (string) "event-plugin-id": com.indigodomo.webserver (string) "event-plugin-name": Web Server (string) "event-type": PluginEventTrigger (string) "http-method": GET (string) "request-url": https://localhost:8176/webhook/8143484549824dbba1286a873daa3cea (string) "source": python (string) "status-code": 200 (integer) "timestamp": 2025-10-21T21:20:50 (string) "webhook-id": 8143484549824dbba1286a873daa3cea (string) } ``` Regardless of where the data came from (trigger, schedule, etc.) the variable name will be *`event_data`*. The data can be accessed like this: ```python # If you did an indigo.actionGroup.execute(12345) the source would be `python`. # You don't need to import `event data`, it will be made available automatically. if event_data["source"] == "python": indigo.server.log(f"{event_data['timestamp']}") # or whatever ... ``` All event_data payloads will be formatted as *``*. ### Miscellaneous Examples #### Get a list of all serial ports, excluding any Bluetooth ports: ```python indigo.server.getSerialPorts(filter="indigo.ignoreBluetooth") ``` #### Sending emails ```python # Simple Example indigo.server.sendEmailTo("emailaddress@company.com", subject="Subject Line Here", body="Some longish text for the body of the email") # Putting a variable's data into the subject and body theVar = indigo.variables[928734897] theSubject = f"The value of {theVar.name}" theBody = f"The value of {theVar.name} is now {theVar.value}" indigo.server.sendEmailTo("emailaddress@company.com", subject=theSubject, body=theBody) # Putting device data into the subject and body theDevice = indigo.devices[980532604] theSubject = f"Summary of {theDevice.name}" theBody = f"onState is {theDevice.onState}\n lastChanged is {theDevice.lastChanged}" indigo.server.sendEmailTo("emailaddress@company.com", subject=theSubject, body=theBody) ``` ## Starting the host from an existing terminal window If you already have a Terminal shell running, you can launch it directly. To start the IPH in interactive mode just execute the following inside the Terminal: `/usr/local/indigo/indigo-host` ![Plugin Host Prompt Image](../images/pluginhost_prompt.png) As shown, the IPH will automatically connect to the IndigoServer running on the same Mac and will show the server's version information and the Indigo Plugin Host Python version. Additionally, you'll notice that Indigo Server logs the connection of the interactive shell plugin. Add /usr/local/indigo/ to the PATH in your shell (in ~/.bashrc) and you won't have to specify the full path. Next, let's tell the Indigo Server to log a message to the Event Log window (again, via the Terminal application): `indigo.server.log("Hello world!")` ![Plugin Host Hello World Example Image](../images/pluginhost_helloworld.png) ## Connecting Remotely over SSH If you have SSH configured so you can remotely connect to your Mac running the Indigo Server, then you can use SSH to start the IPH interactively anywhere using the syntax: `ssh username@indigo_mac_ipaddr -t /usr/local/indigo/indigo-host` ## What Else Can it Do? The IPH gives you full access to the [Indigo Object Model (IOM)](iom-concepts.md) providing access to create/edit/delete/control Indigo objects, as well as several Indigo [utility functions](reference/server-commands.md), [Insteon device commands](reference/insteon-commands.md), and [X10 device commands](reference/x10-commands.md). Access to all of this functionality is provided by the indigo module automatically loaded and included in any python scripts run by the IPH (including interactive IPH sessions like we are using here). ## Executing Indigo Commands Directly In addition to communicating interactively with Indigo via the shell, you can also send direct python commands to Indigo via the IPH. For example, to get the current brightness of the device "office lamplinc": `indigo-host -e 'return indigo.devices["office lamplinc"].brightness'` Or to toggle the device "office lamplinc" three times: ```python indigo-host -e ' indigo.device.toggle("office lamplinc") indigo.device.toggle("office lamplinc") indigo.device.toggle("office lamplinc") ' ``` Note when your commands are executed the indigo module is already loaded and connected, and you can execute standard python code (loops, conditionals, etc.). !!! note Each call creates a new IPH (indigo-host) process which must establish a connection to the Indigo Server. Although this is relatively fast, calling it multiple times a second is not recommended. ## Executing Indigo Python Files The IPH can also be used to execute Indigo python (.py) files, like this: `indigo-host -x '/SomeFolder/indigo_script.py'` !!! note Each call creates a new IPH (indigo-host) process which must establish a connection to the Indigo Server. Although this is relatively fast, calling it multiple times a second is not recommended. ## Executing AppleScript Often times, you may find yourself wanting to execute an AppleScript from Python. You want to send some parameters to the AppleScript and you want to get results back in some format. There's a pretty straight-forward way to do this that [we've described in this article](https://www.indigodomo.com/indigo/applescript.html). This is a great pattern for calling AppleScripts from Python, specifically a great way to integrate other scriptable Mac app data with Indigo. ## Shared Classes and Methods in Python Files (Python Modules) You may install Python modules/libraries in a special location to make them available to Indigo scripts and plugins, but not to generic Python. This is particularly useful if the module/library includes references to the IOM (since it will only be loaded by processes that know about the indigo module). This location is: */Library/Application Support/Perceptive Automation/Python3-includes* Any Python files in this directory that define methods and/or classes will be available to any Python script whenever the interpreter is loaded (note that it's the Library directory at the top level of your boot disk, not the one in your user folder). These are referred to as Python modules. So, you can create files of classes, functions, etc. you want to share between all Python scripts using these mechanisms. If you add/change something in that directory while the Indigo Server is running, you'll need to tell the server to reload - select the `Plugins->Reload Libraries and Attachments` menu item and Indigo will restart the Python interpreter (which will cause your module to be reloaded). Files added to the generic module location can also import the entire IOM - IF the script that's actually running is started by Indigo. Here's a simple script that you can use that will safely import the IOM and will show a specific error when used from a Python script that's not started by Indigo: ```python """ indigo_attachments.py In this file you can insert any methods and classes that you define. They will be shared by all Python scripts - you can even import the IOM (as shown below) but if you do then you'll only be able to import this script in Python processes started by Indigo. If you don't need the IOM then skip the import, and it'll work in any Python script no matter where it's run from. """ try: import indigo except ImportError: print("The indigo module can only be used by scripts started from within Indigo") raise ImportError from datetime import datetime def log(message, label=None): # Create a log line with the date/timestamp prepended in MM/DD/YYYY HH:MM:SS format log_line = f"{datetime.today().strftime('%x %X')} {message}" # Write the log line with the label. If you didn't pass in a label # then the default label will be used. indigo.server.log(log_line, type=label) ``` Then, when you want to use any of the classes/methods in the file, just import the file and go: ```python import indigo_attachments indigo_attachments.log("Here's a special sort of log message", label="My Custom Label") ``` What you'll see in the Event Log: `My Custom Label 01/15/16 10:20:39 Here's a special sort of log message` If you try to run this script in a normal python session, you'll see the print statement followed by the ImportError: ```python MyMac:testdir jay$ python3.10 testHandlerCall.py The indigo module can only be used by scripts started from within Indigo Traceback (most recent call last): File "testHandlerCall.py", line 3, in import indigo_attachments File "/Users/USERNAME/Library/Python/3.10/site-packages/indigo_attachments.py", line 14, in raise ImportError ImportError ``` ## Scripting Indigo Plugins Indigo plugins are also scriptable. Because plugin-defined devices look almost identical to built-in devices, you can get (but not set) state information from them (see [device examples](#device-examples) above for a lot of examples). You can also get some of the other properties for a plugin. Most importantly you can execute plugin-defined actions, which is how you'd set their state values. The first step is to get an instance of the plugin: ```python iTunesId = "com.perceptiveautomation.indigoplugin.itunes" # supplied by the plugin's documentation iTunesPlugin = indigo.server.getPlugin(iTunesId) ``` This will **always** return to you a plugin object, defined with the following properties: | Property | Type | Description | |---------------------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------| | `compatibleUpdateAvailable` | bool | True is there is a compatible update to the plugin available from the store | | `includedWithServer` | str | True if the plugin is included in the Indigo installer | | `incompatibleUpdateAvailable` | bool | True is there is an update to the plugin available from the store but that is incompatible with the Indigo Server version currently running | | `latestCompatibleDownloadCount` | int | The number of downloads from the plugin store of this specific version of the plugin (`None` if the plugin isn't in the plugin store) | | `latestCompatibleDownloadURL` | str | The URL for the latest compatible release from the plugin store (`None` if the plugin isn't in the plugin store) | | `latestCompatibleReleaseDate` | datetime | The date for the latest compatible release in the plugin store (`None` if the plugin isn't in the plugin store) | | `latestCompatibleSummaryDesc` | str | The summary for the latest compatible release in the plugin store (`None` if the plugin isn't in the plugin store) | | `latestCompatibleVers` | str | The version of the most recent compatible release (`None` if the plugin isn't in the plugin store) | | `latestCompatibleWhatsNewDesc` | str | The "what's new" description of the most recent compatible release (`None` if the plugin isn't in the plugin store) | | `latestReleaseDate` | datetime | The release date of the most recent release (`None` if the plugin isn't in the plugin store) | | `latestRequiresIndigoVers` | str | The Indigo release that is the minimum requirement of the most recent release (`None` if the plugin isn't in the plugin store) | | `latestVers` | str | The most recent release version (`None` if the plugin isn't in the plugin store) | | `pluginDisplayName` | str | The name displayed to users in the clients | | `pluginFolderPath` | str | The full path to the plugin file location (useful for creating a full path to files within the plugin bundle) | | `pluginId` | str | The ID of the plugin (same as was passed to the getPlugin() method above) | | `pluginServerApiVersion` | str | The max API version supported by the running Indigo Server | | `pluginSupportURL` | str | The URL supplied by the plugin that points to its user documentation | | `pluginVersion` | str | The version number specified by the plugin | | `storeIconURL` | str | The URL to the plugin's icon in the plugin store (`None` if the plugin isn't in the plugin store) | | `storeName` | str | The name of the plugin in the plugin store (`None` if the plugin isn't in the plugin store) | | `storePluginURL` | str | The URL to the plugin in the plugin store (`None` if the plugin isn't in the plugin store) | | `storeSummary` | str | The description of the plugin in the plugin store (`None` if the plugin isn't in the plugin store) | There are also a couple of methods defined by this object. | Method | Description | |-----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `executeAction(actionId, deviceId, props, waitUntilDone)` | This is the method that actually executes the action. *`actionId`* is the unique action id. *`deviceId`* is the id of the device that the action on which the action will perform (if it requires a device). *`props`* is a dictionary of properties that the action needs to process correctly. See the plugin's documentation for a description of what you need to pass in to execute actions. *`waitUntilDone`* is a boolean that tells the plugin to block until it can return an optional payload object. | | `isEnabled()` | This method will return *`True`* if the plugin is enabled (running), *`False`* otherwise | | `isInstalled()` | This method will return *`True`* if the plugin is installed, *`False`* otherwise | | `isRunning()` | This method will return *`True`* if the plugin is running, *`False`* otherwise | | `restart(waitUntilDone=True)` | This method will restart the plugin if it's already running. Pass the optional parameter `*waitUntilDone=False*` if you don't want to block while the plugin restarts (waitUntilDone defaults to `*True*` if it's no parameters are passed). If you're a plugin developer and you want to restart the plugin from within itself you should pass that parameter. | | `restartAndDebug(waitUntilDone=True)` | The same as above, but it will restart in the debugger the user has selected in the Preferences. | Plugin actions may return a value (a Python "primitive") when you call the *`executeAction`* method, but they are not required to. The return value can be any one of the following object types: - None *`None`*, - booleans *`bool`*, - integers *`int()`*, - floats *`float()`*, - strings *`str()`*, - Indigo Dicts *`indigo.Dict()`*, - Indigo Lists *`indigo.List()`*. Note that a plugin action is not required to return something. If nothing is affirmatively returned, the return value will be set to *`None`* by default. Check with the plugin author's documentation for the possible return value for a particular action. The *`waitUntilDone`* parameter is optional (it defaults to true) and tells the plugin that you want it to pause your script until it's able to complete all the actions required to complete the request. For example, if you want to wait for the plugin to query an API or communicate with a device, set *`waitUntilDone`* to *`True`*. Plugin developers should [test and document their plugin actions](../plugin-dev/reference/xml/actions.md#actions-xml) to make sure that the necessary information is available to scripters. Consult the plugin's documentation to see whether an action call returns a value, and what object type the plugin returns for the action you're calling (different actions can return different object types). ### Examples #### Restart a Plugin There may be instances where you want to restart a plugin. This is how you do that from a script: ```python airfoil_id = "com.perceptiveautomation.indigoplugin.airfoilpro" airfoil_plugin = indigo.server.getPlugin(airfoil_id) if airfoil_plugin.isEnabled(): airfoil_plugin.restart() ``` !!! note For safety reasons, we do not provide the ability to enable a plugin that is disabled, or disable a plugin that is enabled. #### Wait Until Done There may be instances where you want to wait for a plugin to complete its action before you move on. You do that from a script by using the *`waitUntilDone`* parameter: ```python plugin_id = "com.perceptiveautomation.indigoplugin.airfoilpro" # Get a plugin object given the plugin id: airfoil_plugin = indigo.server.getPlugin(plugin_id) if airfoilPlugin.isEnabled(): try: result = airfoilPlugin.executeAction( 'getSources', deviceId=12345678 # ID of Airfoil device, waitUntilDone=True ) except Exception as e: print(f"Exception occurred: {e}") ``` *Note:* The AirFoil plugin doesn't receive props as a part of the call, so no *`props`* argument is provided. In this example, the AirFoil plugin will block until it's finished processing the action before continuing. The reply in this instance is a dictionary that contains information about the result of the action: ```text Data : (dict) audioDevices : (list) Item : (dict) friendlyName : Apple USB audio device (string) icon : [SNIP] identifier : AppleUSBAudioEngine:Apple Inc.:Apple USB audio device:241000:2,1 (string) Item : (dict) friendlyName : Built-in Microphone (string) icon : [SNIP] identifier : AppleHDAEngineInput:1B,0,1,0:1 (string) Item : (dict) friendlyName : USB audio CODEC (string) icon : [SNIP] identifier : AppleUSBAudioEngine:Burr-Brown from TI:USB audio CODEC:400000:2,1 (string) recentApplications : (list) Item : (dict) friendlyName : iTunes (string) icon : [SNIP] identifier : /Applications/iTunes.app (string) Item : (dict) friendlyName : iMovie (string) icon : [SNIP] identifier : /Applications/iMovie.app (string) systemAudio : (list) Item : (dict) friendlyName : System Audio (string) icon : [SNIP] identifier : com.rogueamoeba.source.systemaudio ``` Check the documentation for each plugin for the necessary information and more examples. --- Python Packages (https://docs.indigodomo.com/2025.2/scripting/guides/python-packages/) --- # Python Packages and Indigo !!! abstract "In this guide" How Python 3.13 is bundled with Indigo {{ version }}, which packages come pre-installed, how to install additional packages using `pip3`, and where plugin packages should be installed to avoid conflicts. Also covers the risks of installing other Python versions on the same Mac. This page describes how **Indigo {{ version }}.0** works with Python, specifically with respect to packages/libraries. Indigo {{ version }} ships with [API version 3.8](https://www.indigodomo.com/indigo/api_release_notes/3.8/) (check the [Api Version Chart](https://www.indigodomo.com/indigo/api_version_chart.html) to see what version of the API is available in which Indigo versions). Python 3 support was introduced in Indigo 2023.2 (API 3.4), and Python 3 is automatically installed as part of the Indigo installer. For Indigo {{ version }}, we are including **Python 3.13**. **All embedded and external Python scripts are run in Python 3** - so you will need to update your scripts if they aren't working (we can't automatically tell if they will work or not unfortunately). We believe that simple scripts will most likely work without change, but if not, you can walk through [the document](https://github.com/IndigoDomotics/IndigoSDK/blob/main/Updating%20to%20API%20version%203.0%20(Python%203).md) mentioned above and perhaps spot the changes you need. You can also post your scripts on the [Help Converting to Python 3](https://forums.indigodomo.com/viewforum.php?f=364) forum to get help with any conversion issues. The Indigo {{ version }} installer includes the Python installer (including Intel and Apple Silicon support) from [https://www.python.org](https://www.python.org) - the official Mac installers for the Python language. As a result, any installs of other versions of Python from that website may cause unexpected results in Indigo and potentially cause it to break. We've taken all precautions we can try to avoid conflicts, but there is only so much we can do. So please use caution when installing other Python versions to your server machine. If you have any questions about other Python versions, [post your questions on our forums](https://forums.indigodomo.com/viewforum.php?f=106). ## Installing Additional Python Packages with Indigo {{ version }} As noted above, Indigo {{ version }} ships with Python 3.13 and which contains several modules in addition to Python's standard core modules. The specific modules installed with each version of Python are listed below. However, in some instances, you may need to manually install one or more additional Python packages to support a third-party plugin or your own custom scripts. The manner in which this is done, depends on the Indigo API version that the plugin supports. Use `pip3 install [package]` commands to install packages: ```text Last login: Tue jan 1 12:34:56 on console user@my mac ~ % pip3 install tensorflow ... ``` To see which packages are installed, use the `list` command: ```text Last login: Tue jan 1 12:34:56 on ttys000 user@my mac ~ % pip3 list Package Version ------------------ --------- astroid 2.9.3 certifi 2021.10.8 charset-normalizer 2.0.11 cycler 0.11.0 fonttools 4.29.1 ... user@my mac ~ % ``` !!! note "Note" Some Python packages -- and specifically those that are included in IPH3 as [described below](#python-packages-installed-by-indigo) -- will not appear in the `pip3 list` output. To see more detailed information about an individual package, use the `show` command. ```text Last login: Sun Mar 27 10:20:04 on ttys000 user@my mac ~ % pip3 show requests Name: requests Version: 2.27.1 Summary: Python HTTP for Humans. Home-page: https://requests.readthedocs.io Author: Kenneth Reitz Author-email: me@kennethreitz.org License: Apache 2.0 Location: /Library/Frameworks/Python.framework/Versions/3/lib/python3.13/site-packages Requires: charset-normalizer, certifi, idna, urllib3 Required-by: user@my mac ~ % ``` Particularly helpful in the `show` output is the dependencies information -- both what the package requires and what requires the package. Note that the dependencies information may not include information about packages that are included with the IPH. !!! warning Installing additional Python versions on the Indigo {{ version }} server machine is ****NOT**** recommended (see above). Installing additional Python modules should only be done when necessary. ### Python Packages for Plugin Developers Because the packages included with Indigo should not be modified (to ensure a smoothly running server), plugin developers must include any other desired version within the plugin package. Plugin developers should note that some Python packages will not integrate universally across all installations as they rely on code compiled for specific hardware configurations (for example, Intel vs. Apple silicon). The IPH processes use the following [Module Search Paths](https://docs.python.org/3/tutorial/modules.html#the-module-search-path) (Python interpreters use `PYTHONPATH` to describe the order of directories to search for installed packages, the first matching package will be used): The `PYTHONPATH` for IPH3 is: 1. `Contents/Server Plugin` (inside the plugin bundle) 1. `Contents/Packages` (inside the plugin bundle) 1. IndigoPluginHost3 (inside the app itself) 1. `/Library/Application Support/Perceptive Automation/Python3-includes` 1. `/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages` Packages are installed in several ways: 1. You may add modules/packages **that you develop yourself** to the `Python#-includes` folders above, and those will be shared across all Indigo Plugins and Scripts. 1. Other packages are installed using a `pip3` process. **Warning**: Indigo will preinstall several packages ([listed below](#python-packages-installed-by-indigo)) at install time. You should avoid modifying those unless absolutely necessary as other 3rd party plugins may rely on them. If you are going to instruct users to install packages that support your plugin using *`pip`*, you should probably do one of the following: #### Using a "requirements.txt" File If your plugin requires libraries that are not part of the basic Python installation or packages installed by Indigo, the preferred method is to include a list of those modules so Indigo can install them automatically when your plugin is installed. This automatic process is available in Server API version 3.4 and later (prior API versions will not invoke this process). This is handled by the following process: If a plugin contains the plain text file: *`Contents/Server Plugin/requirements.txt`*, we'll run *`pip`* to install the listed modules into *`Contents/Packages`* (an example is shown below). 1. When a plugin starts up, it'll look for the *`Contents/Server Plugin/requirements.txt`* file. If it's not there, we'll continue with plugin startup. If it **is** there, we'll proceed to the next step. 1. Indigo will check to see if this file exists: *`Contents/Packages/pip-install-log-success.txt`*. If it does exist (meaning that we've already successfully installed requirements before), we will continue plugin startup. If it doesn't exist, we'll proceed to the next step. 1. We run *`pip3`* with the requirements file and target the *`Contents/Package`* directory (all libraries and dependencies will be installed there). If it fails, we output an error and log the install attempt to the Event Log window and stop the plugin. If it installs correctly, we write the install log results to the *`Contents/Packages/pip-install-log-success.txt`* file so that the next time it starts it won't do it again (see previous step above). If processing the *`requirements.txt`* file has failed (say, if the network is down so that pip can't get to pypi.org to download), it will continue to try when the user reloads the plugin, so transient issues will eventually resolve themselves. If the error is caused by a problem with the *`requirements.txt`* file or with a specific requirement, the developer will need to address in a new version. Here is a sample *`requirements.txt`* file. Note that the following includes references to specific versions of the packages to be installed. We strongly recommend this approach to minimize the possibility that a newer version could be installed which may cause problems with your plugin. ```text zeroconf==0.130.0 websocket-client==1.7.0 ``` **Best Practices:** If you're using GitHub, be sure that the contents of the `*../Packages*` folder is included in your `*.gitignore*` file. This will ensure the `*Contents/Packages/pip-install-log-success.txt*` and any other content is not included as a part of your package. When testing your plugin, you should include any needed binaries in this folder so when you publish an update, the binaries will not be included. **Troubleshooting:** If you find that packages are not being installed when a user installs/updates your plugin, one cause might be that you have a *`Contents/Packages/pip-install-log-success.txt`* file left over in your plugin bundle. If it's in the bundle, Indigo will assume the packages are already installed. If you're working on transitioning from included binaries to using a `*requirements.txt*` file, be sure to remove the deprecated binaries from your package. If they're present, Indigo may favor those over the libraries you're trying to install automatically. ##### Manual Installation by User **Using pip:** If you're going to instruct users to install needed packages individually, they should use a command similar to: `pip3 install package -t /Path/To/Plugin/Contents/Packages/` or, if a specific version is required, use: ```text pip3 install package==1.2.3 -t /Path/To/Plugin/Contents/Packages/ ``` which will install the libraries to the specified folder that exists in your plugin bundle. It is strongly recommended that you use the *`Packages`* folder as this may be required in future versions of Indigo. **Logging Import Errors** If your plugin requires one or more libraries that your users will install manually, you should provide some helpful messages to guide them through the process. One such way is to monitor import failures and notify users that they will need to install some additional libraries in order to use your plugin, such as: ```python import_errors = [] try: import zeroconf except ImportError: import_errors.append("zeroconf") ``` ```python def startup(self): self.logger.debug("startup called") if len(import_errors): msg = f"Required Python libraries missing. Run the following command(s) in a Terminal window to install them, then reload the plugin.\n\n" for i in import_errors: msg += f'pip3 install {i} -t "{self.pluginFolderPath}/Contents/Packages/"\n' self.logger.error(msg) return "Plugin startup canceled due to missing Python libraries." ``` #### Access to IPH3 Libraries There are some important implications that result from having some libraries included in the IPH3. Namely, those packages will be available to plugins, Indigo scripts (embedded and linked), the Indigo python shell (from the menu item or by doing `indigo-host` in a terminal window), but IPH3 libraries will ****NOT**** be available if you start the python interpreter directly (by doing python2 or python3 in a terminal window). Refer to this chart to determine accessibility: | Method | Access to
IPH3 Libraries | | --- | --- | | plugins | yes | | embedded scripts | yes | | linked scripts | yes | | Indigo python shell using Indigo menu | yes | | Indigo python shell using Terminal `indigo-host` | yes | | Mac Terminal using `python3` command | no | #### Python Packages for Scripters All Python scripts (either embedded or linked) are executed using the IPH3 process using Python 3. Therefore, the server will be able to locate any packages your scripts use in the same way as described above: 1. IndigoPluginHost3 1. /Library/Application Support/Perceptive Automation/Python3-includes 1. /Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages If you need a module/package for your plugin, you can install it via `pip3` as shown above, which will install it into the Python `site-packages` folder. We recommend (but don't require) the following folders be used for custom scripts and modules: - **/Library/Application Support/Perceptive Automation/Scripts** -- any linked scripts you use for Indigo Schedules and Actions using the "Execute Script > Script and File Actions" command should be stored in this folder. - **/Library/Application Support/Perceptive Automation/Python3-includes** -- any modules you have developed (a module is a file that contains classes, functions, constants, etc.) that you want to share across multiple scripts, should be stored in the `/Library/Application Support/Perceptive Automation/Python3-includes` folder. (Indigo versions prior to 2022 should use the `/Library/Application Support/Perceptive Automation/Python2-includes` folder). !!! warning "Warning 1" As mentioned above, Indigo will preinstall several packages ([listed below](#python-packages-installed-by-indigo)) at install time. You should avoid modifying those unless absolutely necessary as other 3rd party plugins may rely on them. !!! warning "Warning 2" You should use caution when naming your custom scripts to ensure they don't supplant other Python packages. For example, if you install the **holidays** Python package using `pip3 install holidays`, it will be installed in the **site-packages** folder. If you also have a custom script in the `Python3-includes` folder named **holidays.py**, the IPH will import your script instead of the **site-packages** version (because the Python3-includes folder is searched before the site-packages folder). #### If Something Goes Wrong To provide users with a measure of safety regarding the Python packages that Indigo installs, the Indigo installer also includes the capacity to repair the Python installation if something should become damaged. For example, if a user were to accidentally upgrade one of the Python packages -- and as a result cause the Indigo server to stop functioning properly -- re-running the Indigo installer may be able to bring things back under control. There's [a few things you can try](../../user/troubleshooting/python-conflicts.md) should you run into conflicts between Python versions. Re-running the Indigo installer on an existing server shouldn't affect user-installed libraries (existing user-installed libraries should be unaffected). #### Moving Indigo to a New Machine Depending on the method used, any Python modules installed via **pip3** may need to be re-installed on the new machine. For more information, consult [Moving an Indigo Installation to Another Mac](../../user/maintenance/moving.md). #### Python 3.13.9 Packages Installed by Indigo { #python-packages-installed-by-indigo } Indigo {{ version }} ships with support for the following Python 3 packages in addition to the packages installed with the standard Python framework. Generally, if earlier versions of the listed packages are installed at the location Indigo uses, they will be upgraded to the version numbers below. The *`>=`* symbol means the version installed by Indigo may be newer than the version listed, but will not be older. *`pyserial`* is also installed but is installed differently than the other packages listed above and should be unmodifiable. Libraries marked with * are dependencies and may get newer versions on later installs. **You should not update any of the libraries listed as they (and their dependencies may rely on a specific version.)** | PIP installed by Indigo
PIP Modifiable (but not recommended) | | |-----------------------------------------------------------------|--------------| | Package | Version | | * aiofiles | >=24.1.0 | | * anyio | >=4.8.0 | | certifi | ==2023.11.17 | | * cffi | >=1.17.1 | | charset_normalizer | ==3.3.2 | | * contourpy | >=1.3.1 | | cryptography | ==41.0.7 | | * cycler | >=0.12.1 | | dictdiffer | ==0.9.0 | | * fonttools | >=4.56.0 | | future | ==0.18.3 | | * h11 | >=0.14.0 | | * html5tagger | >=1.3.0 | | * httpcore | >=1.0.7 | | * httptools | >=0.6.4 | | httpx | ==0.25.2 | | idna | ==3.6 | | janus | ==1.0.0 | | * jedi | >=0.19.2 | | Jinja2 | ==3.1.2 | | * kiwisolver | >=1.4.8 | | MarkupSafe | ==2.1.3 | | matplotlib | ==3.8.2 | | * multidict | >=6.1.0 | | * numpy | >=1.26.4 | | oauthlib | ==3.2.2 | | * packaging | >=24.2 | | * parso | >=0.8.4 | | Pillow | ==11.1.0 | | pudb | ==2023.1 | | py-applescript | ==1.0.3 | | pyaes | ==1.6.1 | | * pycparser | >=2.22 | | * Pygments | >=2.19.1 | | pyobjc | ==10.0 | | pyparsing | ==3.1.1 | | python-box | ==7.3.2 | | python-dateutil | ==2.8.2 | | requests | ==2.31.0 | | requests-oauthlib | ==1.3.1 | | sanic | ==23.6.0 | | * sanic-routing | >=23.12.0 | | sanic-session | ==0.8.0 | | scipy | ==1.11.4 | | six | ==1.16.0 | | * sniffio | >=1.3.1 | | * tracerite | >=1.1.1 | | * typing_extensions | >=4.12.2 | | * ujson | >=5.10.0 | | urllib3 | ==2.1.0 | | * urwid | >=2.6.16 | | * urwid-readline | >=0.15.1 | | * uvloop | >=0.21.0 | | * wcwidth | >= 0.2.13 | | websockets | ==14.1 | | xmljson | ==0.2.1 | | | | | pyserial | 3.5 | ## Special Packages This section contains notes and examples about some library features that may be of particular help to developers. ### Python Box Depending on your Indigo version, we may have installed the [Python-Box library](https://github.com/cdgriffith/Box). This library allows the user to use JavaScript style dot notation to access parts of a collection. There are good descriptions of all the functionality on the library's page link above. What we were most interested in is a `box` instance that has the `box_dots` option set to true, like this: ```text my_box = box.Box(some_dict, box_dots=True) ``` This allows developers to specify a string using JavaScript style dot notation to get to parts of the `box` object: ```text some_element = my_box["keyhere.[0].anotherkey"] ``` Here are some concrete examples: ```python import box # Given this dictionary my_dict = { "a-list":[1, 2, {"dict-in-list":"fun stuff"}], "a-dict":{"first":"first element", "second":"second element"} } my_box = box.Box(my_dict, box_dots=True) # create a box object of my_dict dict_in_list = my_box["a-list.[2].dict-in-list"] # equals "fun stuff" int_from_list = my_box["a-list.[0]"] # equals 1 a_dict = my_box["a-dict"] # equals {'first': 'first element', 'second': 'second element'} ``` This is useful in a variety of ways, but we use it extensively in the [Event Data Passing](../../user/automation/event-data.md) and [Webhook](../../api/webhooks.md) features. You can determine whether you have the *`box`* library installed by simply trying to import it into a scripting shell or embedded script. --- Action Groups (https://docs.indigodomo.com/2025.2/scripting/reference/action-groups/) --- # Action Groups The following properties and commands are available with the Action Group object class. ## Class Properties { .ref-head-no-code } | Property | Type | Writable | Description | |--------------------------------------------|------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `description` | string | Yes | description of the action group | | `folderId` | integer | No | unique ID of the folder this action group is in | | `id` | integer | No | a unique id of the action group, assigned on creation by IndigoServer | | `name` | string | Yes | the unique name of the action group - no two action groups can have the same name | | `remoteDisplay` | boolean | No | `True` if this action group is shown in remote clients, `False` otherwise | | `sharedProps` | dictionary | No | **[API v2.3](https://www.indigodomo.com/indigo/api_release_notes/2.3/)** : an `indigo.Dict()` representing the name/value pairs that are shared by all plugins. This is the property dictionary that you can edit via the Global Properties plugin, and your plugin may manage properties in this dictionary as well to add metadata to devices that your plugin can use for other purposes. Use `ag.replaceSharedPropsOnServer()` to update them (as with pluginProps, you should get copy first, update the copy, then set them back to that copy so you don't accidentally remove some other plugin's props). | ## Commands (indigo.actionGroup.*) { .ref-head-no-code } The commands in this section are common to all action groups regardless of type. ### Delete { .ref-head-no-code } Delete the specified action group. **Command Syntax Examples** ```python indigo.actionGroup.delete(123) ``` **Parameters** | Parameter | Required | Type | Description | |------------------|----------|---------|----------------------------------------------| | direct parameter | Yes | integer | id or instance of the action group to delete | ### Display In Remote UI { .ref-head-no-code } This command will show or hide the action group in remote clients. **Command Syntax Examples** ```python indigo.actionGroup.displayInRemoteUI(123, value=True) indigo.actionGroup.displayInRemoteUI(123, value=False) ``` **Parameters** | Parameter | Required | Type | Description | |------------------------------------|----------|---------|-------------------------------------------------------| | direct parameter | No | integer | id or instance of the action group | | `value` | Yes | boolean | `True` to show the action group or `False` to hide it | ### Duplicate { .ref-head-no-code } Duplicate the specified action group. This method returns a copy of the new action group. **Command Syntax Examples** ```python indigo.actionGroup.duplicate(123, duplicateName="New Name") ``` **Parameters** | Parameter | Required | Type | Description | |--------------------------------------------|----------|---------|-------------------------------------------------| | direct parameter | Yes | integer | id or instance of the action group to duplicate | | `duplicateName` | No | string | name for the new action group trigger | ### Execute { .ref-head-no-code } Execute the specified action group. **Command Syntax Examples** ```python indigo.actionGroup.execute(123, event_data=some_dict) ``` **Parameters** | Parameter | Required | Type | Description | |------------------|----------|---------|----------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the action group to execute | | event_data | No | dict | a dictionary instance that will get passed to all actions in the action group for processing | A note on `event_data` - Indigo will automatically add a `source` key to your dictionary to represent where the action execution came from: - "server" if it's something generated from the server itself (schedule execution, built-in trigger, etc.) - "python" if it's something that comes through IPH that doesn't already have a source attached (scripts, plugins) - "api-http" if it came from the HTTP API and there wasn't already an included "source" - "api-websocket" if it came from the websocket API and there wasn't already an included "source" However, if you include a `source` key in your `event_data`, we will not overwrite it, we'll just pass through whatever your value is. ### Get Dependencies { .ref-head-no-code } Use this command to retrieve all the Indigo objects dependent on the action group. The command will return an `indigo.Dict` containing all the dependencies. **Command Syntax Examples** ```python indigo.actionGroup.getDependencies(123) ``` **Parameters** | Parameter | Required | Type | Description | |------------------|----------|---------|------------------------------------| | direct parameter | Yes | integer | id or instance of the action group | ### Move To Folder { .ref-head-no-code } Use this command to move the action group to a different folder. You can get a list of folder id’s by using indigo.actionGroups.folders, which will return a dictionary. The key to the dictionary is the ID, the value is the folder name. **Command Syntax Examples** ```python indigo.actionGroup.moveToFolder(123, value=987) ``` **Parameters** | Parameter | Required | Type | Description | |------------------------------------|----------|---------|----------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the action group | | `value` | Yes | integer | id or instance of the folder to move the action group to | --- Actions (https://docs.indigodomo.com/2025.2/scripting/reference/actions/) --- # Actions In the IOM, all actions are derived from a common Action base class. This base contains all the shared components of actions. Unlike the other major classes, however, there isn't a command namespace for Actions. Why is that, you say? Because Actions don't exist outside the object that contains them (Triggers, Schedules, Action Groups, etc.). So, when manipulating actions, you always work directly on the object, then assign that object to one of the container objects. Don't worry, we'll walk you through it. ## Action Base Class {.ref-head-no-code } The Action class is a base class that provides the common functionality to its subclasses (to follow). You may specify a new `Action` class in your list of actions for a trigger, schedule, or action group, but the action type will be `None` - that is, it will delay and speak any text set in the object, but other than that it will do nothing. Most of the time you'll be using one of the subclasses so that you get the specific functionality you're looking for. **Class Properties** | Property | Type | Description | | --- | --- | --- | | `delayAmount` | integer | number of seconds to delay before executing the actions (0 for none) | | `replaceExisting` | boolean | if true then any existing delayed action is replaced by this one | | `textToSpeak` | string | this is the text to speak when the action is executed | ### Complementary Delays { #complementary-delays .ref-head-no-code } For some action types, the user (and you) may specify that the complementary action be taken after some number of seconds. The table below specifies what complementary actions are available with the various action types: | Complementary Actions | | | --- | --- | | Action | Complement | | `DeviceAction`
`kDeviceAction.TurnOff`
`kDeviceAction.TurnOn`
`kDeviceAction.Toggle`
`kDeviceAction.Lock`
`kDeviceAction.Unlock`
| `DeviceAction`
`kDeviceAction.TurnOn`
`kDeviceAction.TurnOff`
`kDeviceAction.Toggle`
`kDeviceAction.Unlock`
`kDeviceAction.Lock`
| | `DisableScheduleAction` | `EnableScheduleAction` | | `DisableTriggerAction` | `EnableTriggerAction` | | `EnableScheduleAction` | `DisableScheduleAction` | | `EnableTriggerAction` | `DisableTriggerAction` | ## DeviceAction (API v2.0+ only) {.ref-head-no-code } API v2.0+ only: This class represents an action to control dimmers (lights), relay (appliances), and locks. Previously this class was named DimmerRelayAction -- it was renamed in API v2.0. **Class Properties** | Property | Type | Description | | --- | --- | --- | | `complementDelay` | integer | the number of seconds to delay before issuing the complementary action (0 for no action) - see [Complementary Delays](#complementary-delays) for details | | `deviceId` | integer | the id of the device | | `deviceAction` | [kDeviceAction](#device-action-enumeration) | this is the command to send to the device | | `actionValue` | integer or dict | if `deviceAction` is in [Brighten, Dim, SetBrightness] then this is an integer value | ### Device Action Enumeration { #device-action-enumeration .ref-head-no-code } | indigo.kDeviceAction | | | --- | --- | | Value | Description | | `AllLightsOff` | turn off all dimmer (light) devices | | `AllLightsOn` | turn on all dimmer (light) devices | | `AllOff` | turn off all dimmer (light) and relay (appliance) devices | | `BrightenBy` | brighten a dimmer (light) device by the amount specified in the `actionValue` integer property | | `DimBy` | dim a dimmer (light) device by the amount specified in the `actionValue` integer property | | `SetBrightness` | set a dimmer (light) device to the brightness specified in the `actionValue` integer property | | `SetColorLevels` | set the color (RGB) and white levels to the values specified in the `actionValue` dict property | | `Toggle` | toggle the on/off state of a dimmer (light) or relay (appliance) device | | `TurnOff` | turn off a dimmer (light) or relay (appliance) device | | `TurnOn` | turn on a dimmer (light) or relay (appliance) device | | `Lock` | lock a deadbolt or door device | | `Unlock` | unlock a deadbolt or door device | ## DisableScheduleAction {.ref-head-no-code } This class represents an action to disable a schedule. **Class Properties** | Property | Type | Description | | --- | --- | --- | | `complementDelay` | integer | the number of seconds to delay before issuing the complementary action (0 for no action), in this case an EnableScheduleAction - see [Complementary Delays](#complementary-delays) for details | | `scheduleId` | integer | the id of the schedule to disable | ## DisableTriggerAction {.ref-head-no-code } This class represents an action to disable a trigger. **Class Properties** | Property | Type | Description | | --- | --- | --- | | `complementDelay` | integer | the number of seconds to delay before issuing the complementary action (0 for no action), in this case an EnableTriggerAction - see [Complementary Delays](#complementary-delays) for details | | `triggerId` | integer | the id of the trigger to disable | ## EmailAction {.ref-head-no-code } This class represents an action to send an email. **Class Properties** | Property | Type | Description | | --- | --- | --- | | `emailBody` | string | the body of the email to send | | `emailSubject` | string | the subject of the email to send | | `emailTo` | string | the (semicolon separated) list of email addresses | ## EnableScheduleAction {.ref-head-no-code } This class represents an action to enable a schedule. **Class Properties** | Property | Type | Description | | --- | --- | --- | | `complementDelay` | integer | the number of seconds to delay before issuing the complementary action (0 for no action), in this case an `DisableScheduleAction` - see [Complementary Delays](#complementary-delays) for details | | `scheduleId` | integer | the id of the schedule to enable | ## EnableTriggerEventAction {.ref-head-no-code } This class represents an action to enable a trigger event. **Class Properties** | Property | Type | Description | | --- | --- | --- | | `complementDelay` | integer | the number of seconds to delay before issuing the complementary action (0 for no action), in this case an DisableTriggerAction - see [Complementary Delays](#complementary-delays) for details | | `eventId` | integer | the id of the event to enable | ## ExecuteGroupAction {.ref-head-no-code } This class represents an action to execute an action group. **Class Properties** | Property | Type | Description | | --- | --- | --- | | `groupId` | integer | the id of the action group to execute | ## ExecuteScriptAction {.ref-head-no-code } This class represents an action to execute a script. **Class Properties** | Property | Type | Description | | --- | --- | --- | | `scriptCode` | string | this is the source code of the script to execute | ## Get Dependencies {.ref-head-no-code } Return an indigo.Dict with all the dependencies on this action group. **Command Syntax Examples** ```python indigo.actionGroup.getDependencies(123) ``` **Parameters** | Parameter | Required | Type | Description | | --- | --- | --- | --- | | direct parameter | Yes | integer | id or instance of the action group to get the dependencies for. | The dictionary will look something like this: ```python >>> print(indigo.actionGroup.getDependencies(91776575)) Data : (dict) actionGroups : (list) controlPages : (list) devices : (list) schedules : (list) Data : (dict) ID : 552463741 (integer) Name : Between condition test (string) Data : (dict) ID : 296710860 (integer) Name : Greater than condition test (string) triggers : (list) variables : (list) ``` So, the dictionary will have 6 top-level keys: "actionGroups", "controlPages", "devices", "schedules", "triggers", and "variables". Each one of those keys will return a list object. Inside that list object will be multiple dicts, one for each dependency (or an empty list if there are none). Each dependency dictionary has two keys: "ID" which is the unique id and "Name" which is the name of the object. ## InputOutputAction {.ref-head-no-code } This class represents an action to control an input/output module. **Class Properties** | Property | Type | Description | |---------------------------------------|-----------------------------------------------|---------------------------------------------------------------------------------------------| | `deviceId` | integer | this is the ID of the I/O device | | `action` | [kIOAction](#input-output-action-enumeration) | this I/O action to execute | | `index` | integer | if `action` is `TurnOffOutput`, `TurnOnOutput`, the index of the input or output to control | ### Input/Output Action Enumeration { #input-output-action-enumeration .ref-head-no-code } | indigo.kIOAction | | |---------------------------------------------------------|-------------------------------------------------| | Value | Description | | `TurnOffOutput` | turn off the output specified by index | | `TurnOffAllOutputs` | turn off all outputs | | `TurnOnOutput` | turn on the output specified by index | | `RequestStatusAll` | request status of all hardware input/outputs | | `RequestAnalogInputValues` | request all analog input values from the device | | `RequestBinaryInputsStatus` | request all binary input statuses | | `RequestBinaryOutputsStatus` | request all binary output statuses | | `RequestSensorInputValues` | request all sensor input values | ## ModifyVariableAction {.ref-head-no-code } This class represents an action to modify an Indigo variable. **Class Properties** | Property | Type | Description | |---------------------------------------------|-------------------------------------------------|----------------------------------------| | `variableId` | integer | the id of the variable | | `variableAction` | [kVariableAction](#variable-action-enumeration) | the type of variable action to execute | | `variableValue` | string | the new variable value | ### Variable Action Enumeration { #variable-action-enumeration .ref-head-no-code } | indigo.kVariableAction | | |---------------------------------------------|--------------------------------------------------| | Value | Description | | `DecrementValue` | decrement the variable value by 1 | | `IncrementValue` | increment the variable value by 1 | | `SetValue` | set the variable to the `variableValue` property | ## PluginAction {.ref-head-no-code } A plugin action is defined by a plugin, and is similar in definition to a CustomPluginDevice. **Class Properties** | Property | Type | Description | |-------------------------------------------|------------|-------------------------------------------------------------------------------------------------| | `deviceId` | integer | the id of the device | | `pluginId` | string | the unique ID of the plugin, specified in the Info.plist for the plugin (or it’s documentation) | | `pluginTypeId` | string | the id specified in the Actions.xml (or it’s documentation) | | `props` | dictionary | an indigo.Dict() defining this action's parameters | ## SendInsteonGroupCommandAction {.ref-head-no-code } This class represents an action to send an Insteon group command. **Class Properties** | Property | Type | Description | |--------------------------------------|---------------------------------------------------------------|-----------------------------------------------| | `command` | [kInstnGroupCommand](#insteon-send-group-command-enumeration) | this is the group command to send | | `group` | integer | this is the group number to send `command` to | ### Insteon Send Group Command Enumeration { #insteon-send-group-command-enumeration .ref-head-no-code } | indigo.kIOAction | | |-----------------------------------------|-----------------------------------------------------------------| | Value | Description | | `InstantOff` | send the instant (fast) off command to group ignoring ramp rate | | `InstantOn` | send the instant (fast) on command to group ignoring ramp rate | | `Off` | send the off command to group | | `On` | send the on command to group | ## SprinklerAction {.ref-head-no-code } This class represents an action to control a sprinkler module. **Class Properties** | Property | Type | Description | |----------------------------------------------|---------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------| | `deviceId` | integer | the id of the sprinkler device | | `multiplierVarId` | integer | API v1.20+ only: optional elem ID for the variable multiplier (None if no multiplier is specified) | | `sprinklerAction` | [kSprinklerAction](#sprinkler-action-enumeration) | this sprinkler action to execute | | `zoneDurations` | list of float | list of floats that represent the durations in minutes for each zone to schedule - used when `sprinklerAction` is `RunNewSchedule` | | `zoneIndex` | integer | the zone to turn on as a 1-based index -- used when `sprinklerAction` is `ZoneOn` | ### Sprinkler Action Enumeration { #sprinkler-action-enumeration .ref-head-no-code } | indigo.kSprinklerAction | | |--------------------------------------------------|------------------------------------------| | Value | Description | | `RunNewSchedule` | run a new sprinkler schedule | | `RunPreviousSchedule` | run the last executed sprinkler schedule | | `PauseSchedule` | pause the current sprinkler schedule | | `ResumeSchedule` | resume the current sprinkler schedule | | `StopSchedule` | stop the current sprinkler schedule | | `PreviousZone` | set sprinkler to the previous zone | | `NextZone` | set sprinkler to the next zone | | `ZoneOn` | turn on a single zone | | `AllZonesOff` | turn off all zones | | `RequestStatusAll` | request status of all valves | ## ResetInterfacesAction {.ref-head-no-code } This class represents an action to reset the built-in interfaces. There are no extra properties necessary for this action. ## ThermostatAction {.ref-head-no-code } This class represents an action to control a thermostat. **Class Properties** | Property | Type | Description | |-----------------------------------------------|----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------| | `deviceId` | integer | this is the ID of the thermostat | | `thermostatAction` | [kThermostatAction](#thermostat-action-enumeration) | this thermostat action to execute | | `actionMode` | [kFanMode](device-subclasses/thermostat.md#fan-mode-enumeration)
OR
[kHvacMode](device-subclasses/thermostat.md#hvac-mode-enumeration) | if action is SetFanMode, then a kFanMode enumeration
if action is SetHvacMode, then a kHvacMode enumeration | | `actionValue` | float | if action is `Decrease` or `Increase`, the amount to increase/decrease the setpoints
if action is `Set`, the temperature to set the setpoint to | ### Thermostat Action Enumeration { #thermostat-action-enumeration .ref-head-no-code } | indigo.kThermostatAction | | |----------------------------------------------------|------------------------------------------------------------------------| | Value | Description | | `DecreaseCoolSetpoint` | decrease setpoint by value | | `DecreaseHeatSetpoint` | decrease setpoint by value | | `IncreaseCoolSetpoint` | increase setpoint by value | | `IncreaseHeatSetpoint` | increase setpoint by value | | `SetCoolSetpoint` | set the setpoint to value | | `SetFanMode` | set the fan mode to mode | | `SetHeatSetpoint` | set the setpoint to value | | `SetHvacMode` | set the hvac mode to mode | | `RequestStatusAll` | request all current values from the thermostat | | `RequestMode` | request the current mode of the thermostat | | `RequestEquipmentState` | request the current operational state of the compressor, furnace, etc. | | `RequestTemperatures` | request all current temperatures from the thermostat | | `RequestHumidities` | request all current humidities from the thermostat | | `RequestDeadbands` | request the current deadband ranges from the thermostat | | `RequestSetpoints` | request all current setpoints from the thermostat | ## UniversalAction (API v2.0+ only) {.ref-head-no-code } API v2.0+ only: This class represents a universal action that can be used with devices of different classes. Previously this class was named GeneralDeviceAction -- it was renamed in API v2.0. **Class Properties** | Property | Type | Description | |-------------------------------------------|--------------------------------------------------------|-------------------------------------------| | `deviceId` | integer | the id of the device | | `deviceAction` | [kUniversalAction](#general-device-action-enumeration) | this is the command to send to the device | ### General Device Action Enumeration { #general-device-action-enumeration .ref-head-no-code } | indigo.kUniversalAction | | |--------------------------------------------|------------------------------------------------------------------------| | Value | Description | | `Beep` | request that the device perform an audible beep or buzz | | `EnergyUpdate` | request that the energy meter send its most recent meter data | | `EnergyReset` | request that the energy meter reset its accumulative energy usage data | | `RequestStatus` | send a device a status request for a complete update | --- Event Data Path Specifiers (https://docs.indigodomo.com/2025.2/scripting/reference/event-data-paths/) --- # Events Data Path Specifiers ## Path Strings Path strings use a specific syntax and require knowledge about the data you'll be working with. For example, given this Indigo event data: ```json { "foo": 1234567890, "bar": "Baz", "data": ["Thing 1", "Thing 2"], # a list "more_data": {'a': 1, 'b': 2}, # a dictionary "timestamp": "2025-08-07T14:32:21", } ``` You could use path strings like these to retrieve the associated data: | Path String | Result | |--------------------|------------------------| | *`bar`* | "Baz" | | *`data`* | ["Thing 1", "Thing 2"] | | *`more_data['b']`* | 2 | If you want to go deeper into the payload, path strings can be chained together like this: ```json { "foo": 1234567890, "bar": "Baz", "data": [ "Thing 1", [ "Thing A", "Thing B", "Thing C" ] ], "more_data": { "a": 1, "b": 2, "c": [ 1, 2, 3, 4, 5 ] }, "timestamp": "2025-08-07T14:32:21" } ``` You can go deeper like this: | Path String | Result | |--------------------|------------------------------------------------------------------------------------------------------------------------| | *`data[0]`* | "Thing 1" (The index of the list element, the index starts at zero) | | *`data[1][2]`* | "Thing C" # The second element (index 1) of "data" is a list and the third element (index 2) of that list is "Thing C" | | *`more_data.a`* | 1 # The value of key "a" is 1 | | *`more_data.c[3]`* | 4 # The value of key "c" is a list and the fourth element (index 3) of that list is 4 | You can chain these path strings as needed, such as *`some_json[3].a.foo[9]`*. NOTE: if a path string is not provided, Indigo will return the entire payload. Any path that includes a collection (like the lists and dicts above), Indigo will convert it to JSON and respond with that data. ## Indigo-Supplied Event Data We updated most of the built-in events (Triggers, Schedules, etc.) to pass through the event data that's specific to those events. All events, regardless of what they are, will contain the following values: ```json { "event-indigo-id": 127375748, "event-type": "VariableValueChangeTrigger", "source": "server", "timestamp": "2025-08-07T14:13:54" } ``` The `event-indigo-id` is the Indigo ID for the trigger, schedule, or action group. The `event-type` is the IOM event type. `source` is how the trigger was fired, and `timestamp` is an ISO formatted `datetime` of the event. Each specific trigger/schedule/action group may add additional data that will assist you later in the event processing chain. The following blocks provide samples of event data that Indigo supplies for various events. ### Action Groups ```json [ { "_comment": "This is what you get when you execute an action group from the http api", "event-indigo-id": 1893263747, "event-type": "ActionGroup", "source": "api-http", "timestamp": "2025-10-16T14:59:05", "unique-id": "1dd66fc1-ce8c-43f7-996f-5b29278c1b90" }, { "_comment": "When a webhook is called with a JSON dictionary (in data param)", "data": { "id": "ShortcutWithJsonInputTests.test_webhook_json_input_var_output", "message": "something-happened", "some-data": { "some-key": [ "some-value", "some-other-value" ], "unique-id": "9d34e21b-9926-4023-ab94-c0cc52ecedd5" } }, "event-indigo-id": 879507411, "event-plugin-event-id": "simpleWebhook", "event-plugin-id": "com.indigodomo.webserver", "event-plugin-name": "Web Server", "event-type": "PluginEventTrigger", "http-method": "POST", "http-post-content": "JSON", "request-url": "https://localhost:8176/webhook/shortcut-json-input-var-output", "source": "python", "status-code": 200, "timestamp": "2025-10-16T14:58:55", "webhook-id": "shortcut-json-input-var-output" } ] ``` ### Control Page Clicks ```json { "client-ip": "127.0.0.1", "client-is-private": true, "control-id": 0, "event-indigo-id": 120091806, "event-type": "ControlPage", "timestamp": "2025-10-16T16:10:54" } ``` ### Device State Change Trigger If a device state has any change. ```python { "_comment": "When the server executes a has any change device state change trigger" "change-type": "any change", "change-type-iom": "indigo.kStateChange.Changes", "device-id": 1167180255, "event-indigo-id": 625370601, "event-type": "DeviceStateChangeTrigger", "new-value": "on", "old-value": "off", "source": "server", "state-key": "onOffState", "timestamp": "2025-10-16T17:07:23" } ``` ### Insteon Command Received ```json [ { "_comment": "when any insteon command is received", "event-indigo-id": 1086420814, "event-type": "InsteonCommandReceivedTrigger", "insteon-cmd-rcvd": { "Address": 1652724, "AddressStr": "19.37.F4", "CommandDetails": 0, "CommandName": "on", "CommandVal": 0, "GroupBroadcastNum": 1, "IsGroupBroadcast": true, "SendCleanups": false }, "source": "server", "timestamp": "2025-09-24T15:00:12" }, { "_comment": "when a double-tap off command is received" "event-indigo-id": 1398699236, "event-type": "InsteonCommandReceivedTrigger", "insteon-cmd-rcvd": { "Address": 1652724, "AddressStr": "19.37.F4", "CommandDetails": 0, "CommandName": "off (instant)", "CommandVal": 0, "GroupBroadcastNum": 1, "IsGroupBroadcast": true, "SendCleanups": false }, "source": "server", "timestamp": "2025-08-12T17:39:49" } ] ``` ### Server Executed Schedules When the server executes a schedule during normal operation. ```json { "event-indigo-id": 485722026, "event-type": "Schedule", "source": "server", "timestamp": "2025-10-16T13:55:02" } ``` ### Variable Changes ```python [ { "change-type": "becomes equal", "change-type-iom": "indigo.kVarChange.BecomesEqual", "event-indigo-id": 1533121690, "event-type": "VariableValueChangeTrigger", "new-val": "var_becomes_equal_to", "old-val": "", "source": "server", "test-val": "var_becomes_equal_to", "timestamp": "2025-10-16T13:56:23", "var-id": 54247914 }, { "change-type": "becomes false", "change-type-iom": "indigo.kVarChange.BecomesFalse", "event-indigo-id": 1202935007, "event-type": "VariableValueChangeTrigger", "new-val": "false", "old-val": "true", "source": "server", "timestamp": "2025-10-16T13:56:24", "var-id": 24284935 }, { "change-type": "becomes greater than", "change-type-iom": "indigo.kVarChange.BecomesGreaterThan", "event-indigo-id": 1952491014, "event-type": "VariableValueChangeTrigger", "new-val": "11", "old-val": "9", "source": "server", "test-val": "10", "timestamp": "2025-10-16T13:56:25", "var-id": 738010762 }, { "change-type": "becomes less than", "change-type-iom": "indigo.kVarChange.BecomesLessThan", "event-indigo-id": 1574402918, "event-type": "VariableValueChangeTrigger", "new-val": "9", "old-val": "11", "source": "server", "test-val": "10", "timestamp": "2025-10-16T13:56:26", "var-id": 1177127882 }, { "change-type": "becomes not equal", "change-type-iom": "indigo.kVarChange.BecomesNotEqual", "event-indigo-id": 986473566, "event-type": "VariableValueChangeTrigger", "new-val": "", "old-val": "var_becomes_not_equal_to", "source": "server", "test-val": "var_becomes_not_equal_to", "timestamp": "2025-10-16T13:56:28", "var-id": 127994353 }, { "change-type": "becomes true", "change-type-iom": "indigo.kVarChange.BecomesTrue", "event-indigo-id": 184109916, "event-type": "VariableValueChangeTrigger", "new-val": "true", "old-val": "false", "source": "server", "timestamp": "2025-10-16T13:56:29", "var-id": 836393156 }, { "change-type": "any change", "change-type-iom": "indigo.kVarChange.Changes", "event-indigo-id": 127375748, "event-type": "VariableValueChangeTrigger", "new-val": "2025-10-16T13:56:30.146312", "old-val": "", "source": "server", "timestamp": "2025-10-16T13:56:30", "var-id": 908899214 } ] ``` ### Z-Wave ```python [ { "_comment": "This is an example of the event_data from a Z-Wave command received, all event types except match raw (see below)", "event-indigo-id": 886317539, "event-plugin-event-id": "zwaveCommand", "event-plugin-id": "com.perceptiveautomation.indigoplugin.zwave", "event-plugin-name": "Z-Wave", "event-type": "PluginEventTrigger", "timestamp": "2025-07-25T15:37:14", "zwavecmd-device-id": 1191650674, "zwavecmd-node-id": 2, "zwavecmd-scene-id": 255 }, { "_comment": "This is an example of the event_data from a Z-Wave command received, Match Raw Packet", "event-indigo-id": 572444380, "event-plugin-event-id": "zwaveCommand", "event-plugin-id": "com.perceptiveautomation.indigoplugin.zwave", "event-plugin-name": "Z-Wave", "event-type": "PluginEventTrigger", "timestamp": "2025-07-25T16:44:40", "zwavecmd-node-id": 2, "zwavecmd-packet": [ 1, 9, 0, 4, 0, 2, 3, 38, 3, 58, 242 ], "zwavecmd-packet-str": "01 09 00 04 00 02 03 26 03 3A F2" }, "If you want to get the match string from the trigger, then you can do this:", " trigger_id = event_data['event-indigo-id'] # get the trigger id", " firing_trigger = indigo.triggers[trigger_id] # get the trigger instance", " zwave_plugin_id = event_data['event-plugin-id'] # get the z-wave plugin id", " zwave_props = firing_trigger.globalProps[zwave_plugin_id] # get the properties", " match_string = zwave_props['matchBytes'] # get the match string from properties" ] ``` --- Folders (https://docs.indigodomo.com/2025.2/scripting/reference/folders/) --- # Folders The folder class represents a folder in the various Indigo user interfaces (Mac client, Indigo Touch, web, etc.). **Class Properties** | Property | Type | Description | |--------------------------------------------|---------|--------------------------------------------------------------------------------------------------------------| | `id` | integer | the unique id of the folder, assigned on creation by IndigoServer | | `name` | string | the name of the folder - no two folders in the same namespace (i.e. `indigo.devices`) can have the same name | | `remoteDisplay` | boolean | should this folder be displayed in remote clients (IWS, Indigo Touch, etc) | ## Commands (indigo.*.folder.*) { .ref-head-no-code } The commands to manipulate folders are within the object lists defined in the IOM Overview page (`indigo.devices.folder.*`, `indigo.variables.folder.*`, etc.) ### Create { .ref-head-no-code } Create a folder. This method returns a **copy** of the newly created folder. **Command Syntax Examples** `indigo.variables.folder.create("Folder Name Here")` **Parameters** | Parameter | Required | Type | Description | |-----------------------------------|----------|--------|------------------------| | `name` | Yes | string | the name of the folder | ### Delete { .ref-head-no-code } Delete the specified folder. **Command Syntax Examples** `indigo.devices.folder.delete(123, deleteAllChildren=True)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------------|----------|---------|------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the folder to delete | | `deleteAllChildren` | No | boolean | a boolean to specify whether all objects contained in the folder should be deleted as well - defaults to `False` | ### Duplicate { .ref-head-no-code } Duplicate the specified folder. This method returns a copy of the new folder. **Command Syntax Examples** `indigo.controlPages.folder.duplicate(123, duplicateName="New Name")` **Parameters** | Parameter | Required | Type | Description | |--------------------------------------------|----------|---------|-------------------------------------------| | direct parameter | Yes | integer | id or instance of the folder to duplicate | | `duplicateName` | No | string | name for the newly duplicated folder | ### Get ID { .ref-head-no-code } Returns the ID of the named folder under the specified object type (device, trigger, etc.) **Command Syntax Examples** `indigo.device.folders.getId("Some Folder Name")` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|--------|---------------------------------------------------| | direct parameter | Yes | string | name of any folder for the specified object type. | ### Set Remote Display { .ref-head-no-code } Use this command to set the remote display flag for the folder. **Command Syntax Examples** `indigo.devices.folder.displayInRemoteUI(123, value=True)`
`indigo.schedules.folder.displayInRemoteUI(123, value=False) `
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|--------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the folder | | `value` | Yes | boolean | True to display the folder on remote user interfaces or False to hide it | ## Examples ```python # create a new variable folder newFolder = indigo.variables.folder.create("My New Variable Folder") # test to see if a folder exists by Name if "My New Variable Folder" in indigo.variables.folders: # should execute this because we just created it indigo.server.log("folder named 'My New Variable Folder' exists") # test to see if a folder exists by ID if newFolder.id in indigo.variables.folders: # should execute this because we just created it indigo.server.log("folder id " + newFolder.id + " exists") # set the remote display flag on the folder immediately indigo.variables.folder.displayInRemoteUI(newFolder, value=False) # a ValueError exception with the text "NameNotUniqueError" is thrown if you try to # create a folder with a name that already exists try: indigo.variables.folder.create("My New Variable Folder") except ValueError as e: if str(e) == "NameNotUniqueError": # should execute this because it's a dup name indigo.server.log("folder named 'My New Variable Folder' already exists") else: indigo.server.log("Some other error") # NOTE - at this point, newFolder.remoteDisplay is still true (default for new folders) # because we're still working with a copy. Refresh it to get it updated: newFolder.refreshFromServer() # change the name of a folder newFolder.name="My Variable Folder" newFolder.replaceOnServer() # duplicate the folder indigo.variables.folder.duplicate(newFolder, duplicateName="My Duplicate Folder") # delete a folder indigo.variables.folder.delete(newFolder) #test to see if a folder doesn't exist using name if "My New Variable Folder" not in indigo.variables.folders: # should execute this because we just deleted it indigo.server.log("folder named 'My New Variable Folder' does not exist on the server") # test to see if a folder doesn't exist using ID (was deleted perhaps) if newFolder.id not in indigo.variables.folders: # should execute this because we just deleted it indigo.server.log("folder id " + newFolder.id + " does not exist on the server") ``` --- Insteon Commands (https://docs.indigodomo.com/2025.2/scripting/reference/insteon-commands/) --- # Insteon Commands (indigo.insteon.*) Commands that are specific to Insteon devices. ## Send Scene Decrease { .ref-head-no-code } This command will send an Insteon Scene Decrease (dim on dimmable loads) command to the specified PowerLinc scene. The value will decrease by 3% for each call. **Command Syntax Examples** `indigo.insteon.sendSceneDecrease(11)`
`indigo.insteon.sendSceneDecrease(11, repeatCount=5)`
`indigo.insteon.sendSceneDecrease(11, suppressLogging=True)`
`indigo.insteon.sendSceneDecrease(11, updateStatesOnly=True)`
`indigo.insteon.sendSceneDecrease("working in office scene")`
**Parameters** | Parameter | Required | Type | Description | |-----------------------------------------------|----------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer or string | either the scene number or the scene name - we encourage numbers since they won't change and the scene names can. | | `repeatCount` | No | integer | a value from 1-32 for the number of times to repeat the decrease (default is 1) | | `suppressLogging` | No | boolean | True if entries in the event log should be suppressed (default is False) | | `updateStatesOnly` | No | boolean | use if you only want Indigo's internal device state representation to be updated - no actual Insteon command will be sent on RF or the power line (default is False) | ## Send Scene Increase { .ref-head-no-code } This command will send an Insteon Scene Increase (brighten on dimmable loads) command to the specified PowerLinc scene. The value will increase by 3% for each call. **Command Syntax Examples** `indigo.insteon.sendSceneIncrease(11)`
`indigo.insteon.sendSceneIncrease(11, repeatCount=5)`
`indigo.insteon.sendSceneIncrease(11, suppressLogging=True)`
`indigo.insteon.sendSceneDecrease(11, updateStatesOnly=True)`
`indigo.insteon.sendSceneIncrease("working in office scene")`
**Parameters** | Parameter | Required | Type | Description | |-----------------------------------------------|----------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer or string | either the scene number or the scene name - we encourage numbers since they won't change and the scene names can. | | `repeatCount` | No | integer | a value from 1-32 for the number of times to repeat the increase (default is 1) | | `suppressLogging` | No | boolean | True if entries in the event log should be suppressed (default is False) | | `updateStatesOnly` | No | boolean | use if you only want Indigo's internal device state representation to be updated - no actual Insteon command will be sent on RF or the power line (default is False) | ## Send Scene ON { .ref-head-no-code } This command will send an Insteon Scene ON command using the specified PowerLinc scene. **Command Syntax Examples** `indigo.insteon.sendSceneOn(11)`
`indigo.insteon.sendSceneOn(11)`
`indigo.insteon.sendSceneOn(11, sendCleanUps=False)`
`indigo.insteon.sendSceneOn(11, suppressLogging=True)`
`indigo.insteon.sendSceneOn(11, updateStatesOnly=True)`
`indigo.insteon.sendSceneOn("working in office scene")`
`indigo.insteon.sendSceneDecrease(11, updateStatesOnly=True)`
`indigo.insteon.sendSceneOn("working in office scene", sendCleanUps=False)`
**Parameters** | Parameter | Required | Type | Description | |-----------------------------------------------|----------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer or string | either the scene number or the scene name - we encourage numbers since they won't change and the scene names can. | | `sendCleanUps` | No | boolean | True if cleanup messages should be sent to each device in the scene after the scene command - this will have the PowerLinc send each device a message to make sure it received the command. You might want to stop cleanup messages from being sent because it involves adding a lot of Insteon traffic and might affect performance (particularly if a group has a lot of responders). But, of course, disabling it may reduce reliability of some modules receiving the scene command so it's usually best to ignore this setting. (default is True) | | `suppressLogging` | No | boolean | True if entries in the event log should be suppressed (default is False) | | `updateStatesOnly` | No | boolean | use if you only want Indigo's internal device state representation to be updated - no actual Insteon command will be sent on RF or the power line (default is False) | ## Send Scene OFF { .ref-head-no-code } This command will send an Insteon Scene OFF command using the specified PowerLinc scene. **Command Syntax Examples** `indigo.insteon.sendSceneOff(11)`
`indigo.insteon.sendSceneOff(11, sendCleanUps=False)`
`indigo.insteon.sendSceneOff(11, suppressLogging=True)`
`indigo.insteon.sendSceneOff(11, updateStatesOnly=True)`
`indigo.insteon.sendSceneOff("working in office scene")`
`indigo.insteon.sendSceneOff("working in office scene", sendCleanUps=False)`
**Parameters** | Parameter | Required | Type | Description | |-----------------------------------------------|----------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer or string | either the scene number or the scene name - we encourage numbers since they won't change and the scene names can. | | `sendCleanUps` | No | boolean | True if cleanup messages should be sent to each device in the scene after the scene command - this will have the PowerLinc send each device a message to make sure it received the command. You might want to stop cleanup messages from being sent because it involves adding a lot of Insteon traffic and might affect performance (particularly if a group has a lot of responders). But, of course, disabling it may reduce reliability of some modules receiving the scene command so it's usually best to ignore this setting. (default is True) | | `suppressLogging` | No | boolean | True if entries in the event log should be suppressed (default is False) | | `updateStatesOnly` | No | boolean | use if you only want Indigo's internal device state representation to be updated - no actual Insteon command will be sent on RF or the power line (default is False) | ## Send Scene Fast ON { .ref-head-no-code } This command will send an Insteon Scene Fast ON command using the specified PowerLinc scene. **Command Syntax Examples** `indigo.insteon.sendSceneFastOn(11)`
`indigo.insteon.sendSceneFastOn(11, sendCleanUps=False)`
`indigo.insteon.sendSceneFastOn(11, suppressLogging=True)`
`indigo.insteon.sendSceneFastOn(11, updateStatesOnly=True)`
`indigo.insteon.sendSceneFastOn("working in office scene")`
`indigo.insteon.sendSceneFastOn("working in office scene", sendCleanUps=False)`
**Parameters** | Parameter | Required | Type | Description | |-----------------------------------------------|----------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer or string | either the scene number or the scene name - we encourage numbers since they won't change and the scene names can. | | `sendCleanUps` | No | boolean | True if cleanup messages should be sent to each device in the scene after the scene command - this will have the PowerLinc send each device a message to make sure it received the command. You might want to stop cleanup messages from being sent because it involves adding a lot of Insteon traffic and might affect performance (particularly if a group has a lot of responders). But, of course, disabling it may reduce reliability of some modules receiving the scene command so it's usually best to ignore this setting. (default is True) | | `suppressLogging` | No | boolean | True if entries in the event log should be suppressed (default is False) | | `updateStatesOnly` | No | boolean | use if you only want Indigo's internal device state representation to be updated - no actual Insteon command will be sent on RF or the power line (default is False) | ## Send Scene Fast OFF { .ref-head-no-code } This command will send an Insteon Scene Fast OFF command using the specified PowerLinc scene. **Command Syntax Examples** `indigo.insteon.sendSceneFastOff(11)`
`indigo.insteon.sendSceneFastOff(11, sendCleanUps=False)`
`indigo.insteon.sendSceneFastOff(11, suppressLogging=True)`
`indigo.insteon.sendSceneFastOff(11, updateStatesOnly=True)`
`indigo.insteon.sendSceneFastOff("working in office scene")`
`indigo.insteon.sendSceneFastOff("working in office scene", sendCleanUps=False)`
**Parameters** | Parameter | Required | Type | Description | |-----------------------------------------------|----------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer or string | either the scene number or the scene name - we encourage numbers since they won't change and the scene names can. | | `sendCleanUps` | No | boolean | True if cleanup messages should be sent to each device in the scene after the scene command - this will have the PowerLinc send each device a message to make sure it received the command. You might want to stop cleanup messages from being sent because it involves adding a lot of Insteon traffic and might affect performance (particularly if a group has a lot of responders). But, of course, disabling it may reduce reliability of some modules receiving the scene command so it's usually best to ignore this setting. (default is True) | | `suppressLogging` | No | boolean | True if entries in the event log should be suppressed (default is False) | | `updateStatesOnly` | No | boolean | use if you only want Indigo's internal device state representation to be updated - no actual Insteon command will be sent on RF or the power line (default is False) | ## Send Scene Start Change { .ref-head-no-code } This command will tell the specified PowerLinc scene to begin increasing/decreasing in value. It will continue to ramp each device in the scene until either the corresponding [Send Scene Stop Change](#send-scene-stop-change) command is called or until the devices are at 100% for increase ramping or 0% for decrease ramping. **Command Syntax Examples** `indigo.insteon.sendSceneIncrease(11, increase=True)`
`indigo.insteon.sendSceneIncrease(11, suppressLogging=True)`
`indigo.insteon.sendSceneIncrease("working in office scene")`
**Parameters** | Parameter | Required | Type | Description | |----------------------------------------------|----------|-------------------|-------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer or string | either the scene number or the scene name - we encourage numbers since they won't change and the scene names can. | | `increase` | Yes | boolean | True if you want to ramp up, False if you want to ramp down | | `suppressLogging` | No | boolean | True if entries in the event log should be suppressed (default is False) | ## Send Scene Stop Change { .ref-head-no-code } This command will tell the specified PowerLinc scene to stop any ramping activity started by a [Send Scene Start Change](#send-scene-start-change) command. **Command Syntax Examples** `indigo.insteon.sendSceneIncrease(11)`
`indigo.insteon.sendSceneIncrease(11, suppressLogging=True)`
`indigo.insteon.sendSceneIncrease("working in office scene")`
**Parameters** | Parameter | Required | Type | Description | |----------------------------------------------|----------|-------------------|-------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer or string | either the scene number or the scene name - we encourage numbers since they won't change and the scene names can. | | `suppressLogging` | No | boolean | True if entries in the event log should be suppressed (default is False) | ## Send Status Request { .ref-head-no-code } This command will send an Insteon Status Request command to the specified address. **Command Syntax Examples** ```python reply = indigo.insteon.sendStatusRequest("0A.B9.DC") indigo.server.log("reply success: %d, ack value: %02X" % (reply.cmdSuccess, reply.ackValue)) ``` **Parameters** | Parameter | Required | Type | Description | |----------------------------------------------|----------|---------|--------------------------------------------------------------------------| | direct parameter | Yes | string | the target Insteon address as a hexadecimal string. | | `waitUntilAck` | No | boolean | true if the caller wants to wait for the result. (default is True) | | `suppressLogging` | No | boolean | True if entries in the event log should be suppressed (default is False) | ## Send Raw { .ref-head-no-code } This command will send a raw Insteon standard command. **Command Syntax Examples** ```python reply = indigo.insteon.sendRaw("0A.B9.DC", [0x10, 0x00]) indigo.server.log("reply success: %d, ack value: %02X" % (reply.cmdSuccess, reply.ackValue)) ``` **Parameters** | Parameter | Required | Type | Description | |---------------------------------------------------|----------|---------|--------------------------------------------------------------------------------------------| | direct parameter | Yes | string | the target Insteon address as a hexadecimal string. | | `cmdBytes` | Yes | list | a list of 2 integer bytes that represent the command to be sent. | | `waitUntilAck` | No | boolean | True if the caller wants to wait for the result. (default is True) | | `waitForStandardReply` | No | boolean | True if the caller wants to wait for a follow-up direct standard reply. (default is False) | | `waitForExtendedReply` | No | boolean | True if the caller wants to wait for a follow-up direct extended reply. (default is False) | | `suppressLogging` | No | boolean | True if entries in the event log should be suppressed (default is False) | ## Send Raw Extended { .ref-head-no-code } This command will send a raw Insteon extended command (command payload can be between 2 and 16 bytes). **Command Syntax Examples** ```python # Get KeypadLinc info (LED states, brightness, etc.) reply = indigo.insteon.sendRawExtended("11.7B.2E", [0x2E, 0x00], waitForExtendedReply=True) if reply.cmdSuccess: indigo.server.log(" backlight brightness: %d" % (reply.replyBytes[10],)) indigo.server.log("button toggle mode bitmap: 0x%02X" % (reply.replyBytes[11],)) indigo.server.log(" button states bitmap: 0x%02X" % (reply.replyBytes[12],)) ``` ```python # Change the Keypad LED brightness from dim to bright setKeypadLedBrightness = [ 0x2E, 0x00, 0x00, # unused 0x07, # change LED backlight brightness 0x11, # brightness between 0x11 and 0x7F 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] for brightness in [0x07, 0x44, 0x66, 0x7F]: setKeypadLedBrightness[4] = brightness indigo.insteon.sendRawExtended("11.7B.2E", setKeypadLedBrightness) ``` **Parameters** | Parameter | Required | Type | Description | |---------------------------------------------------|----------|---------|----------------------------------------------------------------------------------------------------| | direct parameter | Yes | string | the target Insteon address as a hexadecimal string. | | `cmdBytes` | Yes | list | a list of 2 to 16 integer bytes that represent the command to be sent. | | `calcCrc` | No | boolean | automatically calculate 8-bit CRC byte for message (used by i2CS firmware). (default is True) | | `calc16bitCrc` | No | boolean | automatically calculate 16-bit CRC byte for message (used by specific modules). (default is False) | | `waitUntilAck` | No | boolean | True if the caller wants to wait for the result. (default is True) | | `waitForStandardReply` | No | boolean | True if the caller wants to wait for a follow-up direct standard reply. (default is False) | | `waitForExtendedReply` | No | boolean | True if the caller wants to wait for a follow-up direct extended reply. (default is False) | | `suppressLogging` | No | boolean | True if entries in the event log should be suppressed (default is False) | ## Send PowerLinc SET Button Press Message { .ref-head-no-code } This will command the PowerLinc to send its SET Button Press Message, which can be useful in some linking scenarios. **Command Syntax Examples** `reply = indigo.insteon.sendPowerLincSetButtonPress()` **Parameters** | Parameter | Required | Type | Description | |----------------------------------------------|----------|---------|--------------------------------------------------------------------------| | `suppressLogging` | No | boolean | True if entries in the event log should be suppressed (default is False) | ## Subscribe to Events { .ref-head-no-code } Use the lower-level `subscribeToIncoming()` and `subscribeToOutgoing()` methods in the `indigo.insteon` command space to see commands regardless of their effect on device state. **Command Syntax Examples** `indigo.insteon.subscribeToIncoming()`
`indigo.insteon.subscribeToOutgoing()`
For example, ```python def startup(self): self.logger.debug("startup called -- subscribing to all Insteon commands") indigo.insteon.subscribeToIncoming() indigo.insteon.subscribeToOutgoing() ######################################## def insteonCommandReceived(self, cmd): self.logger.debug(f"insteonCommandReceived: \n{str(cmd)}") def insteonCommandSent(self, cmd): self.logger.debug(f"insteonCommandSent: \n{str(cmd)}") ``` --- Schedules (https://docs.indigodomo.com/2025.2/scripting/reference/schedules/) --- # Schedules In the IOM, all schedules are derived from a common Schedule base class. This base contains all the shared components of schedules. ## Schedule Base Class { .ref-head-no-code } All schedules will inherit properties from the Schedule base class. Like other high-level objects in Indigo, there are rules for modifying schedules. For Scripters and Plugin Developers: 1. To duplicate, delete, and send commands to a schedule, use the command namespace as described below 1. To modify an object's definition, get a copy of the schedule, make the necessary changes, then call `mySchedule.replaceSharedPropsOnServer(newPropsDict)` (see below). ### Class Properties { .ref-head-no-code } Under construction | Property | Type | Writable | Description | |-----------------------------------------------|-------------------------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `absoluteDate` | `datetime.datetime` / None | Yes | The absolute date of the next schedule execution with 00:00:00 as the base time. | | `absoluteDateTime` | `datetime.datetime` / None | Yes | The absolute date and time of the next schedule execution. | | `absoluteTime` | `datetime.datetime` / None | Yes | The absolute time of the next schedule execution with 2000-01-01 as the base date. | | `autoDelete` | boolean | Yes | true if Indigo should automatically delete this schedule after the next execution, otherwise false. | | `configured` | boolean | Yes | true if the schedule has been fully configured, otherwise false. | | `dateType` | `indigo.kDateType` / None | | Describes the "type" of date/time options for the schedule. [Absolute / EveryDay / DaysOfWeek / DaysOfMonth] | | `description` | string | Yes | description of the schedule. | | `enabled` | boolean | Yes | true if the schedule is enabled, otherwise false (Indigo will not execute the schedule if false). | | `folderId` | integer | No | unique ID of the folder this schedule is in. | | `globalProps` | dictionary | No | an `indigo.Dict()` that will contain the props for the schedule. It's generally easier to use the shortcut `sharedProps` below. | | `id` | integer | No | a unique id of the schedule, assigned on creation by IndigoServer. | | `name` | string | Yes | the unique name of the schedule - no two schedules can have the same name. | | `nextExecution` | `datetime.datetime` / None | No | The date and time of the schedule's next execution. | | `pluginProps` | dictionary | No | pluginProps will return an empty dict because plugins cannot currently create custom schedules. | | `randomizeBy` | integer | Yes | the number of minutes (plus or minus) Indigo should use to randomize the execution of the schedule. | | `remoteDisplay` | boolean | Yes | true if remote clients should display the schedule, otherwise false (does not affect the Indigo client UI). | | `sharedProps` | dictionary | No | an `indigo.Dict()` containing the name/value pairs that are shared by all plugins. This is the property dictionary that you can edit via the Global Properties plugin, and your plugin may manage properties in this dictionary as well to add metadata to devices that your plugin can use for other purposes. Use `sched.replaceSharedPropsOnServer()` to update them (as with pluginProps, you should get copy first, update the copy, then set them back to that copy so you don't accidentally remove some other plugin's props). | | `sunDelta` | integer | Yes | The number of seconds before (negative) or after (positive) sunrise or sunset when the schedule should be executed (zero if no offset). | | `suppressLogging` | boolean | Yes | true if Indigo should skip logging the schedule's execution in the event log, otherwise false. | | `timeType` | `indigo.kTimeType` | Yes | Absolute / Sunrise / Sunset / Countdown depending on the date and time options selected. | | `upload` | boolean | Yes | true if IndigoServer should attempt to upload this schedule to the interface. | ### Commands (indigo.schedule.*) { .ref-head-no-code } #### Delete { .ref-head-no-code } Delete the specified schedule. **Command Syntax Examples** `indigo.schedule.delete(123)` **Parameters** | Parameter | Required | Type | Description | |------------------|----------|---------|------------------------------------------| | direct parameter | Yes | integer | id or instance of the schedule to delete | #### Duplicate { .ref-head-no-code } Duplicate the specified schedule regardless of the type. This method returns a copy of the new schedule. **Command Syntax Examples** `indigo.schedule.duplicate(123, duplicateName="my duplicate name")` **Parameters** | Parameter | Required | Type | Description | |--------------------------------------------|----------|---------|---------------------------------------------| | direct parameter | Yes | integer | id or instance of the schedule to duplicate | | `duplicateName` | No | string | name for the newly duplicated schedule | #### Enable { .ref-head-no-code } Enables or disables the specified schedule. **Command Syntax Examples** `indigo.schedule.enable(123, value=True, delay=0, duration=0)` **Parameters** | Parameter | Required | Type | Description | |---------------------------------------|----------|---------|---------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the schedule to enable/disable | | `value` | Yes | boolean | set to True to enable the schedule, False to disable the control | | `delay` | No | integer | the number of seconds to wait before executing the command | | `duration` | No | integer | the number of seconds to wait before reverting the executed command | #### Execute { .ref-head-no-code } Execute the specified schedule. **Command Syntax Examples** `indigo.schedule.execute(123, ignoreConditions=False, schedule_data=None)` **Parameters** | Parameter | Required | Type | Description | |-----------------------------------------------|----------|---------|------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the schedule to execute | | `ignoreConditions` | No | boolean | True will execute the schedule regardless of the conditions set within the schedule, False (the default) will honor them | | `schedule_data` | No | object | an `indigo.Dict` to be passed to the schedule before it is executed | A note on `schedule_data` - Indigo will automatically add a `source` key to your dictionary to represent where the action execution came from: - "server" if it's something generated from the server itself (schedule execution, built-in trigger, etc.) - "python" if it's something that comes through IPH that doesn't already have a source attached (scripts, plugins) - "api-http" if it came from the HTTP API and there wasn't already an included "source" - "api-websocket" if it came from the websocket API and there wasn't already an included "source" However, if you include a `source` key in your `schedule_data`, we will not overwrite it, we'll just pass through whatever your value is. For example, if you ```python my_dict = indigo.Dict() my_dict["foo"] = "bar" indigo.schedule.execute(324976872, schedule_data=my_dict) ``` The schedule you executed will receive something like this: ```json {"event-indigo-id": 324976872, "event-type": "Schedule", "foo": "bar", "source": "python", "timestamp": "1970-01-01T09:09:40"} ``` #### Get Dependencies { .ref-head-no-code } Get the dependencies of the specified schedule. Returns an `indigo.Dict` object that contains the schedule's dependencies. Will return an empty `indigo.Dict` object if the schedule has no dependencies. **Command Syntax Examples** `indigo.schedule.getDependencies(123)` **Parameters** | Parameter | Required | Type | Description | |------------------|----------|---------|--------------------------------| | direct parameter | Yes | integer | id or instance of the schedule | #### Move to Folder { .ref-head-no-code } Move the specified schedule to the designated folder. **Command Syntax Examples** `indigo.schedule.moveToFolder(123, value=987)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------|----------|---------|------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the schedule | | `value` | Yes | integer | id or instance of the folder to move the schedule to | #### Remove Delayed Actions { .ref-head-no-code } Remove any outstanding delayed actions from the specified schedule. **Command Syntax Examples** `indigo.schedule.removeDelayedActions(123)` **Parameters** | Parameter | Required | Type | Description | |------------------|----------|---------|--------------------------------| | direct parameter | Yes | integer | id or instance of the schedule | --- Server Properties & Commands (https://docs.indigodomo.com/2025.2/scripting/reference/server-commands/) --- # Server Properties and Commands (indigo.server.*) !!! abstract "In this guide" These are properties and commands that aren't associated with any specific object type and are inside the indigo.server.* command namespace. ## Properties For Connected Indigo Server { .ref-head-no-code } | Property | Type | Writable | Description | |---------------------------------------------|---------|----------|-------------------------------------------------------------------------------------------------------------------------------| | `address` | string | No | the IP address of the currently connected Indigo Server | | `apiVersion` | string | No | [API v1.7](https://www.indigodomo.com/indigo/api_release_notes/1.7/): the currently connected Indigo Server plugin API version as a string (ex: "1.7") | | `connectionGood` | boolean | No | true if the connection to the Indigo Server is currently good | | `licenseStatus` | string | No | [API v2.5](https://www.indigodomo.com/indigo/api_release_notes/2.5/): returns one of the values specified in the `indigo.kLicenseStatus` enumeration below | | `portNum` | integer | No | the port number of the currently connected Indigo Server | | `version` | string | No | the currently connected Indigo Server version string | ## License Status Enumeration { .ref-head-no-code } | `indigo.kLicenseStatus` | | |----------------------------------------------------|----------------------------------------------------------------------------------| | Value | Description | | `ActiveTrial` | the license is a trial | | `ActiveSubscription` | license has an active Indigo Up-to-Date subscription (access to a reflector) | | `ExpiredSubscription` | license has an expired Indigo Up-to-Date subscription (no access to a reflector) | | `Unknown` | license is in an unknown state | ## Broadcast to Subscribers { .ref-head-no-code } This command will broadcast message to other plugins that have subscribed to the specified name message. **Command Syntax Examples** `indigo.server.broadcastToSubscribers(messageName)` ## Calculate Sunrise { .ref-head-no-code } This command will return a datetime object that represents the sunrise for the specified day (or the next sunrise if no date is passed in). **Command Syntax Examples** `indigo.server.calculateSunrise()`
`indigo.server.calculateSunrise(myDateObject)`
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| | direct parameter | No | `datetime.date` | a `datetime.date` object representing the day to calculate the sunrise time for | ## Calculate Sunset { .ref-head-no-code } This command will return a datetime object that represents the sunset for the specified day (or the next sunset if no date is passed in). **Command Syntax Examples** `indigo.server.calculateSunset()`
`indigo.server.calculateSunset(myDateObject)`
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| | direct parameter | No | `datetime.date` | a `datetime.date` object representing the day to calculate the sunrise time for | ## Event Log List { .ref-head-no-code } This command will return a string that contains the latest log entries. Each line is terminated with a line feed character. **Command Syntax Examples** `indigo.server.getEventLogList()`
`indigo.server.getEventLogList(lineCount=5)`
`indigo.server.getEventLogList(showTimeStamp=False)`
`indigo.server.getEventLogList(lineCount=5, showTimeStamp=False)`
`indigo.server.getEventLogList(returnAsList=True, lineCount=5)`
**Parameters** | Parameter | Required | Type | Description | |---------------|----------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | returnAsList | No | boolean | if true a list of dicts is returned containing individual log entry attributes; if false a string containing a textual description of the log lines is returned - the default is false | | lineCount | No | integer | the number of lines to return from the event log starting from newest and going backwards in time - the default is 1500 | | showTimeStamp | No | boolean | indicate whether every line should have its timestamp prepended to the log entry - the default is true | ## Get Database File Name { .ref-head-no-code } Returns the name of the current database name (without the file extension). It takes no parameters. **Command Syntax Examples** `name=indigo.server.getDbName()` ## Get Database File Path { .ref-head-no-code } Returns the POSIX path to the current database file (includes the file name with extension). It takes no parameters. **Command Syntax Examples** `name=indigo.server.getDbFilePath()` ## Get Deprecated Elements { .ref-head-no-code } Returns the server's list of elements that have attributes or properties that are now deprecated. **Command Syntax Examples** `name=indigo.server. getDeprecatedElems(includeWarnings=[True/False]` ## Get Install Folder Path { .ref-head-no-code } Returns the POSIX path to the current Indigo installation path. Useful if you want to manipulate files (like graphics and scripts) that are in the Indigo installation path. It takes no parameters. **Command Syntax Examples** `name=indigo.server.getInstallFolderPath()` ## Get Latitude and Longitude { .ref-head-no-code } Returns a list of floating point numbers where the first float is the latitude and the second (and last) is the longitude. It takes no parameters. **Command Syntax Examples** `latLong = indigo.server.getLatitudeAndLongitude()`
`lat = latLong[0]`
`long = latLong[1]`
## Get Plugin { .ref-head-no-code } Returns a plugin object given the plugin id. **Command Syntax Examples** `myPlugin=indigo.server.getPlugin("com.company.pluginId")` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|--------|----------------------------------| | direct parameter | Yes | string | the id of the plugin to retrieve | See [scripting plugins](../tutorial.md#scripting-indigo-plugins) for details and examples of using this method. ## Get Plugin List { .ref-head-no-code } **[API v2.4](https://www.indigodomo.com/indigo/api_release_notes/2.4/)**: Returns a list of all enabled plugin object instances. **Command Syntax Examples** `enabled_plugin_list=indigo.server.getPluginList()` ## Get Reflector URL { .ref-head-no-code } **[API v2.5](https://www.indigodomo.com/indigo/api_release_notes/2.5/)**: Returns a string with the URL to the active reflector. Returns None if there is no reflector or if remote access is disabled. **Command Syntax Examples** `indigo.server.getReflectorURL()`
`>>> "https://myreflector.indigodomo.net/"`
## Get Serial Ports { .ref-head-no-code } Returns a dictionary representing all serial ports on the server machine. The key is the full path specification for the port (for use by PySerial) and the value is just the name of the port (for display purposes). **Command Syntax Examples** `indigo.server.getSerialPorts()`
`indigo.server.getSerialPorts(filter="indigo.ignoreBluetooth")`
```python ports = indigo.server.getSerialPorts(filter="indigo.ignoreBluetooth") # iterate through the full paths for posixPath in ports: print(posixPath) # iterate through just the port name itself for uiName in ports.itervalues(): print(uiName) # iterate through both for posixPath, uiName in ports.iteritems(): print(posixPath) print(uiName) ``` **Parameters** | Parameter | Required | Type | Description | |-------------------------------------|----------|--------|-----------------------------------------------------------------------------------------------------------------------------------| | `filter` | No | string | currently there’s only one valid filter: "indigo.ignoreBluetooth" which will remove the "Bluetooth-PDA-Sync" option from the list | ## Get Time { .ref-head-no-code } Returns a datetime object representing the server's current time. **Command Syntax Examples** `indigo.server.getTime()` **Parameters**
None ## Get Web Server URL { .ref-head-no-code } Returns a URL string that best represents the URL to the active Indigo Web Server. This is the order of which URL will be returned: 1. Reflector (`https://reflector.indigodomo.net`) if a reflector is configured. 1. Bonjour name (`http://MacName.local:PORT`) if it can be determined. 1. Localhost (`http://localhost:PORT`) if all else fails. Note, there is no trailing slash. `PORT` is the port number of the server (for example, 8176). **Command Syntax Examples** `indigo.server.getWebServerURL()` **Parameters**
None ## Log { .ref-head-no-code } This tells IndigoServer to write a log entry with the specified text. The type in the log will be the name of the plugin. The examples below that refer to the logging package assume that you've done this somewhere before: `import logging` **Command Syntax Examples** `indigo.server.log("Info Text to log")`
`indigo.server.log("Info Text to log", type="myType")`
`indigo.server.log("Warning Text to log", level=logging.WARNING)`
`indigo.server.log("Error Text to log1", level=logging.ERROR)`
`indigo.server.log("Error Text to log2", isError=True)`
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | string | this is the text that’s written to the log | | `type` | No | string | a string representing the type - if it’s not included and is run from a Server Plugin, the name of the plugin will automatically be used | | `level` | No | integer | **[API v2.4](https://www.indigodomo.com/indigo/api_release_notes/2.4/)**: the python logging level which determines both the type shown and the text color used. (ex: using `level=logging.WARNING` will show orange text) | | `isError` | No | boolean | if `True`, it will show up in red in the event log - default is `False` - if no `type` is included, the name of the plugin will automatically be used with " Error" appended | ## Remove All Delayed Actions { .ref-head-no-code } This command will remove all delayed actions currently scheduled. It doesn’t take any parameters. **Command Syntax Examples** `indigo.server.removeAllDelayedActions()` ## Restart Plugin { .ref-head-no-code } This command will tell the server to restart our plugin process. The message is printed to the event log, and if isError is true then it's logged as an error. This command can only be called from a plugin, and it refers to the plugin itself (not other plugins). **Command Syntax Examples** `indigo.server.restartPlugin("Restarting now for some reason", isError=True)` ## Save Plugin Preferences { .ref-head-no-code } The Indigo server will save changes to plugin preferences automatically, and this command will cause the server to save plugin preferences immediately. It doesn’t take any parameters. **Command Syntax Examples** `indigo.server.savePluginPrefs()` ## Send Email { .ref-head-no-code } This tells IndigoServer to send an email using the SMTP settings configured in the preferences "Email" tab. **Command Syntax Examples** `indigo.server.sendEmailTo("my.address@example.com")`
`indigo.server.sendEmailTo("my.address@example.com",`
    `subject="Subject of email", body="Body of email")`
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|--------|-------------------------------------------------| | direct parameter | Yes | string | a semicolon separated string of email addresses | | `subject` | No | string | the subject of the email | | `body` | No | string | the body of the email | ## Speak { .ref-head-no-code } Speak a text string using the built-in speech synthesizer. **Command Syntax Examples** `indigo.server.speak("text to speak", waitUntilDone=True)` **Parameters** | Parameter | Required | Type | Description | |--------------------------------------------|----------|---------|--------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | string | the string to speak | | `waitUntilDone` | No | boolean | should the method call block until speaking is complete or should it just return immediately (queue up the speech) | ## Stop Plugin { .ref-head-no-code } Tell the server to shut down our plugin process. Plugin will remain enabled but be in a stopped state. This command can only be called from a plugin, and it refers to the plugin itself (not all plugins). **Command Syntax Examples** `indigo.server.stopPlugin("Stopping now for some reason", isError=True)` ## Subscribe To Log Broadcasts { .ref-head-no-code } Subscribes to all server event log broadcasts. **Command Syntax Examples** `indigo.server.subscribeToLogBroadcasts()` **Parameters**
None ## Wait Until Idle { .ref-head-no-code } Wait (block) until server has completed event processing and command sending. **Command Syntax Examples** `indigo.server.waitUntilIdle()` **Parameters**
None --- Triggers (https://docs.indigodomo.com/2025.2/scripting/reference/triggers/) --- # Triggers In the IOM, all triggers are derived from a common Trigger base class. This base contains all the shared components of triggers. ## Trigger Base Class { #trigger .ref-head-no-code } All triggers will inherit properties from the Trigger base class - including plugin defined events. Note: plugin defined events will always return False for the upload property. Like other high-level objects in Indigo, there are rules for modifying triggers. For Scripters and Plugin Developers: 1. To create, duplicate, delete, and send commands to a trigger, use the command namespace as described below 1. To modify an object's definition get a copy of the trigger, make the necessary changes, then call `myTrigger.replaceOnServer(newPropsDict)` For Plugin Developers: 1. To update a plugin's props on a trigger, call `myTrigger.replacePluginPropsOnServer(newPropsDict)` rather than try to update them on the local trigger Unlike [Devices](devices/index.md), you can't call `create()` in the trigger base class command namespace (`indigo.trigger.*`). Rather, each subclass has its own `create()` method that takes the appropriate arguments for that trigger type. ### Firing Plugin Defined Triggers { .ref-head-no-code } If you are a plugin developer and your plugin defines events, The process for executing your plugin's events is this: 1. User creates a trigger of type plugin and selects one of your plugin events - configures it (if necessary) and saves. 1. Indigo Server sends the new trigger object to your plugin via the various Trigger Specific Methods in `plugin.py`. The easiest one (parallel to the various device events) is `triggerStartProcessing(self, trigger)`. This method is also called when the server first starts up your plugin - it will pass all triggers defined to your plugin, one at a time, through this method. 1. Your trigger catches the trigger passed in that method and stores it so that it can watch for the conditions that define the event. 1. When the conditions are met that would cause that trigger to fire (plugin implementation specific), you tell the server to [execute the trigger](#execute) using the `indigo.trigger.execute(triggerRef)` method. Note that the execute method will allow you to optionally bypass any conditions - you should NOT do this unless you're providing some kind of override/bypass. Users will clearly want conditions to be taken into account under normal circumstances (that's the default behavior). You will also need to implement `triggerStopProcessing(self,trigger)` so that you can remove disabled/deleted triggers from your watch list. So, the Server isn't really involved at all with the trigger firing (except to execute the trigger when you tell it to) - it only notifies your plugin of events it's supposed to be watching for and your plugin does the rest. ### Class Properties { #properties .ref-head-no-code } | Property | Type | Writable | Description | |----------------------------------------------|------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `description` | string | Yes | description of the trigger | | `enabled` | boolean | Yes | true if this trigger is enabled | | `folderId` | integer | No | unique ID of the folder this trigger is in | | `globalProps` | dictionary | No | an `indigo.Dict()` representing all name/value pairs associated with this trigger - each plugin will have its own dictionary (`globalProps[pluginId]`) - see [About Plugin Properties](#about-plugin-properties) below for details | | `id` | integer | No | a unique id of the trigger, assigned on creation by IndigoServer | | `name` | string | Yes | the unique name of the trigger - no two triggers can have the same name | | `pluginProps` | dictionary | No | an `indigo.Dict()` representing the name/value pairs defined by your plugin for the trigger - plugin developers should publish this information if you want other plugins/scripts to create triggers of this type - see [About Plugin Properties](#about-plugin-properties) below for details | | `sharedProps` | dictionary | No | **[API v2.3](https://www.indigodomo.com/indigo/api_release_notes/2.3/)** : an `indigo.Dict()` representing the name/value pairs that are shared by all plugins. This is the property dictionary that you can edit via the Global Properties plugin, and your plugin may manage properties in this dictionary as well to add metadata to devices that your plugin can use for other purposes. Use `trigger.replaceSharedPropsOnServer()` to update them (as with pluginProps, you should get copy first, update the copy, then set them back to that copy to avoid accidentally removing some other plugin's props). | | `suppressLogging` | boolean | Yes | true if execution of this trigger will not be logged into the event log | | `upload` | boolean | Yes | true if IndigoServer should attempt to upload this trigger to the interface - will always be false for plugin triggers | ### About Plugin Properties { .ref-head-no-code } Triggers have properties - some are class properties, defined by the class itself. One of the biggest requests we've gotten in the past is some way to add arbitrary properties to an object - so that you could store your own data with the object in the database. And with plugin defined triggers, we needed a place to store the properties that you need to operate the trigger. That's what the `pluginProps` and `globalProps` represent - the additional properties that are not defined by the class. `globalProps` is a dictionary of every additional property defined for the trigger - each plugin has its own dictionary of props in here which are readable by anyone. `pluginProps` is a shortcut to get to your plugin's props and are only writable by your plugin. We mentioned before that triggers were read-only, and that's true, and that you'd need to use commands in a different command name space. That's ***mostly*** true. Here's another exception to that rule: to change a trigger's pluginProps (it must be "owned" by your plugin - that is, the pluginId must be set to your id), you use a method that's in the trigger's class: replacePluginPropsOnServer(). Here's an example: ```python trigger=indigo.triggers[123] localPropsCopy = trigger.pluginProps localPropsCopy["pollInterval"] = 10 trigger.replacePluginPropsOnServer(localPropsCopy) ``` You would use this technique if you wanted to just change some of the properties that are already defined. Because this method replaces **all** the properties for your plugin in the trigger, you can just set them all in one call: ```python trigger=indigo.triggers[123] trigger.replacePluginPropsOnServer({"prop1":10,"prop2":True}) ``` Note, though, that if you have a `` defined for the event, those properties are also stored here - so in order to make sure your trigger works correctly you must include those properties as well. If you need to update several properties in your props dict, you can use the `update()` method: ```python trigger=indigo.triggers[123] localPropsCopy = trigger.pluginProps localPropsCopy.update({"prop1":10,"prop2":True}) trigger.replacePluginPropsOnServer(localPropsCopy) ``` The `update()` method will change the properties specified, and add the property if it doesn't exist. Now, you might be wondering - why do the extra `localPropsCopy = trigger.pluginProps` rather than just modify the props in place: ```python trigger=indigo.triggers[123] trigger.pluginProps.update({"prop1":10,"prop2":True}) trigger.replacePluginPropsOnServer(trigger.pluginProps) ``` Because the trigger object is read-only - when you reference `trigger.pluginProps`, it returns a copy rather than returning a reference to the read-only object. So, in effect, you'd be modifying a copy. But, because you aren't saving a reference to that copy, it goes away since the next time you reference `trigger.pluginProps` another copy is made. If you need to just dump all the properties for a trigger, you can just: ```python trigger=indigo.triggers[123] trigger.replacePluginPropsOnServer(None) ``` That will completely remove your properties from the trigger. ### Commands (indigo.trigger.*) { .ref-head-no-code } The commands in this section are common to all triggers regardless of type. #### Delete { .ref-head-no-code } Delete the specified trigger regardless of its type. **Command Syntax Examples** `indigo.trigger.delete(123)` **Parameters** | Parameter | Required | Type | Description | |------------------|----------|---------|-----------------------------------------| | direct parameter | Yes | integer | id or instance of the trigger to delete | #### Duplicate { .ref-head-no-code } Duplicate the specified trigger regardless of the type. This method returns a copy of the new trigger. **Command Syntax Examples** `indigo.trigger.duplicate(123, duplicateName="New Name")` **Parameters** | Parameter | Required | Type | Description | |--------------------------------------------|----------|---------|--------------------------------------------| | direct parameter | Yes | integer | id or instance of the trigger to duplicate | | `duplicateName` | No | string | name for the newly duplicated trigger | #### Enable/Disable Trigger { #enable .ref-head-no-code } Disables or enables the trigger, optionally delaying for some period of time and optionally toggling back after the given period. **Command Syntax Examples** `indigo.trigger.enable(123,value=False)`
`indigo.trigger.enable(123,value=True)`
`indigo.trigger.enable(123, value=True, duration=360, delay=60)`
**Parameters** | Parameter | Required | Type | Description | |---------------------------------------|----------|---------|-----------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the trigger | | `value` | Yes | boolean | True to enable, False to disable | | `delay` | No | integer | number of seconds to delay before disabling or enabling the trigger | | `duration` | No | integer | number of seconds before the trigger is switched back to its original state | #### Execute Trigger { #execute .ref-head-no-code } Tell the IndigoServer to execute the actions associated with the trigger. If your plugin implements events, this is the method you'd call when the conditions for your specific event are met. If you're calling it this way, make sure you include the `ignoreConditions=False` parameter (or don't include the parameter since False is the default) so that any conditions associated with the trigger (by the user in the UI) are evaluated. This method can also be called by scripters to immediately execute the actions associated with the trigger. **Command Syntax Examples** `indigo.trigger.execute(123, ignoreConditions=False)` **Parameters** | Parameter | Required | Type | Description | |-----------------------------------------------|----------|---------|--------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the trigger | | `ignoreConditions` | No | boolean | will ignore any conditions associated with the trigger if True and will evaluate conditions if False (the default) | | `trigger_data` | No | object | an `indigo.Dict` to be passed to the trigger before it is executed | A note on `trigger_data` - Indigo will automatically add a `source` key to your dictionary to represent where the action execution came from: - "server" if it's something generated from the server itself (schedule execution, built-in trigger, etc.) - "python" if it's something that comes through IPH that doesn't already have a source attached (scripts, plugins) - "api-http" if it came from the HTTP API and there wasn't already an included "source" - "api-websocket" if it came from the websocket API and there wasn't already an included "source" However, if you include a `source` key in your `trigger_data`, we will not overwrite it, we'll just pass through whatever your value is. For example, if you ```python my_dict = indigo.Dict() my_dict["foo"] = "bar" indigo.trigger.execute(8974982742, trigger_data=my_dict) ``` The schedule you executed will receive something like this: ```json {"event-indigo-id": 8974982742, "event-type": "Trigger", "foo": "bar", "source": "python", "timestamp": "1970-01-01T09:09:40"} ``` #### Get Dependencies { .ref-head-no-code } Return an indigo.Dict with all the dependencies on this trigger. **Command Syntax Examples** `indigo.trigger.getDependencies(123)` **Parameters** | Parameter | Required | Type | Description | |------------------|----------|---------|------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the trigger to get the dependencies for. | The dictionary will look something like this: ```python >>> print(indigo.trigger.getDependencies(91776575)) Data : (dict) actionGroups : (list) controlPages : (list) devices : (list) schedules : (list) Data : (dict) ID : 552463741 (integer) Name : Between condition test (string) Data : (dict) ID : 296710860 (integer) Name : Greater than condition test (string) triggers : (list) variables : (list) ``` So, the dictionary will have 6 top-level keys: "actionGroups", "controlPages", "devices", "schedules", "triggers", and "variables". Each one of those keys will return a list object. Inside that list object will be multiple dicts, one for each dependency (or an empty list if there are none). Each dependency dictionary has two keys: "ID" which is the unique id and "Name" which is the name of the object. #### Move To Folder { .ref-head-no-code } Use this command to move the trigger to a different folder. You can get a list of folder id’s by using indigo.triggers.folders, which will return a dictionary. The key to the dictionary is the ID, the value is the folder name. **Command Syntax Examples** `indigo.trigger.moveToFolder(123, value=987)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------|----------|---------|-----------------------------------------------------| | direct parameter | Yes | integer | id or instance of the trigger | | `value` | Yes | integer | id or instance of the folder to move the trigger to | #### Remove Delayed Actions { .ref-head-no-code } This command will remove delayed actions for the specified trigger. **Command Syntax Examples** `indigo.trigger.removeDelayedActions(123)` **Parameters** | Parameter | Required | Type | Description | |------------------|----------|---------|-------------------------------| | direct parameter | No | integer | id or instance of the trigger | ### Examples { .ref-head-no-code } While several of a trigger's properties are read-only (the trigger ID for example), other properties can be changed programmatically. These are noted as "writeable" above. For example, ```python # Change a trigger's name: trigger = indigo.triggers[123] trigger.name = "My new name." trigger.replaceOnServer() # Change a trigger's description: trigger = indigo.triggers[123] trigger.description = "My new description." trigger.replaceOnServer() ``` ## DeviceStateChangeTrigger { .ref-head-no-code } The DeviceStateChangeTrigger class represents an event that is described by various changes to a device’s state. Built-in device types have fixed states, but plugins may define custom devices that define their own device states. Note: Controller device types can’t be used in device state change events since they have no state. ### Class Properties { .ref-head-no-code } | Property | Type | Writable | Description | |-------------------------------------------------|------------------------------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------| | `deviceId` | integer | Yes | the unique device id | | `stateChangeType` | [kStateChange](#state-change-type-enumeration) | Yes | the type of state change | | `stateSelector` | [kStateSelector](#state-selector-enumeration) | Yes | can use either a string or one of the enumerations listed in the state selector enumeration | | `stateSelectorIndex` | integer | Yes | a 0-based integer value to specify which of multiple options to monitor - see the About State Selector section below for more information | | `stateValue` | string | Yes | the value the current state should be compared against - will be converted to the right type by the server | These events are rather complex for a variety of reasons, but the primary is that device states can be of multiple types which require a different set of controls. Indigo solves the problem by showing the right control options given the type. If your plugin is defining its own states, you’ll have to specify each state and it’s associated type so that Indigo will know what kind of controls to display to the user. For instance, a temperature reported from a thermostat may be a floating point number. Possible events that you could trigger off of would be greater than, less than, equal, not equal, or has any change. However, becomes true or becomes false doesn’t make any sense. Conversely, an I/O device with binary inputs would really only need becomes true, becomes false, and has any change. The other options don’t make sense. To help you define the appropriate state changes, we’ve created the State Change Enumeration that lists all possible state changes. However, that’s only one side of the issue. The other important concept is the state selector. In the example above, I mentioned a thermostat temperature and a binary input. I implied a single value for each, but in reality thermostats may in fact have multiple temperature sensors and I/O devices usually have multiple inputs and outputs. So, how do we specify which one? #### State Change Type Enumeration { #state-change-type-enumeration .ref-head-no-code } | indigo.kStateChange | | | |-------------------------------------------------|---------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Value | Description | Valid for kStateSelector’s | | `BecomesEqual` | `stateSelector` becomes equal to `stateValue` | `ActiveZone`
`AnalogInput`
`BrightnessLevel`
`HumidityInput`
`SensorInput`
`SetpointCool`
`SetpointHeat`
`TemperatureInput` | | `BecomesFalse` | `stateSelector` becomes `False` | `BinaryInput`
`BinaryOutput`
`HvacCoolerIsOn`
`HvacFanIsOn`
`HvacFanModeIsAlwaysOn`
`HvacFanModeIsAuto`
`HvacHeaterIsOn`
`HvacOperationModeIsAuto`
`HvacOperationModeIsCool`
`HvacOperationModeIsHeat`
`HvacOperationModeIsOff`
`HvacOperationModeIsProgramAuto`
`HvacOperationModeIsProgramCool`
`HvacOperationModeIsProgramHeat`
`OnOffState`
`Zone` | | `BecomesGreaterThan` | `stateSelector` becomes greater than `stateValue` | `AnalogInput`
`BrightnessLevel`
`HumidityInput`
`SensorInput`
`SetpointCool`
`SetpointHeat`
`TemperatureInput` | | `BecomesLessThan` | `stateSelector` becomes less than `stateValue` | `AnalogInput`
`BrightnessLevel`
`HumidityInput`
`SensorInput`
`SetpointCool`
`SetpointHeat`
`TemperatureInput` | | `BecomesNotEqual` | `stateSelector` becomes not equal to `stateValue` | `ActiveZone`
`AnalogInput`
`BrightnessLevel`
`HumidityInput`
`SensorInput`
`SetpointCool`
`SetpointHeat`
`TemperatureInput` | | `BecomesTrue` | `stateSelector` becomes True | `BinaryInput`
`BinaryOutput`
`HvacCoolerIsOn`
`HvacFanIsOn`
`HvacFanModeIsAlwaysOn`
`HvacFanModeIsAuto`
`HvacHeaterIsOn`
`HvacOperationModeIsAuto`
`HvacOperationModeIsCool`
`HvacOperationModeIsHeat`
`HvacOperationModeIsOff`
`HvacOperationModeIsProgramAuto`
`HvacOperationModeIsProgramCool`
`HvacOperationModeIsProgramHeat`
`OnOffState`
`Zone` | | `Changes` | `stateSelector` has any change | `ActiveZone`
`AnalogInput`
`AnalogInputsAll`
`BinaryInput`
`BinaryInputsAll`
`BinaryOutput`
`BinaryOutputsAll`
`BrightnessLevel`
`HumidityInput`
`HumidityInputsAll`
`HvacCoolerIsOn`
`HvacFanIsOn`
`HvacFanMode`
`HvacFanModeIsAlwaysOn`
`HvacFanModeIsAuto`
`HvacHeaterIsOn`
`HvacOperationModeIsAuto`
`HvacOperationModeIsCool`
`HvacOperationModeIsHeat`
`HvacOperationModeIsOff`
`HvacOperationModeIsProgramAuto`
`HvacOperationModeIsProgramCool`
`HvacOperationModeIsProgramHeat`
`OnOffState`
`SensorInput`
`SensorInputsAll`
`SetpointCool`
`SetpointHeat`
`TemperatureInput`
`TemperatureInputsAll`
`Zone` | #### State Selector Enumeration { #state-selector-enumeration .ref-head-no-code } | indigo.kStateSelector | | |-------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Value | Description | | `ActiveZone` | monitor the sprinkler’s activeZone to become =, !=, or any change | | `AnalogInput` | monitor (one of) the analog input(s) available on the device for =, !=, <, >, or any change - stateSelectorIndex is required to be in the range of available inputs | | `AnalogInputsAll` | monitors all of the analog inputs available on the device for any change | | `BinaryInput` | monitor (one of) the binary input(s) to become true, false, or any change - stateSelectorIndex is required to be in the range of available inputs | | `BinaryInputsAll` | monitors all of the binary inputs available on the device for any change | | `BinaryOutput` | monitor (one of) the binary output(s) to become true, false, or any change - stateSelectorIndex is required to be in the range of available outputs | | `BinaryOutputsAll` | | `BrightnessLevel` | monitor the brightness level of a device for =, !=, <, >, and any change | | `HumidityInput` | monitor (one of) the humidity sensor(s) available on the device for =, !=, <, >, and any change - stateSelectorIndex is required to be in the range of available inputs | | `HumidityInputsAll` | monitors all of the humidity sensors available on the device for any change | | `HvacCoolerIsOn` | monitor the thermostat for any time the air conditioning turns on, off, or has any change (coolIsOn is the current compressor state) | | `HvacFanIsOn` | monitor the thermostat for any time the fan turns on, off, or has any change (fanIsOn is the current fan state) | | `HvacFanMode` | monitor the fanMode of the thermostat for any change | | `HvacFanModeIsAlwaysOn` | monitor the fanMode of the thermostat for a change to/from kFanMode.AlwaysOn | | `HvacFanModeIsAuto` | monitor the fanMode of the thermostat for a change to/from kFanMode.AutoOn | | `HvacHeaterIsOn` | monitor the thermostat for any time the heater turns on, off, or has any change (heatIsOn is the current heater state) | | `HvacOperationMode` | monitor the hvacMode of the thermostat for any change | | `HvacOperationModeIsAuto` | monitor the hvacMode of the thermostat for a change to/from kHvacMode.HeatCoolOn | | `HvacOperationModeIsCool` | monitor the hvacMode of the thermostat for a change to/from kHvacMode.CoolOn | | `HvacOperationModeIsHeat` | monitor the hvacMode of the thermostat for a change to/from kHvacMode.HeatOn | | `HvacOperationModeIsOff` | monitor the hvacMode of the thermostat for a change to/from kHvacMode.Off | | `HvacOperationModeIsProgramAuto` | monitor the hvacMode of the thermostat for a change to/from kHvacMode.ProgramAuto | | `HvacOperationModeIsProgramCool` | monitor the hvacMode of the thermostat for a change to/from kHvacMode.ProgramCool | | `HvacOperationModeIsProgramHeat` | monitor the hvacMode of the thermostat for a change to/from kHvacMode.ProgramHeat | | `KeypadButtonLed` | | | `OnOffState` | monitor the device for a change to/from on/off | | `SensorInput` | monitor (one of) the sensor input(s) available on the device for =, !=, <, >, or any change - stateSelectorIndex is required to be in the range of available inputs | | `SensorInputsAll` | monitors all of the sensor inputs available on the device for any change | | `SetpointCool` | monitor the cool setpoint of the thermostat for =, !=, <, >, or any change | | `SetpointHeat` | monitor the heat setpoint of the thermostat for =, !=, <, >, or any change | | `TemperatureInput` | monitor (one of) the temperature sensor(s) available on the device for =, !=, <, >, and any change - stateSelectorIndex is required to be in the range of available inputs | | `TemperatureInputsAll` | monitors all of the temperature sensors available on the device for any change | | `Zone` | monitor (one of) the binary output(s) to become true, false, or any change - stateSelectorIndex is required to be in the range of available outputs | ### Commands (indigo.devStateChange.*) { .ref-head-no-code } #### Create { .ref-head-no-code } Create a DeviceStateChange trigger. This method returns a **copy** of the newly created trigger. **Command Syntax Examples** `indigo.devStateChange.create(name="Trigger Name Here", description="Description Here", folder=1234)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------------------------------------------------| | `description` | No | string | the description of the trigger | | `name` | Yes | string | the name of the trigger | | `folder` | No | integer | id or instance of the folder in which to put the newly created trigger | ### Examples { #devicestatechangetrigger-examples .ref-head-no-code } So, let’s look at a concrete example - a thermostat. A thermostat has several states which can be monitored, a couple of which may be lists. Specifically, a thermostat may have multiple temperature sensors and multiple humidity sensors. Here is an example of creating a device state changed trigger that will execute when the first temperature of thermostat id 738 (named "Main Thermostat") goes over 80 degrees in Python: ```python theTrigger=indigo.devStateChange.create(name="Temp exceeds 80 degrees") theTrigger.deviceId = 738 theTrigger.stateChangeType = indigo.kStateChange.BecomesGreaterThan theTrigger.stateSelector = indigo.kStateSelector.TemperatureInput theTrigger.stateSelectorIndex = 1 theTrigger.stateValue = 80 theTrigger.replaceOnServer() ``` ## EmailReceivedTrigger { .ref-head-no-code } The EmailReceivedTrigger object represents the email scanning feature in Indigo. You can match on any email received or based on the match fields - subject and/or from email address. ### Class Properties { #emailreceivedtrigger-class-properties .ref-head-no-code } | Property | Writable | Type | Description | |-------------------------------------------|-------------------------------------------|------|-----------------------------------------------------------------------------------------| | `emailFilter` | [kEmailFilter](#email-filter-enumeration) | Yes | the type of email filter based on the `kEmailFilter` enumeration below | | `emailFrom` | string | Yes | if `emailFilter` is `MatchEmailFields`, the value to match against the sender’s address | | `emailSubject` | string | Yes | if `emailFilter` is `MatchEmailFields`, the value to match against the subject line | #### Email Filter Enumeration { #email-filter-enumeration .ref-head-no-code } | indigo.kEmailFilter | | |-----------------------------------------------|--------------------------------------| | Value | Description | | `AnyEmail` | when any email is received | | `MatchEmailFields` | when email subject/from fields match | ### Commands (indigo.emailRcvd.*) { .ref-head-no-code } #### Create { #commands-indigoemailrcvd-create .ref-head-no-code } Create a EmailReceivedTrigger. This method returns a **copy** of the newly created trigger. **Command Syntax Examples** `indigo.emailRcvd.create(name="Trigger Name Here", description="Description Here", folder=1234)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------------------------------------------------| | `description` | No | string | the description of the trigger | | `name` | Yes | string | the name of the trigger | | `folder` | No | integer | id or instance of the folder in which to put the newly created trigger | **Examples** ```python theTrigger=indigo.emailRcvd.create(name="Emails from some@body.com") theTrigger.emailFilter = indigo.kEmailFilter.MatchEmailFields theTrigger.emailFrom = "some@body.com" theTrigger.replaceOnServer() ``` ## InsteonCommandReceivedTrigger { .ref-head-no-code } The InsteonCommandReceivedTrigger object will match incoming Insteon command events. ### Class Properties { #insteoncommandreceivedtrigger-class-properties .ref-head-no-code } | Property | Type | Writable | Description | |------------------------------------------------|--------------------------------------------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `deviceId` | integer | Yes | the unique device id - only used if `commandSourceType` is `DeviceId` | | `command` | [kInsteonCmd](#insteon-command-enumeration) | Yes | the command to watch for | | `commandSourceType` | [kDeviceSourceType](#trigger-device-source-type-enumeration) | Yes | the source type - for Insteon only `DeviceId` and `AnyDevice` apply - and `deviceId` above will only be used if set to `DeviceId` | | `buttonOrGroup` | integer | Yes | the button or group number from which the command is received - see [About Button or Group Numbers](#about-button-or-group-numbers-in-insteon-events) below for details | #### About Button or Group Numbers in Insteon Events { #about-button-or-group-numbers-in-insteon-events .ref-head-no-code } Various Insteon devices support features that are implemented via extra group identifiers. For instance, a KeypadLinc will broadcast each of the commands below from each button on it, and each KeypadLinc may have either 6 or 8 buttons depending on configuration. Other devices, like the Motion Sensor and the TriggerLinc will broadcast out group numbers based on other events (battery low, motion detected, dusk/dawn sensor, etc.). #### Insteon Command Enumeration { #insteon-command-enumeration .ref-head-no-code } | indigo.kInsteonCmd | | |--------------------------------------------|--------------------------------------------------------------------| | Value | Description | | `AllBrighten` | when an all brighten command begins | | `AllDim` | when an all dim command begins | | `AllInstantOff` | when an all instant (fast) off is received | | `AllInstantOn` | when an all instant (fast) on is received | | `AllOff` | when an all off is received | | `AllOn` | when an all on is received | | `AnyCommand` | when any command is received | | `Brighten` | when a brighten command begins | | `Dim` | when a dim command begins | | `InstantOff` | when an Instant (Fast) off is received in response to a double-tap | | `InstantOn` | when an Instant (Fast) on is received in response to a double-tap | | `Off` | when an off command is received | | `On` | when an on command is received | | `StatusChanged` | when a status change broadcast is received | #### Trigger Device Source Type Enumeration { #trigger-device-source-type-enumeration .ref-head-no-code } | indigo.kDeviceSourceType | | |-----------------------------------------|------------------------------------------------------------------------------| | Value | Description | | `NoDevice` | when the source uses no device or address (only used for X10 RF A/V remotes) | | `Device` | when the source is an existing device, the ID is specified | | `Address` | when the source is a raw X10 address | | `AnyAddress` | when the source is any X10 address or Insteon device | ### Commands (indigo.insteonCmdRcvd.*) { .ref-head-no-code } #### Create { #commands-indigoinsteoncmdrcvd-create .ref-head-no-code } Create a InsteonCommandReceivedTrigger. This method returns a **copy** of the newly created trigger. **Command Syntax Examples** ```python indigo.insteonCmdRcvd.create(name="Trigger Name Here", description="Description Here", folder=1234) ``` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------------------------------------------------| | `description` | No | string | the description of the trigger | | `name` | Yes | string | the name of the trigger | | `folder` | No | integer | id or instance of the folder in which to put the newly created trigger | **Examples** ```python theTrigger= indigo.insteonCmdRcvd.create(name="KeypadLinc button 1 Any Change") theTrigger.command = indigo.kInsteonCmd.AnyCommand theTrigger.commandSourceType = indigo.kDeviceSourceType.Device theTrigger.deviceId = 43829874 theTrigger.buttonOrGroup = 1 theTrigger.replaceOnServer() ``` ## InterfaceFailureTrigger { .ref-head-no-code } The InterfaceFailureTrigger object represents the trigger that’s executed when an interface fails for some reason. ### Class Properties { #interfacefailuretrigger-class-properties .ref-head-no-code } The InterfaceFailed trigger has no additional properties. ### Commands (indigo.interfaceFail.*) { .ref-head-no-code } #### Create { #commands-indigointerfacefail-create .ref-head-no-code } Create a InterfaceFailureTrigger. This method returns a **copy** of the newly created trigger. **Command Syntax Examples** `indigo.interfaceFail.create(name="Trigger Name Here", description="Description Here", folder=1234)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------------------------------------------------| | `description` | No | string | the description of the trigger | | `name` | Yes | string | the name of the trigger | | `folder` | No | integer | id or instance of the folder in which to put the newly created trigger | **Examples** ```python theTrigger= indigo.interfaceFail.create(name="Any Interface Failed") ``` ## InterfaceInitializedTrigger { .ref-head-no-code } The InterfaceInitializedTrigger object represents the trigger that’s executed when an interface initializes successfully. ### Class Properties { #interfaceinitializedtrigger-class-properties .ref-head-no-code } InterfaceInitializedTrigger has no additional properties. ### Commands (indigo.interfaceInit.*) { .ref-head-no-code } #### Create { #commands-indigointerfaceinit-create .ref-head-no-code } Create a InterfaceInitializedTrigger. This method returns a **copy** of the newly created trigger. **Command Syntax Examples** `indigo.interfaceInit.create(name="Trigger Name Here", description="Description Here", folder=1234)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------------------------------------------------| | `description` | No | string | the description of the trigger | | `name` | Yes | string | the name of the trigger | | `folder` | No | integer | id or instance of the folder in which to put the newly created trigger | **Examples** ```python theTrigger= indigo.interfaceInit.create(name="Any Interface Initialized") ``` ## PluginEventTrigger { .ref-head-no-code } A plugin event is defined by a plugin. ### Class Properties { #plugineventtrigger-class-properties .ref-head-no-code } | Property | Type | Description | |-------------------------------------------|--------|-------------------------------------------------------------------------------------------------| | `pluginId` | string | the unique ID of the plugin, specified in the Info.plist for the plugin (or it’s documentation) | | `pluginTypeId` | string | the id specified in the Events.xml (or it’s documentation) | ### Commands (indigo.pluginEvent.*) { .ref-head-no-code } #### Create { #commands-indigopluginevent-create .ref-head-no-code } Create a trigger. You can create triggers using this method of any type except for events that are defined by your plugin (that class, PluginEventTrigger, has its own create() method). This method returns a **copy** of the newly created trigger. Once a trigger of this type is created, the properties can change (via the [pluginProps in the Trigger base class](#properties)), but nothing else can be changed. **Command Syntax Examples** `indigo.pluginEvent.create(name="Trigger Name Here", description="Description Here", folder=1234), pluginId="com.mycompany.myplugin", pluginTypeId="myEvent", props={"propA":"value","propB":"value"}` **Parameters** | Parameter | Required | Type | Description | |-------------------------------------------|----------|------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `description` | No | string | the description of the trigger | | `name` | Yes | string | the name of the trigger | | `folder` | No | integer | id or instance of the folder in which to put the newly created trigger | | `pluginId` | No | string | the plugin id of the plugin that owns the device you're creating - if it's not present, it defaults to your plugin's id | | `pluginTypeId` | Yes | string | this is the id of the as specified in the Events.xml or in the documentation for the plugin | | `props` | No | dictionary | this is the properties for the trigger - they will be inserted in to the pluginId's property space as supplied above. If you are creating a trigger of a type defined in a different plugin, it's that plugin's id and properties. | **Examples** See the Command Syntax above for an example. See [Firing Plugin Defined Triggers](#firing-plugin-defined-triggers) above for details on how your plugin should watch and fire triggers based on events defined in your Events.xml. ## PowerFailureTrigger { .ref-head-no-code } The PowerFailureTrigger object represents a trigger that’s executed when an interface loses power (if applicable). Note - not all interfaces can detect a power failure - usually only those interfaces that are plugged directly into an electrical outlet. ### Class Properties { #powerfailuretrigger-class-properties .ref-head-no-code } PowerFailureTrigger has no additional properties. ### Commands (indigo.powerFailure.*) { .ref-head-no-code } #### Create { #commands-indigopowerfailure-create .ref-head-no-code } Create a `PowerFailureTrigger`. This method returns a **copy** of the newly created trigger. **Command Syntax Examples** `indigo.powerFailure.create(name="Trigger Name Here", description="Description Here", folder=1234)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------------------------------------------------| | `description` | No | string | the description of the trigger | | `name` | Yes | string | the name of the trigger | | `folder` | No | integer | id or instance of the folder in which to put the newly created trigger | **Examples** ```python theTrigger= indigo.powerFailure.create(name="Any Interface Detected Power Failure") ``` ## ServerStartupTrigger { .ref-head-no-code } The ServerStartupTrigger class represents a trigger that’s executed when the IndigoServer process starts up. This is a special event in that it adds no additional parameters beyond what it inherits from [Trigger](#trigger). ### Commands (indigo.serverStartup.*) { .ref-head-no-code } #### Create { #commands-indigoserverstartup-create .ref-head-no-code } Create a ServerStartupTrigger trigger. This method returns a **copy** of the newly created trigger. **Command Syntax Examples** `indigo.serverStartup.create(name="Trigger Name Here", description="Description Here", folder=1234)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------------------------------------------------| | `description` | No | string | the description of the trigger | | `name` | Yes | string | the name of the trigger | | `folder` | No | integer | id or instance of the folder in which to put the newly created trigger | **Examples** ```python theTrigger= indigo.serverStartup.create(name="Startup") ``` ## X10CommandReceivedTrigger { .ref-head-no-code } The X10CommandReceivedTrigger object will match incoming X10 command events. ### Class Properties { #x10commandreceivedtrigger-class-properties .ref-head-no-code } | Property | Type | Description | |------------------------------------------------|--------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------| | `address` | string | if commandSourceType = Address, the full X10 address to listen for or just the house code if command is All* or AnyCommand | | `avButton` | [kX10AvButton](#x10-a-v-button-enumeration) | if command = AvButtonPressed, the A/V button to monitor | | `deviceId` | integer | if commandSourceType = Device, the unique device id | | `command` | [kX10Cmd](#x10-command-enumeration) | the X10 command to watch for | | `commandSourceType` | [kDeviceSourceType](#trigger-device-source-type-enumeration) | the type of source specified for this trigger | #### X10 Command Enumeration { #x10-command-enumeration .ref-head-no-code } | indigo.kX10Cmd | | |------------------------------------------------|-----------------------------------------------| | Value | Description | | `AllOff` | when an all off is received | | `AllLightsOff` | when an all lights off is received | | `AllLightsOn` | when an all lights on is received | | `AvButtonPressed` | when an A/V button press is received | | `AnyCommand` | when any command is received | | `Brighten` | when a brighten command begins | | `Dim` | when a dim command begins | | `ExtendedData` | when an extended data X10 command is received | | `Off` | when an off command is received | | `On` | when an on command is received | | `PresetDim` | when a preset dim command is received | | `StatusOffResponse` | when a status off response is received | | `StatusOnResponse` | when a status on response is received | #### X10 A/V Button Enumeration { #x10-a-v-button-enumeration .ref-head-no-code } | indigo.kX10AvButton | | |------------------------------------------|--------------| | Value | Value | | `0` | `Left` | | `1` | `Menu` | | `2` | `Mute` | | `3` | `Pause` | | `4` | `PC` | | `5` | `Play` | | `6` | `Power` | | `7` | `Recall` | | `8` | `Record` | | `9` | `Return` | | `AB` | `Rewind` | | `ChannelDown` | `Right` | | `ChannelUp` | `Stop` | | `Display` | `Title` | | `Down` | `Up` | | `Enter` | `VolumeDown` | | `Exit` | `VolumeUp` | | `Forward` | ### Commands (indigo.x10CmdRcvd.*) { .ref-head-no-code } #### Create { #commands-indigox10cmdrcvd-create .ref-head-no-code } Create an X10CommandReceivedTrigger. This method returns a **copy** of the newly created trigger. **Command Syntax Examples** `indigo.x10CmdRcvd.create(name="Trigger Name Here", description="Description Here", folder=1234)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------------------------------------------------| | `description` | No | string | the description of the trigger | | `name` | Yes | string | the name of the trigger | | `folder` | No | integer | id or instance of the folder in which to put the newly created trigger | **Examples** ```python myTrigger=indigo.x10CmdRcvd.create(name="Received any command from F7") myTrigger.command = indigo.kX10Cmd.AnyCommand myTrigger.commandSourceType = indigo.kDeviceSourceType.Address myTrigger.address = "F7" myTrigger.replaceOnServer() ``` ## VariableValueChangeTrigger { .ref-head-no-code } The VariableValueChangeTrigger object will match variable changes. ### Class Properties { #variablevaluechangetrigger-class-properties .ref-head-no-code } | Property | Type | Description | |-------------------------------------------------|-------------------------------------------------|------------------------------------------------------------------------------------------| | `variableChangeType` | [kVarChange](#variable-change-type-enumeration) | the type of variable change to monitor | | `variableId` | integer | the unique variable id | | `variableValue` | string | the value to compare against the variable’s value if `variableChangeType` is =, !=, >, < | #### Variable Change Type Enumeration { #variable-change-type-enumeration .ref-head-no-code } | indigo.kVarChange | | |-------------------------------------------------|-------------------------------------| | Value | Description | | `BecomesEqual` | variable value becomes equal to | | `BecomesFalse` | variable value becomes false | | `BecomesGreaterThan` | variable value becomes greater than | | `BecomesLessThan` | variable value becomes less than | | `BecomesNotEqual` | variable value becomes not equal to | | `BecomesTrue` | variable value becomes true | | `Changes` | variable value has any change | ### Commands (indigo.varValueChange.*) { .ref-head-no-code } #### Create { #commands-indigovarvaluechange-create .ref-head-no-code } Create an VariableChangeEvent. This method returns a **copy** of the newly created trigger. **Command Syntax Examples** `indigo.varValueChange.create(name="Trigger Name Here", description="Description Here", folder=1234)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------------------------------------------------| | `description` | No | string | the description of the trigger | | `name` | Yes | string | the name of the trigger | | `folder` | No | integer | id or instance of the folder in which to put the newly created trigger | **Examples** Commands (indigo.devStateChange.*) ```python theTrigger=indigo.varValueChange.create(name="Var Changed to True") theTrigger.variableChangeType = indigo.kVarChange.BecomesEqual theTrigger.variableId = 9283749872 theTrigger.variableValue = "True" theTrigger.replaceOnServer() ``` --- Utility Classes & Functions (https://docs.indigodomo.com/2025.2/scripting/reference/utils/) --- # Utility Classes & Functions The `indigo.utils` module collects several things that aren't directly tied to a specific Indigo object but are helpful when writing scripts and building plugins. Everything below is reached through the `indigo.utils.` prefix from any Indigo Python script or plugin (e.g. `indigo.utils.ValidationError`). ## Classes { #classes } | Name | Base | Description | | --- | --- | --- | | `IndigoJSONEncoder` | `json.JSONEncoder` | A JSON encoder that converts Python `date`/`datetime` objects (and `NaN`) so they can be serialized. Very useful when encoding a device dictionary into JSON: `json.dumps(dict(my_device), cls=indigo.utils.IndigoJSONEncoder)`. The original name `JSONDateEncoder` is still available as an alias. | | `ValidationError` | `Exception` | An exception for reporting validation problems. It can carry a single summary message or a whole dictionary of field-specific errors. See [ValidationError](#validationerror) below. | ### IndigoJSONEncoder { #indigojsonencoder } By default the Python `json` module can't serialize `datetime` objects. Pass this encoder as the `cls` argument to a JSON dump call and any `date`/`datetime` encountered during encoding is converted to its ISO string. Indigo constants (e.g. `indigo.kFanMode.Auto`) are encoded as their full string representation so they can be reconstituted later. ```python import json my_device = indigo.devices[123456] print(json.dumps(dict(my_device), indent=4, cls=indigo.utils.IndigoJSONEncoder)) ``` ### ValidationError { #validationerror } `ValidationError` is primarily used when validating fields for commands, messages, or [Config UIs](../../plugin-dev/reference/xml/configui/validation.md), but it can carry any kind of validation result. The simplest use is to raise it with a string and let the caller `str()` it. For more involved cases it holds a dictionary of field names (or keys), each mapped to a single error string or a list of error strings, so the caller can process individual errors — for example to mark the offending fields when validating a Config UI. **Constructor** `indigo.utils.ValidationError(message, error_state_str=None, error_dict=None)` | Parameter | Required | Type | Description | | --- | --- | --- | --- | | `message` | Yes | string | A general message summarizing the entire validation. Useful for logging. | | `error_state_str` | No | string | If the validation represents a device state issue, this string is used as the device's error state in the various UIs. Pass an empty string to clear any existing error. | | `error_dict` | No | dict | A dictionary of field names mapped to error message(s) — each value may be a single string or a list of strings. | **Attributes** | Attribute | Type | Description | | --- | --- | --- | | `error_message` | string | The general summary message passed to the constructor. | | `error_state_str` | string or None | The optional device error-state string. | | `error_dict` | dict | The dictionary of field/key → error message(s). | **Methods** | Method | Description | | --- | --- | | `add_error(key, description)` | Add an error for `key` (e.g. a field name). `description` can be a single string or a list of strings. If the key already has an error, the new message is appended (the value becomes a list). | | `remove_error(key)` | Remove and return all error message(s) for `key`. Note that this removes the entire key, including any multiple messages. | | `raise_if_errors()` | Raise this exception if there is anything to report — i.e. if `error_dict` is non-empty or `error_state_str` is not `None`. Otherwise does nothing. | A `ValidationError` is also iterable: calling `dict(my_validation_error)` yields the contents of `error_dict`. And `str(my_validation_error)` produces a human-readable summary that includes the general message followed by the formatted error details. **Example** ```python # Accumulate field errors, then raise only if something failed. errors = indigo.utils.ValidationError("Sensor configuration is invalid") if not values.get("address"): errors.add_error("address", "You must enter an address.") if not indigo.utils.is_int(values.get("pollInterval", "")): errors.add_error("pollInterval", "Poll interval must be a whole number.") errors.raise_if_errors() # raises only if at least one error was added ``` For a worked example of turning a `ValidationError` into the error dictionary a Config UI validation method returns, see [Validation Methods](../../plugin-dev/reference/xml/configui/validation.md#using-validationerror). ## Functions ### Return Static File { #return-static-file } Accepts a file path and an optional content type and returns the correctly structured `indigo.Dict` that the Indigo Web Server (IWS) interprets as a directive to stream the specified file back to the caller. This avoids returning a large amount of data through the plugin IPC mechanism. **Command Syntax Examples** ```python indigo.utils.return_static_file("some/relative/path/to/file.txt") indigo.utils.return_static_file("/some/path/to/file.json", status=400, path_is_relative=False, content_type="application/json") ``` **Parameters** | Parameter | Required | Type | Description | | --- | --- | --- | --- | | `file_path` | Yes | string or list | A string path to the file, or a list of path parts ending in the file name. | | `status` | No | int | The HTTP status code to return. Defaults to `200`. | | `path_is_relative` | No | boolean | `True` if the path is relative to the Indigo install folder, `False` for a complete file path. Defaults to `True`. | | `content_type` | No | string | The MIME type for the `Content-Type` header. If omitted, an appropriate type is chosen from the file extension. | The returned `indigo.Dict` can be passed directly back to IWS from an HTTP processing call in your plugin. It looks something like this: ```json { "status": 404, "headers": { "Content-Type": "text/html" }, "file_path": "/Library/Application Support/Perceptive Automation/Indigo {{ version }}/Plugins/Example HTTP Responder.indigoPlugin/Contents/Resources/static/html/static_404.html" } ``` IWS uses this to create the HTTP reply that streams the file back to the caller. The function raises a `FileNotFoundError` if the file doesn't exist, or a `TypeError` if `file_path` isn't a list of path parts or a string. ### Validate Email Address { #validate-email-address } Accepts an email address string and returns `True` if it is constructed correctly, `False` otherwise. Note: it only checks that the address is *formatted* correctly — it does not verify that the address exists on the destination system. **Command Syntax Examples** ```python indigo.utils.validate_email_address("valid_email@someserver.com") # True indigo.utils.validate_email_address("invalid address") # False ``` **Parameters** | Parameter | Required | Type | Description | | --- | --- | --- | --- | | `address` | Yes | string | A string that represents an email address. | ### Boolean Functions { #boolean-functions } Two functions help convert and use strings that represent boolean values but aren't literally `True`/`False`. Both use the following map (and its reverse): ```text BOOL_MAP_TRUE = { "y": "n", "yes": "no", "t": "f", "true": "false", "on": "off", "1": "0", "open": "closed", "locked": "unlocked", } ``` `str_to_bool(val)` converts the supplied string to a boolean. It returns `True` for true values (`y`, `yes`, `t`, `true`, `on`, `1`, `open`, `locked`), `False` for the corresponding false values, and raises a `ValueError` if the input can't be converted. A `bool` passed in is returned unchanged. ```python indigo.utils.str_to_bool("closed") # False indigo.utils.str_to_bool("on") # True ``` `reverse_bool_str_value(val)` returns the string representing the opposite boolean value using the map above. It raises a `ValueError` if the input can't be found. ```python indigo.utils.reverse_bool_str_value("closed") # "open" ``` | Parameter | Required | Type | Description | | --- | --- | --- | --- | | `val` | Yes | string | A string that represents a boolean value as mapped above. | ### Is Integer { #is-int } Accepts any value and returns `True` if it is an integer or can be cast to one, `False` otherwise. Handy for validating user-entered Config UI fields, which arrive as strings. ```python indigo.utils.is_int("42") # True indigo.utils.is_int("3.5") # False indigo.utils.is_int("abc") # False ``` | Parameter | Required | Type | Description | | --- | --- | --- | --- | | `value` | Yes | any | Any Python object to test. | ## Converting indigo.Dict and indigo.List { #conversion-methods } The `indigo.utils` module also attaches convenience methods to the `indigo.Dict` and `indigo.List` classes that recursively convert them to their native Python counterparts: ```python python_dict = my_indigo_dict.to_dict() # recursively convert an indigo.Dict to a python dict python_list = my_indigo_list.to_list() # recursively convert an indigo.List to a python list ``` These are the same conversions used when you call `dict()` on an Indigo object. For the full picture of how a device is represented as a dictionary, see [Dictionary Representation](devices/dictionary.md). --- Variables (https://docs.indigodomo.com/2025.2/scripting/reference/variables/) --- # Variables The variable class represents an Indigo variable. ## Class Properties { .ref-head-no-code } | Property | Type | Description | |--------------------------------------------|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `id` | integer | the unique id of the variable, assigned on creation by IndigoServer | | `folderId` | integer | the unique id of the folder this variable is in (0 if it's not in a folder) - use `moveToFolder()` method to change | | `name` | string | the name of the variable - no two variables can have the same name and the name cannot contain whitespace | | `readOnly` | string | is the variable read only - currently only the `isDaylight` variable is read only | | `remoteDisplay` | boolean | should this variable be displayed in remote clients (IWS, Indigo Touch, etc) | | `sharedProps` | dictionary | **[API v2.3](https://www.indigodomo.com/indigo/api_release_notes/2.3/)** : an `indigo.Dict()` representing the name/value pairs that are shared by all plugins. This is the property dictionary that you can edit via the Global Properties plugin, and your plugin may manage properties in this dictionary as well to add metadata to devices that your plugin can use for other purposes. Use `var.replaceSharedPropsOnServer()` to update them (as with pluginProps, you should get copy first, update the copy, then set them back to that copy so you don't accidentally remove some other plugin's props). | | `value` | string | the Unicode string value of the variable | ## Class Method { .ref-head-no-code } The Variable class has a special method, `getValue(TYPE, default=VALUE)`, that you can call which will retrieve the variable value as the specified Python class. There are a couple of advantages to using this method. First, it won't throw an exception but will always return a value. Second, the Indigo server will do the conversion in the exact same way that it does type conversions when using variables in triggers and conditions. You can also optionally specify a default value if the value can't be successfully converted into the specified type. Here's a list of the valid types you can specify and what the default is if not specified: | Type Literal | Type Returned | Default | |--------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | bool | boolean | `True` will be returned if the value is one of these: "true", "on", "yes", and "1". `False` will be returned if the value is one of these: "false", "off", "no", and "0". If no default value is specified and the value can't be successfully converted, the method will return False. | | int | integer | An integer object will be returned if the value is an integer number or a float/decimal. In the latter case, the number will be rounded to the nearest integer. If no default value is specified and the value can't be converted, the method will return 0 | | float | float | A float object will be returned if the value is a float/decimal or an integer. If no default value is specified and the value can't be converted, the method will return 0.0 | Remember that accessing the `value` property of a variable object (`var.value`) will return a Unicode string object so you don't need a conversion for that. **Class Method Examples** ```python # Get a variable var = indigo.variables[123456] # Getting the Unicode string value (no conversion call necessary, just access the property) unicodeValue = var.value # Getting the boolean value intValue = var.getValue(bool) # False if it can't be converted intValue = var.getValue(int, default=True) # True if it can't be converted # Getting the integer value intValue = var.getValue(int) # 0 if it can't be converted intValue = var.getValue(int, default=10) # 10 if it can't be converted # Getting the float value intValue = var.getValue(float) # 0 if it can't be converted intValue = var.getValue(float, default=98.6) # 98.6 if it can't be converted # Getting the name of the variable varName = var.name # Getting the id of the variable varId = var.id ``` ## Commands (indigo.variable.*) { .ref-head-no-code } ### Create { .ref-head-no-code } Create a variable. This method returns a **copy** of the newly created variable. **Command Syntax Examples** `indigo.variable.create("VariableName", value="Var Value", folder=843920)` **Parameters** | Parameter | Required | Type | Description | |-------------------------------------|----------|---------|---------------------------------------------------------------------------------------------------| | direct parameter | Yes | string | the name of the variable | | `value` | No | string | the value of the variable | | `folder` | No | integer | id or instance of the folder in which to put the newly created device - defaults to 0 (no folder) | ### Delete { .ref-head-no-code } Delete the specified variable. **Command Syntax Examples** `indigo.variable.delete(123)` **Parameters** | Parameter | Required | Type | Description | |------------------|----------|---------|------------------------------------------| | direct parameter | Yes | integer | id or instance of the variable to delete | ### Duplicate { .ref-head-no-code } Duplicate the specified variable. This method returns a copy of the new variable. **Command Syntax Examples** `indigo.variable.duplicate(123, duplicateName="NewName")` **Parameters** | Parameter | Required | Type | Description | |--------------------------------------------|----------|---------|---------------------------------------------| | direct parameter | Yes | integer | id or instance of the variable to duplicate | | `duplicateName` | No | string | name for the newly duplicated variable | ### Get Dependencies { .ref-head-no-code } Return an indigo.Dict with all the dependencies on this variable. **Command Syntax Examples** `indigo.variable.getDependencies(123)` **Parameters** | Parameter | Required | Type | Description | |------------------|----------|---------|-------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the variable to get the dependencies for. | The dictionary will look something like this: ```python >>> print(indigo.variable.getDependencies(91776575)) Data : (dict) actionGroups : (list) controlPages : (list) devices : (list) schedules : (list) Data : (dict) ID : 552463741 (integer) Name : Between condition test (string) Data : (dict) ID : 296710860 (integer) Name : Greater than condition test (string) triggers : (list) variables : (list) ``` So, the dictionary will have 6 top-level keys: "actionGroups", "controlPages", "devices", "schedules", "triggers", and "variables". Each one of those keys will return a list object. Inside that list object will be multiple dicts, one for each dependency (or an empty list if there are none). Each dependency dictionary has two keys: "ID" which is the unique id and "Name" which is the name of the object. ### Move To Folder { .ref-head-no-code } Use this command to move the variable to a different folder. **Command Syntax Examples** `indigo.variable.moveToFolder(123, value=987)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------|----------|---------|------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the variable | | `value` | Yes | integer | id or instance of the folder to move the variable to | ### Set Remote Display { .ref-head-no-code } Use this command to set the remote display flag for the folder. **Command Syntax Examples** `indigo.variable.displayInRemoteUI(123, value=True)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------|----------|---------|----------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the variable | | `value` | Yes | boolean | True to display the variable on remote user interfaces or False to hide it | ### Update Value { .ref-head-no-code } Use this command to set the value of a variable without having to get a local copy first. **Command Syntax Examples** `indigo.variable.updateValue(123, value="New Value")` **Parameters** | Parameter | Required | Type | Description | |------------------------------------|----------|---------|--------------------------------| | direct parameter | Yes | integer | id or instance of the variable | | `value` | Yes | string | the new value for the variable | **Examples** ```python # Create a new variable newVar = indigo.variable.create("fooName", "fooMonster") # Updating value via command space function: indigo.variable.updateValue(newVar, "asleep789") newVar.refreshFromServer() # refresh needed to update local's .value # changing name property newVar.name = "goodName" newVar.replaceOnServer() newVar.name = "bad name" # should throw because of space character # changing name and values properties: newVar.name = "goodName2" newVar.value = "searchingForWaldo" newVar.replaceOnServer() indigo.variable.delete(newVar) # Getting a variable using its ID someVar = indigo.variables[123] # Getting a variable using its name someVar = indigo.variables["MyVarName"] ``` --- X10 Commands (https://docs.indigodomo.com/2025.2/scripting/reference/x10-commands/) --- # X10 Commands (indigo.X10.*) Commands that are specific to X10 devices. ## Send Address { .ref-head-no-code } This command will send an X10 address to the interface with NO function code. **Command Syntax Examples** `indigo.x10.sendAddress("A1")` **Parameters** | Parameter | Required | Type | Description | |----------------------------------------------|----------|---------|------------------------------------------------------------------------------------------| | direct parameter | Yes | string | X10 address | | `suppressLogging` | No | boolean | a boolean indicating if entries in the event log should be suppressed (default is False) | ## Send Brighten { .ref-head-no-code } This will send the Brighten command to an X10 address. **Command Syntax Examples** `indigo.x10.sendBrighten("A1", delta=15)` **Parameters** | Parameter | Required | Type | Description | |-----------------------------------------------|----------|---------|-------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | string | X10 address | | `delta` | Yes | integer | the amount to brighten by relative to the current brightness - valid values from 1 to 100 | | `suppressLogging` | No | boolean | a boolean indicating if entries in the event log should be suppressed (default is False) | | `updateStatesOnly` | No | boolean | use if you only want Indigo's internal device state representation to be updated - no actual X10 commands will be sent (default is False) | ## Send Dim { .ref-head-no-code } This will send the Dim command to an X10 address. **Command Syntax Examples** `indigo.x10.sendDim("A1", delta=15)` **Parameters** | Parameter | Required | Type | Description | |-----------------------------------------------|----------|---------|-------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | string | X10 address | | `delta` | Yes | integer | the amount to dim by relative to the current brightness - valid values from 1 to 100 | | `suppressLogging` | No | boolean | a boolean indicating if entries in the event log should be suppressed (default is False) | | `updateStatesOnly` | No | boolean | use if you only want Indigo's internal device state representation to be updated - no actual X10 commands will be sent (default is False) | ## Send Extended { .ref-head-no-code } This will send an Extended command to an X10 address. **Command Syntax Examples** `indigo.x10.sendExtended("A1", data=10, command=128)` **Parameters** | Parameter | Required | Type | Description | |-----------------------------------------------|----------|---------|-------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | string | X10 address | | `command` | Yes | integer | the command to send | | `data` | Yes | integer | the data to send | | `suppressLogging` | No | boolean | a boolean indicating if entries in the event log should be suppressed (default is False) | | `updateStatesOnly` | No | boolean | use if you only want Indigo's internal device state representation to be updated - no actual X10 commands will be sent (default is False) | ## Send Hail Request { .ref-head-no-code } This will send a Hail Request command to the interface. **Command Syntax Examples** `indigo.x10.sendHailRequest("A1")` **Parameters** | Parameter | Required | Type | Description | |----------------------------------------------|----------|---------|------------------------------------------------------------------------------------------| | direct parameter | Yes | string | X10 address | | `suppressLogging` | No | boolean | a boolean indicating if entries in the event log should be suppressed (default is False) | ## Send Hail Reply { .ref-head-no-code } This will send a Hail Reply command to the interface. **Command Syntax Examples** `indigo.x10.sendHailReply("A1")` **Parameters** | Parameter | Required | Type | Description | |----------------------------------------------|----------|---------|------------------------------------------------------------------------------------------| | direct parameter | Yes | string | X10 address | | `suppressLogging` | No | boolean | a boolean indicating if entries in the event log should be suppressed (default is False) | ## Send On { .ref-head-no-code } This will send an ON command to an X10 address. **Command Syntax Examples** `indigo.x10.sendOn("A1")`
`indigo.x10.sendOn("A1", suppressLogging=True)`
`indigo.x10.sendOn("A1", updateStatesOnly=True)`
`indigo.x10.sendOn("A1", suppressLogging=True, updateStatesOnly=True)`
**Parameters** | Parameter | Required | Type | Description | |-----------------------------------------------|----------|---------|-------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | string | X10 address | | `suppressLogging` | No | boolean | a boolean indicating if entries in the event log should be suppressed (default is False) | | `updateStatesOnly` | No | boolean | use if you only want Indigo's internal device state representation to be updated - no actual X10 commands will be sent (default is False) | ## Send Off { .ref-head-no-code } This will send an OFF command to an X10 address. **Command Syntax Examples** `indigo.x10.sendOff("A1")`
`indigo.x10.sendOff("A1", suppressLogging=True)`
`indigo.x10.sendOff("A1", updateStatesOnly=True)`
`indigo.x10.sendOff("A1", suppressLogging=True, updateStatesOnly=True)`
**Parameters** | Parameter | Required | Type | Description | |-----------------------------------------------|----------|---------|-------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | string | X10 address | | `suppressLogging` | No | boolean | a boolean indicating if entries in the event log should be suppressed (default is False) | | `updateStatesOnly` | No | boolean | use if you only want Indigo's internal device state representation to be updated - no actual X10 commands will be sent (default is False) | ## Send Status Response On { .ref-head-no-code } This will send a Status Response On command to the interface. **Command Syntax Examples** `indigo.x10.sendStatusResponseOn("A1")` **Parameters** | Parameter | Required | Type | Description | |----------------------------------------------|----------|---------|------------------------------------------------------------------------------------------| | direct parameter | Yes | string | X10 address | | `suppressLogging` | No | boolean | a boolean indicating if entries in the event log should be suppressed (default is False) | ## Send Status Response Off { .ref-head-no-code } This will send a Status Response Off command to the interface. **Command Syntax Examples** `indigo.x10.sendStatusResponseOff("A1")` **Parameters** | Parameter | Required | Type | Description | |----------------------------------------------|----------|---------|------------------------------------------------------------------------------------------| | direct parameter | Yes | string | X10 address | | `suppressLogging` | No | boolean | a boolean indicating if entries in the event log should be suppressed (default is False) | --- Device Subclasses (https://docs.indigodomo.com/2025.2/scripting/reference/device-subclasses/) --- # Device Subclasses ## Device Subclasses { #device-subclasses-device-subclasses } Each built-in device type extends the [Device base class](../devices/index.md) with its own properties, states, and command namespace: - [DimmerDevice](dimmer.md) · [RelayDevice](relay.md) · [SensorDevice](sensor.md) - [SpeedControlDevice](speedcontrol.md) · [SprinklerDevice](sprinkler.md) · [ThermostatDevice](thermostat.md) · [MultiIODevice](multiio.md) For a cross-cutting view of which controls each device *capability* enables (rather than per-subclass), see [Device Capabilities](../devices/capabilities.md). --- DimmerDevice (https://docs.indigodomo.com/2025.2/scripting/reference/device-subclasses/dimmer/) --- # DimmerDevice { .ref-head-no-code } Dimmer devices are dimmable light modules. They can be turned on and off and their brightness may be set. Your script may manipulate any dimmer device. You may specify that devices defined by your plugin are of this type. The standard dimmer UI in the various clients will be presented to users attempting to control your device. ## Class Properties { .ref-head-no-code } | Property | Type | Writable | Description | |-----------------------------------------|---------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `brightness` | integer | No | an integer from 0-100 indicating the brightness of the device - plugin developers may decide if their device may be on but have a brightness of 0 (non-standard) - set using commands below | | `ledStates` | list | No | this is a list of booleans that represent the state of LEDs on the device. So, to check to see if LED 3 is on you'd check dev.ledStates[2] (Python arrays are 0-based so the first element is element 0). Currently, only KeypadLinc devices use this array - however, future devices may use it as well so you should probably check the length first to make sure that the LED you're looking for is actually there. Use the len(dev.ledStates) method to see how many entries there are before you access a specific index. | | `onState` | boolean | No | indicates whether the device is on - shortcut for `dev.states['onOffState']` | ## Device States { .ref-head-no-code } These are the states provided by this device type and accessible through the `dev.states` dictionary. They are read-only, but if you're a plugin developer you can use the `updateStateOnServer()` class method to update the value for devices owned by your plugin. | State ID | Type | Property Name | Notes | |----------------------------------------------|---------|---------------|---------------------------------------------------------------------------------------------------| | `brightnessLevel` | integer | `brightness` | brightness of the device - value is in the range of 0 to 100. Can be accessed by `dev.brightness` | | `onOffState` | boolean | `onState` | indicates whether the device is on - can be accessed by `dev.onState`. | ## Commands (indigo.dimmer.*) { .ref-head-no-code } ### All Lights Off { .ref-head-no-code } Turns off all lights for all protocols unless a direct parameter is specified. The direct parameter, if specified, will determine which lights will be turned off. Lights are defined as all dimmable devices. This command doesn’t work for plugin defined devices regardless of type. **Command Syntax Examples** `indigo.dimmer.allLightsOff()`
`indigo.dimmer.allLightsOff(indigo.kAllDeviceSel.HouseCodeA)`
`indigo.dimmer.allLightsOff(indigo.kAllDeviceSel.Insteon)`
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | No | [kAllDeviceSel](../devices/base-class.md#all-device-selector-enumeration) | enumerated value to indicate which devices to turn off, all if no parameter is passed - see the [kAllDeviceSel](../devices/base-class.md#all-device-selector-enumeration) enumeration for a full description | ### All Lights On { .ref-head-no-code } Turns on all lights for all protocols unless a direct parameter is specified. The direct parameter, if specified, will determine which lights will be turned on. Lights are defined as all dimmable devices. This command doesn’t work for plugin defined devices regardless of type. **Command Syntax Examples** `indigo.dimmer.allLightsOn()`
`indigo.dimmer.allLightsOn(indigo.kAllDeviceSel.HouseCodeA)`
`indigo.dimmer.allLightsOn(indigo.kAllDeviceSel.Insteon)`
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | No | [kAllDeviceSel](../devices/base-class.md#all-device-selector-enumeration) | enumerated value to indicate which lights to turn on, all if no parameter is passed - see the [kAllDeviceSel](../devices/base-class.md#all-device-selector-enumeration) enumeration for a full description | ### Brighten { .ref-head-no-code } Changes the brightness of the specified light relative to the current brightness. **Command Syntax Examples** `indigo.dimmer.brighten(123)`
`indigo.dimmer.brighten('Office Lamp', by=50, delay=4)`
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `by` | No | integer | relative amount to brighten by where brightness is 0-100 | | `delay` | No | integer | number of seconds to delay before executing the brighten command | ### Dim { .ref-head-no-code } Dim the brightness of the specified light by some amount relative to the current brightness. **Command Syntax Examples** `indigo.dimmer.dim(123)`
`indigo.dimmer.dim('Office Lamp', by=50, delay=4)`
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|-------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `by` | No | integer | relative amount to dim by where dimness is 0-100 | | `delay` | No | integer | number of seconds to delay before executing the dim command | ### Set Brightness { .ref-head-no-code } Changes the brightness of the specified light to a specific value. **Command Syntax Examples** `indigo.dimmer.setBrightness(123, value=75)`
`indigo.dimmer.setBrightness(123, value=75, delay=360)`
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|-------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `value` | Yes | integer | absolute value to set the brightness to on a scale of 0-100 | | `delay` | No | integer | number of seconds to delay before executing the dim command | ### Set LED State { .ref-head-no-code } Turns on/off the specified LED. Useful for KeypadLinc (and similar) devices. !!! note You can't use this method to control the LED of the button(s) that control the direct load - use Turn ON/Turn OFF for those. **Command Syntax Examples** `indigo.dimmer.setLedState(123, index=0, value=True)`
`indigo.dimmer.setLedState(123, index=0, value=True, suppressLogging=True)`
`indigo.dimmer.setLedState(123, index=0, value=True, updateStatesOnly=True)`
**Parameters** | Parameter | Required | Type | Description | |-----------------------------------------------|----------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `index` | Yes | integer | the index of the button. Recall that Python-based arrays are 0-based, so the index is also 0-based. That is, the first object in an array is object 0. | | `value` | Yes | boolean | True to turn on the LED, False to turn it off | | `suppressLogging` | No | boolean | True to suppress logging (defaults to False) | | `updateStatesOnly` | No | boolean | True to only update Indigo's internal state - no command will be sent to the KPL (defaults to False) | ### Set Color Levels { .ref-head-no-code } Sets the color levels for the dimmer device. The levels are the same as brightness, so 0 to 100, except for **whiteTemperature** which ranges from 1200-15000. Real number precision is allowed and the actions UI will display precision up to the hundredths digit, allowing for relatively good precision if a plugin needs to convert between RGB and HSB. Some RGBW devices (Aeotec bulb) support 2 different white channels with unique white temperatures: warm and cool. In those cases both **whiteLevel** and **whiteLevel2** can be used. Other white color devices allow for a specific temperature value to be specified (in Kelvin). For those devices you would use both the **whiteLevel** parameter in combination with the **whiteTemperature** parameter. Note for specifying white color temperature devices should support either the 2 white level technique, or the single white level + white temperature technique. That is, if the **whiteLevel2** parameter is used then whiteTemperature will be ignored. **Command Syntax Examples** ```python indigo.dimmer.setColorLevels(device, redLevel, greenLevel, blueLevel, whiteLevel, whiteLevel2, whiteTemperature, delay, suppressLogging, updateStatesOnly ) ``` **Parameters** | Parameter | Required | Type | Description | |-----------------------------------------------|----------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `redLevel` | No | integer | 0 - 100 | | `greenLevel` | No | integer | 0 - 100 | | `blueLevel` | No | integer | 0 - 100 | | `whiteLevel` | No | integer | 0 - 100 | | `whiteLevel2` | No | integer | 0 - 100 | | `whiteTemperature` | No | integer | 1200 - 15000 | | `delay` | No | integer | Defaults to 0. | | `suppressLogging` | No | boolean | Defaults to `False` | | `updateStatesOnly` | No | boolean | is an argument that exists on several device instance APIs that has Indigo update its device state/UI but does not have the corresponding action sent out to physical module. The use cases for this are pretty few, but sometimes you just want the internal state to update and can skip the actual command to the module. Defaults to `False` | A note about setting color levels: RGB values are expressed as values from 0 to 255, however, Indigo stores those values from 0 to 100. This requires the RGB values to be converted which is very easy. ```python # RGB value / 255 * 100 redLevel = 128 newRedLevel = redLevel / 255 * 100 ``` ### Set On State { .ref-head-no-code } Use the Turn On, Turn Off, and Toggle methods to turn on/off a dimmer device. **Examples** Here are examples of device properties: ```python # Getting a device myDevice = indigo.devices[123] ``` ```python # Set a device’s brightness to 75 if it’s currently # less than that, but only if it’s turned on myDevice = indigo.devices[123] if ((myDevice.brightness < 75) and (myDevice.onState)): indigo.dimmer.setBrightness(myDevice, 75) ``` ```python # Brighten a light by 25% after 10 minutes indigo.dimmer.brighten(123, by=25, delay=600) ``` ```python # Logging a message if it’s showing in Indigo Touch myDevice = indigo.devices[123] if (myDevice.remoteDisplay): indigo.server.log("device is showing in Indigo Touch") ``` ```python # Getting the folder ID that the device is in myDevice = indigo.devices[123] myDevice.folderId # OR indigo.devices[123].folderId # see comments below ``` You might look at the second example above, and wonder why we didn’t do something like this: ```python # Set a device’s brightness to 75 if it’s currently # less than that, but only if it’s turned on myDevice = indigo.devices[123] if ((indigo.devices[123].brightness < 75) and (indigo.devices[123].onState)): indigo.dimmer.setBrightness(indigo.devices[123], 75) ``` That code works correctly, but is very inefficient. The Python to C++ bridge will cause a copy of the object to be made every time `indigo.devices[123]` is used since the devices list is a C++ object, but the object returned from it using the id subscript is bridged to a Python object, and all bridged objects are copies. So, the code directly above will create 3 copies of the device object - very inefficient. The code in the example above gets a single copy of the object and uses that in all the rest of the code. A good rule of thumb is to get an explicit copy of an object if you need to use it more than once. --- MultiIODevice (https://docs.indigodomo.com/2025.2/scripting/reference/device-subclasses/multiio/) --- # MultiIODevice { .ref-head-no-code } I/O devices have a wide variety of capabilities that we’ve tried to boil down to some specifics. The I/O devices that Indigo supports generally have some combination of three types of inputs: analog, binary, and sensor. They may also support some number of binary outputs. ## Class Properties { .ref-head-no-code } All outputs are modified using commands below. | Property | Type | Writable | Description | |------------------------------------------------|-----------------|----------|----------------------------------------------------------------------------------------------------------------------------------------| | `analogInputs` | list of integer | No | a list of the current analog input values, one per input, in a python list - can be accessed individually using the states below | | `analogInputCount` | integer | No | number of analog inputs this device supports | | `binaryInputs` | list of boolean | No | a list of the current binary input values, one per input - can be accessed individually using the states below | | `binaryInputCount` | integer | No | number of binary inputs this device supports | | `binaryOutputs` | list of boolean | No | a list of the current binary output values, on per output (max 12 total outputs) - can be accessed individually using the states below | | `binaryOutputCount` | integer | No | number of binary outputs this device supports | | `sensorInputCount` | integer | No | number of sensor inputs this device supports | | `sensorInputs` | list of integer | No | a list of the current sensor input values, one per input - can be accessed individually using the states below | ## Device States { .ref-head-no-code } These are the states provided by this device type and accessible through the `dev.states` dictionary. They are read-only. | State ID | Type | Property Name | Notes | |-----------------------------------------------|---------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------| | `analogInput#` | integer | N/A | value of input number represented by the # sign (input 1 would be `analogInput1`) - there will only be `dev.analogInputCount` inputs available | | `analogInputsAll` | string | N/A | a comma separated list of all analog input values. Can be accessed as a Python list of integers by using `dev.analogInputs`. | | `binaryInput#` | boolean | N/A | value of input number represented by the # sign (input 1 would be `binaryInput1`) - there will only be `dev.binaryInputCount` inputs available | | `binaryInputsAll` | string | N/A | a comma separated list of all binary input values. Can be accessed as a Python list of booleans by using `dev.binaryInputs`. | | `binaryOutput#` | boolean | N/A | value of output number represented by the # sign (output 1 would be `binaryOutput1`) - there will only be `dev.binaryOutputCount` outputs available | | `binaryOutputsAll` | string | N/A | a comma separated list of all binary output values. Can be accessed as a Python list of booleans by using `dev.binaryOutputs`. | | `sensorInput#` | integer | N/A | value of input number represented by the # sign (input 1 would be `sensorInput1`) - there will only be `dev.sensorInputCount` inputs available | | `sensorInputsAll` | string | N/A | a comma separated list of all binary input values. Can be accessed as a Python list of booleans by using `dev.sensorInputs`. | ## Commands (indigo.iodevice.*) { .ref-head-no-code } ### Set Binary Output { .ref-head-no-code } Set the state of the specified binary output. **Command Syntax Examples** `indigo.iodevice.setBinaryOutput(123, index=2, value=True)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `index` | Yes | integer | 0-based index of the output to change - should be less than the `binaryOutputCount` property of the device | | `value` | Yes | boolean | True to turn the output on, False to turn it off | **Examples** ```python # If binary input 3 is true, set binary output 1 # to false (python arrays are 0-based) myIODevice = indigo.devices[123] if not myIODevice.binaryInputs[2]: indigo.iodevice.setBinaryOutput(myIODevice, index=1, value=False) ``` --- RelayDevice (https://docs.indigodomo.com/2025.2/scripting/reference/device-subclasses/relay/) --- # RelayDevice { .ref-head-no-code } Relay devices are very simple devices, often times called appliance modules. They can be turned on and off only. Your script may manipulate any relay device. Your plugin may specify devices that are of this type and the standard relay UI in the various clients will be presented to users when controlling it. ## Class Properties { .ref-head-no-code } | Property | Type | Writable | Description | |----------------------------------------|---------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `ledStates` | list | No | this is a list of booleans that represent the state of LEDs on the device. So, to check to see if LED 3 is on you'd check dev.ledStates[2] (Python arrays are 0-based so the first element is element 0). Currently, only KeypadLinc devices use this array - however, future devices may use it as well so you should probably check the length first to make sure that the LED you're looking for is actually there. Use the len(dev.ledStates) method to see how many entries there are before you access a specific index. | | `onState` | boolean | No | indicates whether the device is on - shortcut to `dev.states['onOffState']` | ## Device States { .ref-head-no-code } These are the states provided by this device type and accessible through the `dev.states` dictionary. They are read-only, but if you're a plugin developer you can use the `updateStateOnServer()` class method to update the value for devices owned by your plugin. | State ID | Type | Property Name | Notes | |-----------------------------------------|---------|---------------|-------------------------------------------------------------------------------| | `onOffState` | boolean | `onState` | indicates whether the device is on - use `dev.onState` property as a shortcut | ## Commands (indigo.relay.*) { .ref-head-no-code } Use the [Turn On](../devices/base-class.md#turn-on), [Turn Off](../devices/base-class.md#turn-off), and [Toggle](../devices/base-class.md#toggle) methods in the indigo.device.* namespace to turn on/off a relay device. ### Set LED State { .ref-head-no-code } Turns on/off the specified LED. Useful for KeypadLinc (and similar) devices. !!! note You can't use this method to control the LED of the button(s) that control the direct load - use Turn ON/Turn OFF for those. **Command Syntax Examples** `indigo.relay.setLedState(123, index=0, value=True)`
`indigo.relay.setLedState(123, index=0, value=True, suppressLogging=True)`
`indigo.relay.setLedState(123, index=0, value=True, updateStatesOnly=True)`
**Parameters** | Parameter | Required | Type | Description | |-----------------------------------------------|----------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `index` | Yes | integer | the index of the button. Recall that Python-based arrays are 0-based, so the index is also 0-based. That is, the first object in an array is object 0. | | `value` | Yes | boolean | True to turn on the LED, False to turn it off | | `suppressLogging` | No | boolean | True to suppress logging (defaults to False) | | `updateStatesOnly` | No | boolean | True to only update Indigo's internal state - no command will be sent to the KPL (defaults to False) | **Examples** ```python # Toggle the device indigo.device.toggle(123) ``` --- SensorDevice (https://docs.indigodomo.com/2025.2/scripting/reference/device-subclasses/sensor/) --- # SensorDevice { .ref-head-no-code } Some sensor devices, like motion sensors, are treated differently in Indigo. They don’t generally maintain state - so there’s no concept of a sensor being on/off: in the case of a motion sensor, detecting motion and not detecting motion. They generally send a command of some type when they detect some condition and send another command when they stop detecting it. However, dealing with them in Indigo as if they maintain state is much more useful in many cases. So, Indigo will attempt to maintain a virtual state for each motion sensor if configured that way. Currently, the following devices fall under this category: - X10 Motion Sensors - the Wireless Insteon Motion / Occupancy Sensor (2420M) from SmartLabs - the TriggerLinc from SmartLabs - the SynchroLinc from SmartLabs ## Class Properties { .ref-head-no-code } | Property | Type | Writable | [Min API](https://www.indigodomo.com/indigo/api_version_chart.html) | Description | |-----------------------------------------------------|------------------|----------|---------------------------------------|-----------------------------------------------------------------------------------------------------------| | `allowOnStateChange` | boolean | No | 1.6 | True if UI controls should be shown or enabled to change the onState | | `allowSensorValueChange` | boolean | No | 1.6 | True if UI controls should be shown or enabled to change the sensorValue | | `onState` | boolean | No | 1.0 | indicates that the device is currently in a triggered or ON state (None if sensor doesn't support ON/OFF) | | `sensorValue` | integer or float | No | 1.6 | the numerical value of a sensor, such as temperature (None if sensor doesn't support sensor values) | ## Commands (indigo.sensor.*) { .ref-head-no-code } ### Set On State { .ref-head-no-code } Set the sensor onState property. Normally this is unnecessary since Indigo will maintain it for you so this method is provided mainly for testing and error recovery. **Command Syntax Examples** `indigo.sensor.setOnState(123, value=True)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|----------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | value | Yes | boolean | True to set Indigo’s state to on, False to set it to off | **Examples**
None --- SpeedControlDevice (https://docs.indigodomo.com/2025.2/scripting/reference/device-subclasses/speedcontrol/) --- # SpeedControlDevice { .ref-head-no-code } Speed control devices are generally controllers for motors of some type. The first implementation was for the Insteon FanLinc, which is a ceiling fan motor controller. This device type provides two different methods of setting the speed of the motor: by level, which ranges from 0 (off) to 100 (full on), and by index. With a device like a FanLinc, for instance, you can't set an arbitrary speed - you can only set it to some number of fixed speeds and this is what index is for. The FanLinc will in fact respond to setting the level, but we normalize the level to the appropriate speed. Other speed control devices may not choose to do so. ## Class Properties { .ref-head-no-code } | Property | Type | Writable | Description | |----------------------------------------------|---------|----------|----------------------------------------------------------------------------------------------------------------------------| | `onState` | boolean | No | indicates whether the device is on - shortcut to `dev.states['onOffState']` | | `speedIndex` | integer | No | indicates the current speed index for devices that support some fixed # of speeds - shortcut to `dev.states['speedIndex']` | | `speedIndexCount` | integer | No | indicates the number of indexes available for this device (defaults to 4) | | `speedLevel` | integer | No | indicates the level the device is set to (0-100) - shortcut to `dev.states['speedLevel']` | ## Device States { .ref-head-no-code } These are the states provided by this device type and accessible through the `dev.states` dictionary. They are read-only, but if you're a plugin developer you can use the `updateStateOnServer()` class method to update the value for devices owned by your plugin. | State ID | Type | Property Name | Notes | |--------------------------------------------|---------|---------------|---------------------------------------------------------------------------------------------------------------------------------| | `onOffState` | boolean | `onState` | indicates whether the device is on - use `dev.onState` property as a shortcut | | `speedIndex` | boolean | `speedIndex` | indicates the current speed index for devices that support some fixed # of speeds - use `dev.speedIndex` property as a shortcut | | `speedIndex.ui` | string | n/a | a more user friendly name for the current index (e.g. high, medium, low, off) | | `speedLevel` | integer | `speedLevel` | indicates the level the device is set to (0-100) - use `dev.speedLevel` property as a shortcut | ## Commands (indigo.speedcontrol.*) { .ref-head-no-code } ### Decrease Speed Index { .ref-head-no-code } Decreases the speed index. **Command Syntax Examples** `indigo.speedcontrol.decreaseSpeedIndex(123)`
`indigo.speedcontrol.decreaseSpeedIndex(123, by=2)`
`indigo.speedcontrol.decreaseSpeedIndex(123, delay=10)`
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|-----------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `by` | No | integer | the number of index positions to decrease by. Defaults to 1 if not specified. | | `delay` | No | integer | delays the command by the specified number of seconds. Defaults to no delay if not specified. | ### Increase Speed Index { .ref-head-no-code } Increases the speed index. **Command Syntax Examples** `indigo.speedcontrol.increaseSpeedIndex(123)`
`indigo.speedcontrol.increaseSpeedIndex(123, by=2)`
`indigo.speedcontrol.increaseSpeedIndex(123, delay=10)`
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|-----------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `by` | No | integer | the number of index positions to increase by. Defaults to 1 if not specified. | | `delay` | No | integer | delays the command by the specified number of seconds. Defaults to no delay if not specified. | ### Set Speed Index { .ref-head-no-code } Sets the speed index to the specified value. **Command Syntax Examples** `indigo.speedcontrol.setSpeedIndex(123, value=2)`
`indigo.speedcontrol.increaseSpeedIndex(123, value=2, delay=10)`
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|-----------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `value` | Yes | integer | index position to set the index to. | | `delay` | No | integer | delays the command by the specified number of seconds. Defaults to no delay if not specified. | ### Set Speed Level { .ref-head-no-code } Sets the speed index to the specified value. **Command Syntax Examples** `indigo.speedcontrol.setSpeedLevel(123, value=50)`
`indigo.speedcontrol.setSpeedLevel(123, value=75, delay=10)`
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|-----------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `value` | Yes | integer | level of the motor control, from 0-100. | | `delay` | No | integer | delays the command by the specified number of seconds. Defaults to no delay if not specified. | --- SprinklerDevice (https://docs.indigodomo.com/2025.2/scripting/reference/device-subclasses/sprinkler/) --- # SprinklerDevice { .ref-head-no-code } Sprinkler devices generally have some number of sprinkler zones. ## Class Properties { .ref-head-no-code } | Property | Type | Writable | [Min API](https://www.indigodomo.com/indigo/api_version_chart.html) | Description | |------------------------------------------------------------------|-----------------|----------|---------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `activeZone` | integer | No | 1.0 | the 1-based index of the active zone (None=all zones off, 1=zone 1, 2=zone 2, ...). Note this property has undergone changes in name (previously called activeZoneIndex) as well as semantics (previously the index being 0-based). | | `zoneCount` | integer | No | 1.0 | the number of zones available for this sprinkler | | `zoneEnableList` | list of boolean | No | 1.13 | list of booleans, starting at zone 1 through zone [`zoneCount`], that specify if a given zone is enabled (has a maximum zone duration > 0). | | `zoneNames` | list of string | No | 1.0 | list of zone names, starting at zone 1 through zone [`zoneCount`]. You must include `zoneCount` strings in the list. | | `zoneMaxDurations` | list of floats | No | 1.0 | list of zone durations in minutes, starting at zone 1 through zone [`zoneCount`]. You must include `zoneCount` integers in the list. | | `zoneScheduledDurations` | list of floats | No | 1.0 | list of currently active zone durations in minutes if a schedule is running (empty list if no schedule is running), starts at zone 1 through zone [`zoneCount`] | | `pausedScheduleZone` | integer | No | 1.16 | the 1-based index of the paused sprinkler zone index (None=schedule not paused, 1=zone 1 paused, 2=zone 2 paused, ...). | | `pausedScheduleRemainingZoneDuration` | float | No | 1.16 | the paused sprinkler zone duration in minutes (None if schedule not paused) | ## Device States { .ref-head-no-code } These are the states provided by this device type and accessible through the `dev.states` dictionary. They are read-only. | State ID | Type | Property Name | Notes | |--------------------------------------------|---------|---------------|---------------------------------------------------------------------------------------------------------| | `activeZone` | integer | `activeZone` | the number of the active zone, 0 if off (1=zone 1, 2=zone 2, etc) | | `activeZone.ui` | string | N/A | the active zone as a human readable string | | `zone#` | boolean | N/A | replace the # with the zone number (up to `dev.zoneCount`) to return whether the zone is running or not | ## Commands (indigo.sprinkler.*) { .ref-head-no-code } ### Next Zone { .ref-head-no-code } Set the sprinkler to the next zone, and turn off if it’s the last defined zone. **Command Syntax Examples** `indigo.sprinkler.nextZone(123)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------| | direct parameter | Yes | integer | id or instance of the device | ### Pause Schedule { .ref-head-no-code } Pause the current sprinkler schedule but keep it active so it can be resumed. **Command Syntax Examples** `indigo.sprinkler.pause(123)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------| | direct parameter | Yes | integer | id or instance of the device | ### Previous Zone { .ref-head-no-code } Set the sprinkler to the previous zone, and turn off if it’s the first defined zone. **Command Syntax Examples** `indigo.sprinkler.previousZone(123)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------| | direct parameter | Yes | integer | id or instance of the device | ### Resume Schedule { .ref-head-no-code } Resume the current sprinkler schedule. **Command Syntax Examples** `indigo.sprinkler.resume(123)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------| | direct parameter | Yes | integer | id or instance of the device | ### Run Schedule { .ref-head-no-code } Run a sprinkler schedule. **Command Syntax Examples** `indigo.sprinkler.run(123, schedule=[10,15,8, 0, 0, 0, 0, 0])` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `schedule` | Yes | list of reals | list of reals representing the number of minutes to run for each zone - the list must have [`zoneCount`] elements, with 0 for any zone that shouldn’t run | ### Stop Schedule { .ref-head-no-code } Stop the current sprinkler schedule and clear it. **Command Syntax Examples** `indigo.sprinkler.stop(123)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------| | direct parameter | Yes | integer | id or instance of the device | ### Set Active Zone { .ref-head-no-code } API v1.12+ only: Turn on a specific zone. **Command Syntax Examples** `indigo.sprinkler.setActiveZone(123, index=2)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|--------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `index` | Yes | integer | 1-based index of the zone to turn on | **Examples** ```python # run schedule indigo.sprinkler.run(123, schedule=[10,15,8, 0, 0, 0, 0, 0]) ``` --- ThermostatDevice (https://docs.indigodomo.com/2025.2/scripting/reference/device-subclasses/thermostat/) --- # ThermostatDevice { .ref-head-no-code } Thermostats have a wide variety of capabilities that we’ve tried to boil down to some specifics. They can have multiple temperature and humidity sensors and--depending on the device capabilities and region--may have a fan mode, an HVAC mode (heating, cooling, etc.) and associated setpoints. **Note:** some thermostats don’t support getting the equipment state values: `coolIsOn`, `fanIsOn`, `heatIsOn`, `dehumidifierIsOn`, and `humidifierIsOn`. For those thermostats those properties will always be False. ## Class Properties { .ref-head-no-code } | Property | Type | Writable | [Min API](https://www.indigodomo.com/indigo/api_version_chart.html) | Description | |-----------------------------------------------------|-------------------------------------|----------|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `coolIsOn` | boolean | No | 1.0 | is the cooling system (compressor) currently running - shortcut for `dev.states['hvacCoolerIsOn']`. This property is always present and will be `False` if the corresponding state `dev.states['hvacCoolerIsOn']` is not present. | | `coolSetpoint` | float | No | 1.0 | current cool setpoint value - shortcut for `dev.states['setpointCool']` | | `dehumidifierIsOn` | boolean | No | 1.7 | is the dehumidifier currently turned ON - shortcut for `dev.states['hvacDehumidifierIsOn']` | | `fanMode` | [kFanMode](#fan-mode-enumeration) | No | 1.0 | the operating mode for the fan attached to the thermostat - shortcut for `dev.states['hvacFanMode']` | | `fanIsOn` | boolean | No | 1.0 | is the fan currently running - shortcut for `dev.states['hvacFanIsOn']` | | `heatIsOn` | boolean | No | 1.0 | is the heater currently running - shortcut for `dev.states['hvacHeaterIsOn']`. This property is always present and will be `False` if the corresponding state `dev.states['hvacHeaterIsOn']` is not present. | | `heatSetpoint` | float | No | 1.0 | current heat setpoint value - shortcut for `dev.states['setpointHeat']` | | `humidities` | list of float | No | 1.0 | a list of floating point values representing the current values of all humidity sensors connected to the thermostat | | `humiditySensorCount` | integer | No | 1.0 | number of humidity sensors this thermostat supports | | `humidifierIsOn` | boolean | No | 1.7 | is the humidifier currently turned ON - shortcut for `dev.states['hvacHumidifierIsOn']` | | `hvacMode` | [kHvacMode](#hvac-mode-enumeration) | No | 1.0 | the operating mode for the HVAC system attached to the thermostat - shortcut for `dev.states['hvacOperationMode']` | | `temperatureSensorCount` | integer | No | 1.0 | number of temperature sensors this thermostat supports | | `temperatures` | list of float | No | 1.0 | a list of floating point values representing the current values of all temperature sensors connected to the thermostat | To check whether the thermostat device actually supports the `coolIsOn` and `heatIsOn` properties, one can check: `support_heat_and_cool = dev.pluginProps.get("ShowCoolHeatEquipmentStateUI", False)` ### Plugin Capabilities { .ref-head-no-code } These pluginProps can be updated by a plugin to describe the capabilities for a particular thermostat instance. Some of these are useful to provide a higher-level abstraction for accessing/changing thermostat properties or states. | Property | Type | Writeable | Description | |-----------------------------------------------------------|---------|-----------|------------------------------| | `NumTemperatureInputs` | Integer | Yes | should range between 1 and 3 | | `NumHumidityInputs` | Integer | Yes | should range between 0 and 3 | | `SupportsHeatSetpoint` | Boolean | Yes | True or False | | `SupportsCoolSetpoint` | Boolean | Yes | True or False | | `SupportsHvacOperationMode` | Boolean | Yes | True or False | | `SupportsHvacFanMode` | Boolean | Yes | True or False | | `ShowCoolHeatEquipmentStateUI` | Boolean | Yes | True or False | Some of these are reflected as attributes in the device instance as well: | Attribute | Read-Only | |------------------------------------------------------------|-----------| | `dev.hvacMode` | Yes | | `dev.fanMode` | Yes | | `dev.coolSetpoint` | Yes | | `dev.heatSetpoint` | Yes | | `dev.temperatureSensorCount` | Yes | | `dev.temperatures` | Yes | | `dev.humiditySensorCount` | Yes | | `dev.humidities` | Yes | | `dev.coolIsOn` | Yes | | `dev.heatIsOn` | Yes | | `dev.fanIsOn` | Yes | | `dev.dehumidifierIsOn` | Yes | | `dev.humidifierIsOn` | Yes | | `dev.supportsHvacFanMode` | Yes | | `dev.supportsHvacOperationMode` | Yes | | `dev.supportsCoolSetpoint` | Yes | | `dev.supportsHeatSetpoint` | Yes | More information on how to use these Thermostat properties and attributes can be found in the `Example Device - Thermostat.indigoPlugin` in the [Indigo Plugin SDK](https://github.com/IndigoDomotics/IndigoSDK). #### Fan Mode Enumeration { #fan-mode-enumeration .ref-head-no-code } | `indigo.kFanMode` | | |----------------------------------------------|------------------------------------------------------------------------------------| | Value | Description | | `AlwaysOn` | signal the fan that it should be running continuously | | `Auto` | signal the fan that it should only run when the HVAC system needs it to be running | #### HVAC Mode Enumeration { #hvac-mode-enumeration .ref-head-no-code } | `indigo.kHvacMode` | | |-----------------------------------------------|----------------------------------------------------------------------------------------------------------------------| | Value | Description | | `Cool` | the hvac system is only reacting to cool setpoints | | `HeatCool` | the hvac system is reacting to both cool and heat setpoints | | `Heat` | the hvac system is only reacting to and heat setpoints | | `Off` | the hvac system is turned off | | `ProgramHeatCool` | the hvac system is executing it’s built-in automatic program, which usually responds to both heat and cool setpoints | | `ProgramCool` | the hvac system is executing it’s built-in cooling program | | `ProgramHeat` | the hvac system is executing it’s built-in heating program | ## Device States { .ref-head-no-code } These are the states provided by this device type and accessible through the `dev.states` dictionary. They are read-only, but if you're a plugin developer you can use the `updateStateOnServer()` class method to update most of these states for devices owned by your plugin (exceptions are noted below). | State ID | Type | Property Name | Notes | |-------------------------------------------------------------|-------------------------------------|-----------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `humidityInput#` | float | N/A | replace the # with the humidity sensor # (up to `humiditySensorCount`) to directly access the humidity value for that input | | `humidityInputsAll` | string | N/A | a comma separated list of all humidity values | | `hvacCoolerIsOn` | boolean | `coolIsOn` | `True` if the cooling system currently running | | `hvacDehumidifierIsOn` | boolean | `dehumidifierIsOn` | `True` if the dehumidifier is currently running | | `hvacFanIsOn` | boolean | `fanIsOn` | `True` if the fan currently running | | `hvacFanMode` | [kFanMode](#fan-mode-enumeration) | `fanMode` | operating mode for the fan attached to the thermostat | | `hvacFanIsAlwaysOn` | boolean | N/A | `True` if the fan mode is set to `AlwaysOn`. Note: this state can't be directly updated but rather will be updated automatically when you update `hvacFanMode`. | | `hvacFanIsAuto` | boolean | N/A | `True` if the fan mode is set to `Auto`. Note: this state can't be directly updated but rather will be updated automatically when you update `hvacFanMode`. | | `hvacHeaterIsOn` | boolean | `heatIsOn` | `True` if the heating system currently running | | `hvacHumidifierIsOn` | boolean | `humidifierIsOn` | `True` if the humidifier is currently running | | `hvacOperationMode` | [kHvacMode](#hvac-mode-enumeration) | `hvacMode` | operating mode for the HVAC system attached to the thermostat | | `hvacOperationModeIsAuto` | boolean | N/A | `True` if the HVAC is set to `HeatCool`. Note: this state can't be directly updated but rather will be updated automatically when you update `hvacOperationMode`. | | `hvacOperationModeIsCool` | boolean | N/A | `True` if the HVAC is set to `Cool`. Note: this state can't be directly updated but rather will be updated automatically when you update `hvacOperationMode`. | | `hvacOperationModeIsHeat` | boolean | N/A | `True` if the HVAC is set to `Heat`. Note: this state can't be directly updated but rather will be updated automatically when you update `hvacOperationMode`. | | `hvacOperationModeIsOff` | boolean | N/A | `True` if the HVAC is set to `Off`. Note: this state can't be directly updated but rather will be updated automatically when you update `hvacOperationMode`. | | `hvacOperationModeIsProgramAuto` | boolean | N/A | `True` if the HVAC is set to `ProgramHeatCool`. Note: this state can't be directly updated but rather will be updated automatically when you update `hvacOperationMode`. | | `hvacOperationModeIsProgramCool` | boolean | N/A | `True` if the HVAC is set to `ProgramCool`. Note: this state can't be directly updated but rather will be updated automatically when you update `hvacOperationMode`. | | `hvacOperationModeIsProgramHeat` | boolean | N/A | `True` if the HVAC is set to `ProgramHeat`. Note: this state can't be directly updated but rather will be updated automatically when you update `hvacOperationMode`. | | `setpointCool` | float | `coolSetpoint` | the cool setpoint | | `setpointHeat` | float | `heatSetpoint` | the heat setpoint | | `temperatureInput#` | float | N/A | replace the # with the temperature sensor # (up to `temperatureSensorCount`) to directly access the humidity value for that input | | `temperatureInputsAll` | string | N/A | a comma separated list of all temperature values | ## Commands (indigo.thermostat.*) { .ref-head-no-code } ### Decrease Cool Setpoint { .ref-head-no-code } Decrease the cool setpoint by a delta value. **Command Syntax Examples** `indigo.thermostat.decreaseCoolSetpoint(123)`
`indigo.thermostat.decreaseCoolSetpoint(123, delta=5)`
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|----------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `delta` | No | float | Number of degrees to decrease the cool setpoint. If unspecified, the change will be equal to one degree. | ### Decrease Heat Setpoint { .ref-head-no-code } Decrease the heat setpoint by a delta value. **Command Syntax Examples** `indigo.thermostat.decreaseHeatSetpoint(123)`
`indigo.thermostat.decreaseHeatSetpoint(123, delta=5)`
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|-----------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `delta` | No | float | Number of degrees to decrease the heat setpoint. If unspecified, the change will be equal to one degree. | ### Increase Cool Setpoint { .ref-head-no-code } Increase the cool setpoint by a delta value. **Command Syntax Examples** `indigo.thermostat.increaseCoolSetpoint(123)`
`indigo.thermostat.increaseCoolSetpoint(123, delta=5)`
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|-----------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `delta` | No | float | Number of degrees to increase the cool setpoint. If unspecified, the change will be equal to one degree. | ### Increase Heat Setpoint { .ref-head-no-code } Increase the heat setpoint by a delta value. **Command Syntax Examples** `indigo.thermostat.increaseHeatSetpoint(123)`
`indigo.thermostat.increaseHeatSetpoint(123, delta=5)`
**Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|----------------------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `delta` | No | float | Number of degrees to increase the heat setpoint. If unspecified, the change will be equal to one degree. | ### Set Cool Setpoint { .ref-head-no-code } Set the cool setpoint to an absolute temperature. **Command Syntax Examples** `indigo.thermostat.setCoolSetpoint(123, value=78)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `value` | Yes | integer | the absolute temperature of the setpoint | ### Set Fan Mode { .ref-head-no-code } Adjust the fan mode. **Command Syntax Examples** `indigo.thermostat.setFanMode(123, value=indigo.kFanMode.AlwaysOn)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|-----------------------------------|--------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `value` | Yes | [kFanMode](#fan-mode-enumeration) | the operating mode for the fan | ### Set Heat Setpoint { .ref-head-no-code } Set the heat setpoint to an absolute temperature. **Command Syntax Examples** `indigo.thermostat.setHeatSetpoint(123, value=78)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|---------|------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `value` | Yes | integer | the absolute temperature of the setpoint | ### Set HVAC Mode { .ref-head-no-code } Adjust the HVAC mode. **Command Syntax Examples** `indigo.thermostat.setHvacMode(123, value=indigo.kHvacMode.HeatCool)` **Parameters** | Parameter | Required | Type | Description | |------------------------------------------|----------|-------------------------------------|------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `value` | Yes | [kHvacMode](#hvac-mode-enumeration) | HVAC mode identifier | **Examples** ```python # increase the cool setpoint by 5 degrees indigo.thermostat.decreaseCoolSetpoint(123, delta=5) # set the thermostat mode to auto indigo.thermostat.setHvacMode(123, value=indigo.kHvacMode.HeatCool) # set the heat setpoint to 78 indigo.thermostat.setHeatSetpoint(123, value=78) ``` --- Devices (https://docs.indigodomo.com/2025.2/scripting/reference/devices/) --- # Devices In the IOM, all devices are derived from a common `Device` base class. This base class provides for all the common properties of a device (including devices defined by Server plugins) and each subclass also inherits all the commands in the device command namespace (`indigo.device.`) - there are a few exceptions that will be noted below. Check out the examples for each section to see how you use each device type. Like other high-level objects in Indigo, there are rules for modifying devices. For Scripters and Plugin Developers: - To create, duplicate, delete, and send commands to a device, use the appropriate command namespace as defined below - To modify a device's definition get a copy of the device, make the necessary changes, then call `myDevice.replaceOnServer()` For Plugin Developers: - To update a plugin's props on a device on the server, call `myDevice.replacePluginPropsOnServer(newPropsDict)` rather than try to update them on the local device - To change a device's state on the server, use `myDevice.updateStateOnServer(key='keyName', value='Value')` - To change multiple device states on the server at one time, use `myDevice.updateStatesOnServer(update_list)` passing a dictionary that looks like this: **example** ```python dev = indigo.devices[12345678] key_value_list = [ {'key':'someKey1', 'value':True}, {'key':'someKey2', 'value':456}, {'key':'someKey3', 'value':789.123, 'uiValue':"789.12 lbs", 'decimalPlaces':2} ] dev.updateStatesOnServer(key_value_list) ``` !!! note All API descriptions apply to all API versions unless otherwise specified. ## In This Section - **[Device Base Class](base-class.md)** — properties, plugin properties, custom states, and the `indigo.device.*` commands shared by every device. - **[Device Dictionary Representation](dictionary.md)** — the dict form of a device. - **[Device Subclasses](../device-subclasses/index.md)** — type-specific properties, states, and commands (dimmer, relay, sensor, speed control, sprinkler, thermostat, multi-IO). - **[Device Capabilities](capabilities.md)** — the cross-cutting capability → control reference: which `supports*` attribute, subclass, or plugin property gates each control, and the command that drives it. --- Device Base Class (https://docs.indigodomo.com/2025.2/scripting/reference/devices/base-class/) --- # Device Base Class { .ref-head-no-code } The `Device` class is generally used as a base class - your script will use objects that are instances of one of its subclasses - we'll discuss each of the subclasses later in this section. First, a quick refresher on a fundamental aspect of devices: some devices can only be controlled (i.e. old-style ApplianceLincs, most X10 devices, etc.), called responders in Indigo terminology, other devices are only controllers (i.e. RemoteLincs, PalmPads, etc.), and some devices are both responders and controllers (i.e. KeypadLincs). This terminology was really invented for Insteon devices, but we think it applies to other technologies as well but perhaps in a slightly different way. In any event, controller devices can support a number of buttons and/or a number of groups from which commands are sent, all of which Indigo can use to trigger actions. From a technical and practical standpoint, they are in fact the same thing so a single number, indexed based on how the device supports them, is how this property should be used. The base class supports getting the number of buttons or groups as a single property (buttonGroupCount) for the device. Use that count as a guide to what you can pass in the various trigger classes. For any device that doesn’t support local physical buttons or sending group commands and/or status commands, the number returned should be 0. If you’re writing a plugin that defines devices, they will automatically inherit all the following properties. **Class Properties** | Property | Type | Writable | [Min API](https://www.indigodomo.com/indigo/api_version_chart.html) | Description | |-----------------------------------------------------|--------------------------------------------------|----------|-------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `address` | string | No* | 1.0 | the address for the device. *Plugin developers can change this value for their plugin's devices. | | `batteryLevel` | integer | No | 1.0 | battery level for the device - `None` if the device doesn't support battery operation - shortcut for `dev.states['batteryLevel']` | | `buttonGroupCount` | integer | No | 1.0 | the number of groups (or buttons in the case of the RemoteLinc and ControLinc) that the controller supports - currently used only for Insteon devices - 0 for other devices | | `configured` | boolean | No | 1.0 | true if the device has been fully configured - if it's a plugin device, use this to make sure that the device's config dialog has been run at least once. | | `description` | string | Yes | 1.0 | a description of the device as specified by the user | | `deviceTypeId` | string | No | 1.0 | the typeId specified in the Devices.xml (or it’s documentation) - only used for plugin defined devices | | `displayStateId` | string | No | 1.15 | main display stateId key which can be used as a key into states[] property below | | `displayStateValRaw` | boolean integer string or none | No | 1.15 | raw value of main display state (ex: 72.0) | | `displayStateValUi` | string | No | 1.15 | UI value (string) of main display state (ex: '72.0°F' ) | | `displayStateImageSel` | *[kStateImageSel](#state-image-sel-enumeration)* | No | 1.18 | an enumeration specifying which state image icon is shown in Indigo Touch and Indigo client UI - see state image sel enumeration below for possible values | | `enabled` | boolean | No | 1.0 | is the device enabled - set using the `indigo.device.enable()` method | | `energyCurLevel` | float | No | 1.11 | current Wattage energy being used by the device (None if not supported) | | `energyAccumTotal` | float | No | 1.11 | current accumulated energy used since last base time specified in `energyAccumBaseTime` (None if not supported) | | `energyAccumBaseTime` | datetime | No | 1.11 | the base time from which to calculate the energy total (`energyAccumTotal`) (None if not supported) | | `energyAccumTimeDelta` | integer | No | 1.11 | the time delta in seconds since the last base time (None if not supported) | | `errorState` | string | No | 1.0 | the string that represents the current error for the device, empty string if there is no error | | `folderId` | integer | No | 1.0 | the unique ID of the folder this device is in (0 if it's not in a folder) - use `moveToFolder()` method to change | | `globalProps` | dictionary | No | 1.0 | an `indigo.Dict()` representing all name/value pairs associated with this device - each plugin will have its own dictionary (`globalProps[pluginId]`) - see [About Plugin Properties](base-class.md#about-plugin-properties) below for details | | `id` | integer | No | 1.0 | id or instance of the device, assigned on creation by IndigoServer | | `lastChanged` | datetime | No | 1.0 | the last date/time that the device was changed - populated by IndigoServer | | `model` | string | No | 1.0 | the model name of the device - defined either by Indigo based on type or by the plugin's device definition | | `name` | string | Yes | 1.0 | the unique name of the device - no two devices can have the same name | | `ownerProps` | dictionary | No | 1.20 | an `indigo.Dict()` representing the name/value pairs defined by the plugin that created the device - this is a shortcut into the owner plugin's globalProps data | | `pluginId` | string | No | 1.0 | if protocol is `Plugin`, the string ID for the plugin | | `pluginProps` | dictionary | No | 1.0 | an `indigo.Dict()` representing the name/value pairs defined by your plugin for the device - plugin developers should publish this information if you want other plugins/scripts to create devices of this type - see [About Plugin Properties](base-class.md#about-plugin-properties) below for details - use `replacePluginPropsOnServer()` method to change | | `protocol` | *[kProtocol](#protocol-enumeration)* | No | All | an enumeration specifying the kProtocol of the device - see protocol enumeration below for possible values | | `remoteDisplay` | boolean | Yes | 1.0 | should this device be displayed in remote clients (IWS, Indigo Touch, etc) - may also be set with `indigo.device.displayInRemoteUI()` | | `sharedProps` | dictionary | No | **[2.3](https://www.indigodomo.com/indigo/api_release_notes/2.3/)** | an `indigo.Dict()` representing the name/value pairs that are shared by all plugins. This is the property dictionary that you can edit via the Global Properties plugin, and your plugin may manage properties in this dictionary as well to add metadata to devices that your plugin can use for other purposes. Use `dev.replaceSharedPropsOnServer()` to update them (as with pluginProps, you should get copy first, update the copy, then set them back to that copy so you don't accidentally remove some other plugin's props). | | `states` | dictionary | No | 1.0 | returns an indigo.Dict() of device states - the key is the state id and the value is the value. Note that enumerated states will have not only the state, but also each option for the state. So, for instance, if I had a state called *status* and it had 3 options (*online*, *offline*, *error*), then you'd not only have *status* as a key, but also *status.online*, *status.offline*, and *status.error* as keys in the dictionary. This is so that you can test each state enumeration independently in trigger actions (e.g. *status.online* is true). | | `supportsAllLightsOnOff` | boolean | No | All | indicates that this device should react to all lights On and all lights Off commands - always False for plugin defined devices | | `supportsAllOff` | boolean | No | 1.0 | indicates that this device should react to all Off commands - always False for plugin defined devices | | `supportsOnState` | boolean | No | **[2.2](https://www.indigodomo.com/indigo/api_release_notes/2.2/)** | indicates that the device has an on state | | `supportsStatusRequest` | boolean | No | 1.0 | indicates if the device supports querying the status - always False for plugin defined devices | | `version` | boolean | No* | 1.0 | indicates the device's version as appropriate. *Plugin developers can change this value for their plugin's devices. | ## Protocol Enumeration { #protocol-enumeration .ref-head-no-code } | indigo.kProtocol | | |--------------------------------------|------------------------------------------------------| | Value | Description | | `Insteon` | identifies the device as an Insteon device | | `X10` | identifies the device as an X10 device | | `ZWave` | identifies the device as an Z-Wave device | | `Plugin` | identifies the device as a being defined by a plugin | ## State Image Sel Enumeration { #state-image-sel-enumeration .ref-head-no-code } !!! note API v1.18+ only | indigo.kStateImageSel | | |--------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------| | Value | Description | | `Auto` | specifies Indigo Server to pick a device image icon that best represents this device class and/or state value (default for all devices) | | `AvPaused` | overrides to show a A/V paused icon | | `AvPlaying` | overrides to show a A/V playing icon | | `AvStopped` | overrides to show a A/V stopped icon | | `Closed` | overrides to show a generic sensor off icon (grey circle) | | `DehumidifierOff` | overrides to show a dehumidifier turned off icon | | `DehumidifierOn` | overrides to show a dehumidifier turned on icon | | `DimmerOff` | overrides to show a dimmer or bulb off icon | | `DimmerOn` | overrides to show a dimmer or bulb on icon | | `DoorSensorClosed` | overrides to show a door sensor closed icon (grey circle) | | `DoorSensorOpened` | overrides to show a door sensor opened icon (green circle) | | `EnergyMeterOff` | overrides to show an energy meter off icon | | `EnergyMeterOn` | overrides to show an energy meter on icon | | `FanHigh` | overrides to show a fan on (high) icon | | `FanLow` | overrides to show a fan on (low) icon | | `FanMedium` | overrides to show a fan on (medium) icon | | `FanOff` | overrides to show a fan off icon | | `HumidifierOff` | overrides to show a humidifier turned off icon | | `HumidifierOn` | overrides to show a humidifier turned on icon | | `HumiditySensor` | overrides to show a humidity sensor icon | | `HumiditySensorOn` | overrides to show a humidity sensor on icon | | `HvacAutoMode` | overrides to show a thermostat in auto mode icon | | `HvacCooling` | overrides to show a thermostat that is cooling icon | | `HvacCoolMode` | overrides to show a thermostat in cool mode icon | | `HvacFanOn` | overrides to show a thermostat with fan blower on only icon | | `HvacHeating` | overrides to show a thermostat that is heating icon | | `HvacHeatMode` | overrides to show a thermostat in heat mode icon | | `HvacOff` | overrides to show a thermostat off icon | | `LightSensor` | overrides to show a light meter off icon | | `LightSensorOn` | overrides to show a light meter on icon | | `Locked` | overrides to show a green lock icon | | `MotionSensor` | overrides to show a motion sensor icon | | `MotionSensorTripped` | overrides to show a motion sensor tripped/activated icon | | `NoImage` | overrides to show no device image icon (was `None` in previous API versions) | | `Opened` | overrides to show a generic sensor off icon (green circle) | | `PowerOff` | overrides to show a power off icon | | `PowerOn` | overrides to show a power on icon | | `SensorOff` | overrides to show a generic sensor off icon (gray circle) | | `SensorOn` | overrides to show a generic sensor on icon (green circle) | | `SensorTripped` | overrides to show a generic sensor tripped icon (red circle) | | `SprinklerOff` | overrides to show a sprinkler off icon | | `SprinklerOn` | overrides to show a sprinkler off icon | | `TemperatureSensor` | overrides to show a temperature sensor icon | | `TemperatureSensorOn` | overrides to show a temperature sensor on icon | | `TimerOff` | overrides to show a timer off icon | | `TimerOn` | overrides to show a timer on icon | | `Unlocked` | overrides to show a red lock icon | | `WindowSensorClosed` | overrides to show a window sensor closed icon (grey circle) | | `WindowSensorOpened` | overrides to show a window sensor opened icon (green circle) | !!! note The following image selectors are available but do not yet have function-specific icons in Indigo Touch and Indigo client UI. Developers are encouraged to use them for automatic future compatibility when the icons are added. | indigo.kStateImageSel | | |-----------------------------------------------------------|---------------------------------------------------------------------------------------------| | Value | Description | | `BatteryCharger` | overrides to show a battery charger icon | | `BatteryChargerOn` | overrides to show a battery charger on icon | | `BatteryLevel` | overrides to show a battery level icon | | `BatteryLevel25` | overrides to show a battery level (25%) icon | | `BatteryLevel50` | overrides to show a battery level (50%) icon | | `BatteryLevel75` | overrides to show a battery level (75%) icon | | `BatteryLevelHigh` | overrides to show a battery level (full) icon | | `BatteryLevelLow` | overrides to show a battery level (low) icon | | `Custom` | overrides to show a plugin defined custom image icon (not yet implemented; shows `NoImage`) | | `Error` | overrides to show an error device image icon | | `WindDirectionSensor` | overrides to show a wind direction sensor icon | | `WindDirectionSensorEast` | overrides to show a wind direction sensor (E) icon | | `WindDirectionSensorNorth` | overrides to show a wind direction sensor (N) icon | | `WindDirectionSensorNorthEast` | overrides to show a wind direction sensor (NE) icon | | `WindDirectionSensorNorthWest` | overrides to show a wind direction sensor (NW) icon | | `WindDirectionSensorSouth` | overrides to show a wind direction sensor (S) icon | | `WindDirectionSensorSouthEast` | overrides to show a wind direction sensor (SE) icon | | `WindDirectionSensorSouthWest` | overrides to show a wind direction sensor (SW) icon | | `WindDirectionSensorWest` | overrides to show a wind direction sensor (W) icon | | `WindSpeedSensor` | overrides to show a wind speed sensor icon | | `WindSpeedSensorHigh` | overrides to show a wind speed sensor (high) icon | | `WindSpeedSensorLow` | overrides to show a wind speed sensor (low) icon | | `WindSpeedSensorMedium` | overrides to show a wind speed sensor (medium) icon | ## Device Base Class Instance Methods { .ref-head-no-code } The following instance methods can be called on device objects. Most are restricted and can only be called from a plugin on a device that the plugin owns. See the Restricted column below for those that are restricted in this way. | Method | Restricted | [Min API](https://www.indigodomo.com/indigo/api_version_chart.html) | Description | |-------------------------------------------------------------------------------|------------|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `replaceOnServer()` | No | 1.0 | Because you can't directly modify a device's properties on the server, you have to get a local instance of the device and modify the writable properties as necessary. You then call this method and the device will be updated on the server and changes will be sent out to all connected clients. A few examples appear below this table. | | `replacePluginPropsOnServer(newPropsdict)` | Yes | 1.0 | If you need to make a change to your plugin's property dictionary that's stored as part of the device (see [About Plugin Properties](base-class.md#about-plugin-properties) below) you just use this method to replace the entire dict with a new one. A typical usage will be to get the property dictionary from the device, make changes to the dict, then use this method to store the new dict. A few examples appear below this table. | | `stateListOrDisplayStateIdChanged()` | Yes | 1.0 | Plugins can subclass the method `getDeviceStateList()` to provide dynamic state list definition information. The default implementation provides a static solution by retrieving the device state list definition from the Devices.xml file. Likewise, the method `getDeviceDisplayStateId()` can be used to dynamically determine which device state should be displayed in the State column of the main device table UI. The problem is that the Indigo Server only calls `getDeviceStateList()` and `getDeviceDisplayStateId()` at very specific times, like when a plugin device dialog is dismissed. So, call `stateListOrDisplayStateIdChanged()` on the device instance you need refreshed at any time and Indigo will then automatically call your plugin's `getDeviceStateList()` and `getDeviceDisplayStateId()` methods (or use the base implementation of looking up the list from Devices.xml) and update the Indigo Server (and all clients). This is particularly useful for plugin updates that need to add new device states to existing device instances created by older versions. In this case, the plugin will need to update the device instances by calling `stateListOrDisplayStateIdChanged()`. A likely place to do this type of instance level upgrading is inside your plugin's `deviceStartComm()` method. | | `setErrorStateOnServer('error string')` | Yes | 1.0 | The supplied string will show in the state column and turn it red. Passing `None` will clear it. | | `updateStateOnServer(key, value, clearErrorState)` | Yes | 1.0 | Use this method to update the value of one of your device's states on the server. The server will propagate the change out to any connected clients and fire any triggers that are defined on that state. Pass "true" (default) or "false" on the clearErrorState parameter (not required) to have the error state of the device (set with `setErrorStateOnServer` above) cleared. | | `updateStateImageOnServer(stateImageSel)` | Yes | 1.18 | Use this method to override which [device state image icon](#state-image-sel-enumeration) is shown for this device on Indigo Touch and the Indigo client UI. The default behavior is for Indigo Server to automatically determine which icon should be shown based on the device class and state value. Only call this method if overriding the default behavior is needed. | **Command Syntax Examples** Certain device properties are writeable for all Indigo devices, including a device's name, description, enabled/disabled state and remote display. You can't do this directly because device instances are read only. You must first get a copy of the device, make changes to the copy, and then send the copy back to the server. For example, ```python # Updating a device's description (the Indigo UI Notes field value.) dev = indigo.devices[123456789] dev.description = "My new description." dev.replaceOnServer() dev = indigo.devices[987654321] dev.name = "My New Name" dev.replaceOnServer() ``` Properties like `Comm Enabled` and `Remote Display` can also be updated using a different approach. ```python # Set Enabled/Disabled state indigo.device.enable(12345678, True) # Set Remote Display Flag indigo.device.displayInRemoteUI(987654321, False) ``` **Command Syntax Examples** Some device base class properties need to be updated differently than the examples above because they can only be updated by the plugin that owns them. Properties like `address` and `version` can be updated by their own plugins. For example, ```python # Updating a plugin device's properties dev = indigo.devices[641471711] new_props = dev.pluginProps new_props['address'] = "abc" new_props['version'] = "123" # Indigo UI Firmware field dev.replacePluginPropsOnServer(new_props) ``` Note that you can't update these in the same way as `name` and `description`. Instead, you change them as `pluginProps` and Indigo migrates these values to the base class props for you. ## About Plugin Properties { .ref-head-no-code } Devices have properties - some are class properties, defined by the class itself. One of the biggest requests we've gotten in the past is some way to add arbitrary properties to a device - so that you could store your own data with the device in the database. And with plugin defined devices, we needed a place to store the properties that you need to operate the device. That's what the `pluginProps` and `globalProps` represent - the additional properties that are not defined by the class. `globalProps` is a dictionary of every additional property defined for the device - each plugin has its own dictionary of props in here which are readable by anyone. `pluginProps` is a shortcut to get to your plugin's props and are only writable by your plugin once the plugin has been created - a script can create a device supplied by your plugin along with the necessary properties, which are passed in on the `create()` method. You should publish the properties necessary to make your device work so that scripters can create your devices. We mentioned before that devices were read-only, and that's true, and that you'd need to use commands in a different command name space. That's ***mostly*** true. Here's one exception to that rule: to change a device's pluginProps (it must be "owned" by your plugin - that is, the pluginId must be set to your id), you use a method that's in the device's class: replacePluginPropsOnServer(). Here's an example: ```python dev=indigo.devices[123] localPropsCopy = dev.pluginProps localPropsCopy['pollInterval'] = 10 dev.replacePluginPropsOnServer(localPropsCopy) ``` You would use this technique if you wanted to just change some of the properties that are already defined. Because this method replaces ALL the properties for your plugin in the device, you can just set them all in one call: ```python dev=indigo.devices[123] dev.replacePluginPropsOnServer({'pollInterval':10,'checkForUpdates':True}) ``` Note, though, that if you have a `` defined for the device, those properties are also stored here - so in order to make sure your device works correctly you must include those properties as well. If you need to update several properties in your props dict, you can use the `update()` method: ```python dev=indigo.devices[123] localPropsCopy = dev.pluginProps localPropsCopy.update({'pollInterval':10, 'checkForUpdates':True}) dev.replacePluginPropsOnServer(localPropsCopy) ``` The `update()` method will change the properties specified, and add the property if it doesn't exist. Now, you might be wondering - why do the extra `localPropsCopy = dev.pluginProps` rather than just modify the props in place: ```python dev=indigo.devices[123] dev.pluginProps.update({'pollInterval':10, 'checkForUpdates':True}) dev.replacePluginPropsOnServer(dev.pluginProps) ``` Because the dev object is read-only - when you reference `dev.pluginProps`, it returns a copy rather than returning a reference to the read-only object. So, in effect, you'd be modifying a copy. But, because you aren't saving a reference to that copy, it goes away since the next time you reference `dev.pluginProps` another copy is made. If you need to just dump all the properties for a device, you can just: ```python dev=indigo.devices[123] dev.replacePluginPropsOnServer(None) ``` That will completely remove your properties from the device. ## About Custom Device States { .ref-head-no-code } If your plugin defines custom devices, they will also need to define a collection of custom states. For instance, let's look at the states defined in a custom device's Devices.xml: ```xml Player Status Changed Player Status is Current Player Status Player Status is Separator String Current Playlist Name Current Playlist Name String Current Album Current Album String Current Artist Current Artist String Current Track Current Track Integer Current Volume Current Volume Boolean Shuffling Shuffling playStatus ``` You'll recall from the [Custom Device Type](../../../plugin-dev/reference/xml/devices.md#devices-xml-custom-device) section of the developers guide, these define the states that are used in various places in the UI and by other objects (triggers, control pages, etc.) So, the question is now that the server understands the structure of your devices' states, how do you change them? It's actually pretty simple. When your plugin detects a change in one of the states, you just call the `updateStateOnServer('id', value='value')` method. Here are some examples for setting the state based on the above state definitions: ```python # assume that someMusicServer represents a device with the above states # to update the volume state someMusicServer.updateStateOnServer('volume', value=50) # to update the track name someMusicServer.updateStateOnServer('track', value='Cluster One') # to update the album name someMusicServer.updateStateOnServer('album', value='The Division Bell') # to update the artist name someMusicServer.updateStateOnServer('artist', value='Pink Floyd') # to update the playStatus someMusicServer.updateStateOnServer('playStatus', value='playing') # to update the playStatus someMusicServer.updateStateOnServer('shuffle', value=True) ``` !!! note For states that have a `` of `Number`, you pass an integer or float; for states that are `Boolean`, you pass Python `True` or `False`; all others pass a string. It's just that simple. This will cause any triggers on the server that are set on your device's states to be fired. It will update any visible control pages. It will show the state that's defined in the `` element in the Mac device table's `State` column. The `updateStateOnServer` method has 3 optional parameters: `decimalPlaces` (integer), `triggerEvents` (boolean), and `uiValue` (string, API v1.6+ only). When updating floating point state values use the `decimalPlaces` parameter to specify the number of fractional digits to store and display. For example: `someThermmostateDevice.updateStateOnServer('mainTemp', value=76.1234, decimalPlaces=2)` instructs the Indigo Server to store and display the value as 76.12. The `triggerEvents` parameter can be set to False (defaults to True) to have the Indigo Server update the state but ignore any Device State Changed triggers that should be processed as a result of the state change. And the optional `uiValue` parameter is used to set UI only display string of the value which is not used in triggers or conditional logic. This is useful for adding units, percent signs, etc.: `dev.updateStateOnServer('sensorValue', 72.3, uiValue=u'72.3 °F')` ## Commands (indigo.device.*) { .ref-head-no-code } ### All Off { .ref-head-no-code } Turns off all devices for all protocols unless a direct parameter is specified. The direct parameter, if specified, will determine which devices will be turned off. In the context of this command, devices are defined as all dimmable (light) and relay (appliance) devices and does not include other device types that may have an on/off state. This command doesn’t work for plugin defined devices regardless of type. **Command Syntax Examples** `indigo.device.allOff()`
`indigo.device.allOff(indigo.kAllDeviceSel.HouseCodeA)`
`indigo.device.allOff(indigo.kAllDeviceSel.Insteon)`
**Parameters** | Parameter | Required | Type | Description | |---------------------------------------------|----------|-----------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------| | direct parameter | Yes | *[kAllDeviceSel](#all-device-selector-enumeration)* | enumerated value to indicate which devices to turn off, all if no parameter is passed - see the `kAllDeviceSel` enumeration for a full description | #### All Device Selector Enumeration { #all-device-selector-enumeration .ref-head-no-code } | `indigo.kAllDeviceSel` | | |---------------------------------------------------|-------------------------------------------------| | Enumerated Type | Description | | `Insteon` | specify all Insteon devices that support ON/OFF | | `X10` | specify all X10 devices that support ON/OFF | | `ZWave` | specify all Z-Wave devices that support ON/OFF | | `HouseCodeA` | specify all X10 devices in house code A | | `HouseCodeB` | specify all X10 devices in house code B | | `HouseCodeC` | specify all X10 devices in house code C | | `HouseCodeD` | specify all X10 devices in house code D | | `HouseCodeE` | specify all X10 devices in house code E | | `HouseCodeF` | specify all X10 devices in house code F | | `HouseCodeG` | specify all X10 devices in house code G | | `HouseCodeH` | specify all X10 devices in house code H | | `HouseCodeI` | specify all X10 devices in house code I | | `HouseCodeJ` | specify all X10 devices in house code J | | `HouseCodeK` | specify all X10 devices in house code K | | `HouseCodeL` | specify all X10 devices in house code L | | `HouseCodeM` | specify all X10 devices in house code M | | `HouseCodeN` | specify all X10 devices in house code N | | `HouseCodeO` | specify all X10 devices in house code O | | `HouseCodeP` | specify all X10 devices in house code P | ### Beep { .ref-head-no-code } !!! note API v1.11+ only Requests that the device make an audible beep or buzz. Only supported by some hardware. **Command Syntax Examples** `indigo.device.beep(123)` **Parameters** | Parameter | Required | Type | Description | |---------------------------------------------|----------|---------|------------------------------| | direct parameter | Yes | integer | id or instance of the device | ### Create { .ref-head-no-code } Create a device. You can create devices that are defined by your plugin, in other plugins, and X10 devices. You can't currently create devices that use the Insteon or Z-Wave protocol because of the complex synchronization needed during definition. Use this method to create ALL device types - it will return a device of the correct class to you based on the arguments. It can be considered the "device" factory method. This method returns a **copy** of the newly created device. **Command Syntax Examples** ```python indigo.device.create(protocol=indigo.kProtocol.Plugin, address='F8', name='Device Name Here', description='Description Here', pluginId='com.mycompany.pluginId', deviceTypeId='myDeviceTypeId', props={'propA':'value', 'propB':'value'}, folder=1234) ``` **Parameters** | Parameter | Required | Type | Description | |-------------------------------------------|----------|--------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `address` | No | string | the address of the X10 device - plugins must set an `address` property in their property dictionary | | `description` | No | string | the description of the device | | `deviceTypeId` | Yes | string | the id of the device type – defined by the plugin or one of the defined X10 devices. | | `folder` | No | integer | id or instance of the folder in which to put the newly created device | | `name` | Yes | string | the name of the device | | `pluginId` | No | string | the plugin ID - defaults to your plugin's id if in a [Server Plugin](../../../plugin-dev/guide.md#indigo-server-plugins) | | `props` | No | dictionary | this is the properties for the device - they will be inserted in to the pluginId's property space as supplied above. If you are creating a device of a type defined in a different plugin, it's that plugin's id and properties. | | `protocol` | Yes | *[kProtocol](#protocol-enumeration)* | the protocol for the device (`indigo.kProtocol.Plugin` or `indigo.kProtocol.X10`) | ### Delete { .ref-head-no-code } Delete the specified device regardless of its type. **Command Syntax Examples** `indigo.device.delete(123)` **Parameters** | Parameter | Required | Type | Description | |---------------------------------------------|----------|---------|----------------------------------------| | direct parameter | Yes | integer | id or instance of the device to delete | ### Duplicate { .ref-head-no-code } Duplicate the specified device regardless of the type. This method returns a copy of the new device. **Command Syntax Examples** `indigo.device.duplicate(123, duplicateName='New Name')` **Parameters** | Parameter | Required | Type | Description | |---------------------------------------------|----------|---------|-------------------------------------------| | direct parameter | Yes | integer | id or instance of the device to duplicate | | `duplicateName` | No | string | name for the newly duplicated device | ### Enable/Disable { .ca #enable-disable } Enable/Disable the specified device regardless of the type. **Command Syntax Examples** ```python indigo.device.enable(123, value=True) # enable indigo.device.enable(123, value=False) # disable ``` **Parameters** | Parameter | Required | Type | Description | |---------------------------------------------|----------|---------|------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device to enable/disable | | `value` | No | boolean | `True` to enable, `False` to disable | ### Get Dependencies { .ref-head-no-code } Return an indigo.Dict with all the dependencies on this device. **Command Syntax Examples** `indigo.device.getDependencies(123)` **Parameters** | Parameter | Required | Type | Description | |---------------------------------------------|----------|---------|-----------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device to get the dependencies for. | The dictionary will look something like this: ```python >>> print( )indigo.device.getDependencies(91776575)) Data : (dict) actionGroups : (list) Data : (dict) ID : 1280166770 (integer) Name : Set var to device state (string) controlPages : (list) devices : (list) schedules : (list) triggers : (list) Data : (dict) ID : 106487666 (integer) Name : Thermostat condition test (string) variables : (list) ``` So, the dictionary will have 6 top-level keys: "actionGroups", "controlPages", "devices", "schedules", "triggers", and "variables". Each one of those keys will return a list object. Inside that list object will be multiple dicts, one for each dependency (or an empty list if there are none). Each dependency dictionary has two keys: "ID" which is the unique id and "Name" which is the name of the object. ### Get Group List { .ref-head-no-code } !!! note API v1.14+ only Return an indigo.List with all device IDs in a device group. **Command Syntax Examples** Returns an indigo.List of all devices grouped with dev `indigo.device.getGroupList(123)` **Parameters** | Parameter | Required | Type | Description | |---------------------------------------------|----------|---------|--------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of any device that belongs to a device group. | getGroupList() is useful to get the main/root device of a device group. Some properties, such as batteryLevel, only exist on the main/root device. In this example we log the batteryLevel for a module given any devices that belong to its group: ```python groupList = indigo.device.getGroupList(devIdOrInstance) rootDevice = indigo.devices[groupList[0]] indigo.server.log('battery level is: ' + str(rootDevice.batteryLevel)) ``` See also `indigo.device.groupWithDevice()` and `indigo.device.ungroupDevice()`. ### Group With Device { .ref-head-no-code } To group two or more devices together, use the `indigo.device.groupWithDevice()` command. The parameters are the Indigo Device object IDs of the devices to be grouped. **Note if you have the device dialog UI open, it will not dynamically update, and you shouldn’t call either method if the device factory UI is open.** **Command Syntax Examples** `indigo.device.groupWithDevice(dev_1, dev_2)` **Parameters** | Parameter | Required | Type | Description | |-----------------------------------------------------|----------|---------|-----------------------------------------------------------------------| | direct parameter (dev_1) | Yes | integer | id, name or instance of a device that will belong to the group. | | direct parameter (dev_2) | Yes | integer | id, name or instance of another device that will belong to the group. | For example, if you want to group devices 123 and 456, you would use `indigo.device.groupWithDevice(123, 456)`. There is no message printed to the events log if the devices grouped together successfully. If you want to add device 789 to the group, you would use `indigo.device.groupWithDevice(456, 789)`. This is a great way to bring together different devices that have a common thread, but bear in mind that it's best not to try to group too many devices together. See also `indigo.device.ungroupDevice()` and `indigo.device.getGroupList()`. ### Move To Folder { .ref-head-no-code } Use this command to move the device to a different folder. **Command Syntax Examples** `indigo.device.moveToFolder(123, value=987)` **Parameters** | Parameter | Required | Type | Description | |---------------------------------------------|----------|---------|----------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `value` | Yes | integer | id or instance of the folder to move the device to | ### Ping Device { .ref-head-no-code } !!! note API v1.16+ only Sends the Z-Wave or Insteon module a ping command and measures the round trip ACK time. Returns a dict containing the `Success` and `TimeDelta` (milliseconds) result. **Command Syntax Examples** ```python result = indigo.device.ping(123, suppressLogging=True) if result["Success"]: indigo.server.log("%.3f seconds ping for %s" % (result["TimeDelta"]/1000.0, dev.name)) else: indigo.server.log("ping failed for %s" % dev.name, isError=True) ``` **Parameters** | Parameter | Required | Type | Description | |----------------------------------------------|----------|---------|---------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `suppressLogging` | No | boolean | `True` to keep the request from being logged into the event log window (default is `False`) | ### Remove Delayed Actions { .ref-head-no-code } This command will remove delayed actions for the specified device. **Command Syntax Examples** `indigo.device.removeDelayedActions(123)` **Parameters** | Parameter | Required | Type | Description | |---------------------------------------------|----------|---------|------------------------------| | direct parameter | No | integer | id or instance of the device | ### Reset Accumulated Energy Total { .ref-head-no-code } !!! note API v1.11+ only Resets the `energyAccumTotal` and `energyAccumTimeDelta` values and changes the `energyAccumBaseTime` to the server's current datetime. **Command Syntax Examples** `indigo.device.resetEnergyAccumTotal(123)` **Parameters** | Parameter | Required | Type | Description | |---------------------------------------------|----------|---------|------------------------------| | direct parameter | Yes | integer | id or instance of the device | ### Set Remote Display { .ref-head-no-code } Use this command to set the remote display flag for the folder. **Command Syntax Examples** `indigo.device.displayInRemoteUI(123, value=True)` **Parameters** | Parameter | Required | Type | Description | |---------------------------------------------|----------|---------|--------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `value` | Yes | boolean | True to display the device on remote user interfaces or False to hide it | ### Status Request { .ref-head-no-code } This tells IndigoServer to send a status request command to the specified device and refresh its status. **Command Syntax Examples** `indigo.device.statusRequest(123)`
`indigo.device.statusRequest(123, suppressLogging=True)`
**Parameters** | Parameter | Required | Type | Description | |----------------------------------------------|----------|---------|---------------------------------------------------------------------------------------------| | direct parameter | Yes | integer | the id of the device | | `suppressLogging` | No | boolean | `True` to keep the request from being logged into the event log window (default is `False`) | ### Toggle { .ref-head-no-code } This tells IndigoServer to toggle a device from on to off or vice versa depending on its current state. This command only works for device types that can be turned on and off. **Command Syntax Examples** `indigo.device.toggle(123)`
`indigo.device.toggle(123, delay=10, duration=300)`
**Parameters** | Parameter | Required | Type | Description | |---------------------------------------------|----------|---------|-------------------------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `delay` | No | integer | number of seconds to delay before toggling the device | | `duration` | No | integer | number of seconds delay before the device toggles back to it’s original state | ### Turn Off { .ref-head-no-code } This tells IndigoServer to turn off a device. This command only works for device types that can be turned on and off. **Command Syntax Examples** `indigo.device.turnOff(123)`
`indigo.device.turnOff(123, delay=10, duration=300)`
**Parameters** | Parameter | Required | Type | Description | |---------------------------------------------|----------|---------|----------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `delay` | No | integer | number of seconds to delay before turning off the device | | `duration` | No | integer | number of seconds delay before the device turns back on | ### Turn On { .ref-head-no-code } This tells IndigoServer to turn on a device. This command only works for device types that can be turned on and off. **Command Syntax Examples** `indigo.device.turnOn(123)`
`indigo.device.turnOn(123, delay=10, duration=300)`
**Parameters** | Parameter | Required | Type | Description | |---------------------------------------------|----------|---------|----------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `delay` | No | integer | number of seconds to delay before turning on the device | | `duration` | No | integer | number of seconds delay before the device turns back off | ### Ungroup With Device { .ref-head-no-code } If you want to remove a device from a group, use the `indigo.device.ungroupDevice()` command. Use this command with the ID of the device you want removed from the group.**Note if you have the device dialog UI open, it will not dynamically update, and you shouldn’t call either method if the device factory UI is open.** **Command Syntax Examples** `indigo.device.ungroupDevice(dev)` **Parameters** | Parameter | Required | Type | Description | |---------------------------------------------|----------|---------|------------------------------------------------------------------| | direct parameter | Yes | integer | id, name or instance of the device to be removed from the group. | If successful, nothing will be printed to the events log. See also `indigo.device.groupWithDevice()` and `indigo.device.getGroupList()`. ### Unlock { .ref-head-no-code } !!! note API v2.0+ only This tells IndigoServer to unlock a device. This command only works for relay device types that have `pluginProps["IsLockSubType"]` set to True. **Command Syntax Examples** `indigo.device.unlock(123)`
`indigo.device.unlock(123, delay=10, duration=300)`
**Parameters** | Parameter | Required | Type | Description | |---------------------------------------------|----------|---------|---------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `delay` | No | integer | number of seconds to delay before unlocking the device | | `duration` | No | integer | number of seconds delay before the device automatically locks | ### Lock { .ref-head-no-code } !!! note API v2.0+ only This tells IndigoServer to lock a device. This command only works for relay device types that have the property `pluginProps["IsLockSubType"]` set to True. **Command Syntax Examples** `indigo.device.lock(123)`
`indigo.device.lock(123, delay=10, duration=300)`
**Parameters** | Parameter | Required | Type | Description | |---------------------------------------------|----------|---------|-----------------------------------------------------------------| | direct parameter | Yes | integer | id or instance of the device | | `delay` | No | integer | number of seconds to delay before locking the device | | `duration` | No | integer | number of seconds delay before the device automatically unlocks | **Command Syntax Examples** ```python # Creating a device myDevice = indigo.device.create(protocol=indigo.kProtocol.X10, name="Office Lamp", description="X10 Lamp module", address="F7", deviceTypeId="LampLinc Plus Plug-In Dimmer") # Getting a copy of a device myDevice = indigo.devices[123] # Logging a message if it’s an X10 device if myDevice.protocol == indigo.kProtocol.X10: indigo.server.log("device is an X10 device") # Logging a message if it’s showing in Indigo Touch if myDevice.remoteDisplay: indigo.server.log("device is showing in Indigo Touch") # Setting the folder ID that the device is in indigo.device.setFolder(myDevice, 987) # Turning off all devices (dimmer and relay) indigo.device.allOff() # Turning off all devices in X10 house code A indigo.device.allOff(indigo.kAllDeviceSel.HouseCodeA) # Turning off all Insteon devices indigo.device.allOff(indigo.kAllDeviceSel.Insteon) ``` --- Device Capabilities (https://docs.indigodomo.com/2025.2/scripting/reference/devices/capabilities/) --- # Device Capabilities Two orthogonal questions decide what a device UI should show: - **What *kind* of device is it?** — its **class** (subclass: `DimmerDevice`, `RelayDevice`, `ThermostatDevice`, …). Good for browsing and organizing ("show my thermostats"). - **What can you *do* with it?** — its **capabilities**, expressed by the device's `supports*` attributes (and, for some, its subclass or plugin-defined properties). This is the axis that decides which **controls** to render. Class alone is not enough. Capabilities cut **across** classes — on/off (`onState`) spans relays, dimmers, sensors, sprinklers, and speed controls; color and white-temperature split *dimmers*; cool setpoint and fan mode split *thermostats*. And plugin-defined **custom** devices (a plain `Device` with no subclass) can't be recognized by class at all. So for **control UI, capability is the primary axis and class is the secondary (browse) axis.** This page is the **canonical capability → control reference.** [The per-subclass reference](../device-subclasses/index.md) documents each type's full property, state, and command set; this page is the cross-cut: which capability gates which control, and the command that drives it. ## Capability reference Each capability is gated by a **signal** on the device — an IOM `supports*` attribute, the device's subclass, or a plugin-defined property. Render the control only when the signal is present. | Capability | Gating signal | Control | Command | |---|---|---|---| | On / off | `dev.supportsOnState` (and writable — inherent for relay/dimmer; a sensor must allow user on/off changes) | on/off toggle | `indigo.device.turnOn` / `turnOff` / `toggle` | | Brightness (dimming) | `DimmerDevice` subclass | brightness slider | `indigo.dimmer.setBrightness` / `brighten` / `dim` | | Color (RGB) | `dev.supportsColor` / `dev.supportsRGB` | RGB color picker | `indigo.dimmer.setColorLevels` (red/green/blue) | | White level | `dev.supportsWhite` | white slider | `indigo.dimmer.setColorLevels` (`whiteLevel`) | | Two white levels | `dev.supportsTwoWhiteLevels` | warm + cool white sliders | `indigo.dimmer.setColorLevels` (`whiteLevel` + `whiteLevel2`) | | White temperature | `dev.supportsWhiteTemperature` (range from `WhiteTemperatureMin` / `WhiteTemperatureMax` plugin props) | white-temperature slider | `indigo.dimmer.setColorLevels` (`whiteTemperature`, 1200–15000 K) | | Cool setpoint | `dev.supportsCoolSetpoint` | cool setpoint stepper | `indigo.thermostat.setCoolSetpoint` / `increaseCoolSetpoint` / `decreaseCoolSetpoint` | | Heat setpoint | `dev.supportsHeatSetpoint` | heat setpoint stepper | `indigo.thermostat.setHeatSetpoint` / `increaseHeatSetpoint` / `decreaseHeatSetpoint` | | HVAC mode | `dev.supportsHvacOperationMode` | mode selector (off / heat / cool / auto) | `indigo.thermostat.setHvacMode` | | Fan mode | `dev.supportsHvacFanMode` | fan mode selector (auto / always-on) | `indigo.thermostat.setFanMode` | | Speed control | `SpeedControlDevice` subclass (see `speedIndexCount`) | speed selector | `indigo.speedcontrol.setSpeedIndex` / `setSpeedLevel` | | Sprinkler zones | `SprinklerDevice` subclass | zone + schedule controls | `indigo.sprinkler.run` / `stop` / `pause` / `resume` / `nextZone` / `previousZone` / `setActiveZone` | | I/O outputs | `MultiIODevice` subclass (binary outputs) | binary-output bank | `indigo.iodevice.setBinaryOutput` | | Status request | `dev.supportsStatusRequest` | refresh / status-request button | `indigo.device.statusRequest` | | Energy meter | `SupportsEnergyMeter` plugin prop | accumulated-energy readout + reset | `indigo.device.resetEnergyAccumTotal` | | Power meter | `SupportsPowerMeter` plugin prop | current-power readout | *(read the `curEnergyLevel` state)* | | Battery | `batteryLevel` state present | battery-level readout | *(read the `batteryLevel` state)* | | Sensor value | `dev.supportsSensorValue` | sensor-value readout | *(read the `sensorValue` state)* | Readout-only rows (power, battery, sensor value) carry no write command — a detail view shows them, a picker doesn't offer them. ## On/off presentation modifiers Some devices are on/off devices with a **domain label** rather than a distinct capability. These **relabel** the on/off control; they add no new controls or commands: - **Lock / unlock** — the `IsLockSubType` plugin property. The on/off control reads *Lock / Unlock*. (Dedicated `indigo.device.lock` / `unlock` commands also exist.) - **Open / close** — the `IsOpenCloseSubType` plugin property. The on/off control reads *Open / Close*. A client MAY honor these labels or MAY present a plain on/off — both are correct, since a lock or blind/shade/door is fundamentally an on/off device. ## Custom (plugin) devices A plugin **custom** device is a plain `Device` with no subclass. In practice, a plugin that wants on/off implements a **relay** device, so a custom device does not advertise controllable capabilities. Treat custom devices as **information-only** (states and details) from a presentation perspective: each will likely provide actions to support controlling the device. A custom device is still enumerable and browsable by class. ## Using the model The capability model answers both directions of device UI from one mapping: - **Which devices does a control offer?** *(forward)* — a brightness control offers only devices with the *brightness* capability; a color control only *color*-capable ones. Filter the device list by the capability the control requires. - **Which controls does a device show?** *(inverse)* — intersect the device's capability set with the table above and render the controls whose capability the device has. Keeping both directions on **one** mapping is what keeps every client — the macOS client, the iOS and web clients, and any MCP-driven UI — in agreement about "can this device do X." ## See also - [Device Subclasses](../device-subclasses/index.md) — per-type properties, states, and commands. - [Device base class](base-class.md) — the shared `indigo.device.*` commands, plugin properties, and custom states. --- Dictionary Representation (https://docs.indigodomo.com/2025.2/scripting/reference/devices/dictionary/) --- # Generating a Dictionary for a device { .ref-head-no-code } Sometimes, it's useful to get a python dictionary that has all properties and states of a device, perhaps for conversion to JSON to send to some other system. This can easily be done for any device regardless of type: ```text >>> my_device = indigo.devices[854505717] # "017 - Door/Window Sensor" >>> python_dict = dict(my_device) >>> print(json.dumps(python_dict, indent=4, cls=indigo.utils.IndigoJSONEncoder)) { "allowOnStateChange": false, "protocol": "ZWave", "configured": true, "states": { "onOffState": true, "batteryLevel.ui": "100%", "batteryLevel": 100 }, "subType": "", "globalProps": { "com.perceptiveautomation.indigoplugin.zwave": { "zwEncryptClassCmdMapStr": "- none -", "zwConfigVals": {}, "zwClassInstanceCountMap": {}, "zwFeatureListStr": "routing, battery, beaming, waking", "zwShowWakeIntervalUI": true, "zwModelId": 0, "zwClassIds": [ 4, 7, 1 ], "zwModelName": "Notification Sensor", "zwShowDumpDevToLog": false, "zwEndpointClassMapStr": "- none -", "zwLibType": 3, "SupportsBatteryLevel": true, "zwShowSubmitModelInfoUI": true, "zwEncryptionStatusStr": "Not Supported", "zwProtoVersMajor": 4, "zwEndpointDevTypeMap": {}, "userWakeInterval": 60, "indigoObjVersion": 10, "zwClassInstanceCountMapStr": "- none -", "version": "5.01", "zwClassCmdMap": { "c134": 1, "c90": 1, "c133": 1, "c132": 2, "c89": 1, "c94": 1, "c128": 1, "c122": 1, "c115": 1, "c114": 1, "c113": 1, "c32": 1 }, "SupportsOnState": true, "zwClassCmdMapStr": "20v1 80v1 84v2 85v1 86v1 71v1 72v1 73v1 59v1 5Av1 7Av1 5Ev1", "userEnergyPollingEnabled": false, "zwShowMainUI": true, "zwAppVersMinor": 1, "userPollInterval": 0, "zwDevSubIndex": 0, "zwShowManualModifyConfigParmUI": true, "zwManufactureName": "Unknown", "zwEncryptClassCmdMap": {}, "address": 17, "zwEndpointClassMap": {}, "zwConfigValsStr": "- none -", "zwClassName": "Notification Sensor", "zwEndpointDevTypeMapStr": "- none -", "zwNodeNeighborsStr": "1, 15, 16", "zwModelDefnVers": 0, "zwManufactureId": 0, "SupportsSensorValue": false, "zwAssociationsMap": { "g1": [ 1 ] }, "zwShowPollingUI": false, "zwAssociationsMapStr": "1:[1]", "zwShowEnergyPollingUI": false, "zwWakeInterval": 60, "userPollAfterActivity": true, "zwClassCmdBase": 0, "zwAppVersMajor": 5, "zwProtoVersMinor": 5, "zwNodeNeighbors": [ 1, 15, 16 ], "userPollingEnabled": true } }, "pluginProps": {}, "lastSuccessfulComm": "2021-11-18T10:56:35", "buttonGroupCount": 0, "id": 854505717, "supportsAllOff": false, "errorState": "", "remoteDisplay": true, "energyAccumBaseTime": null, "displayStateValUi": "on", "subModel": "", "allowSensorValueChange": false, "version": "5.01", "energyAccumTimeDelta": null, "displayStateId": "onOffState", "batteryLevel": 100, "supportsStatusRequest": true, "supportsSensorValue": false, "ownerProps": { "zwEncryptClassCmdMapStr": "- none -", "zwConfigVals": {}, "zwClassInstanceCountMap": {}, "zwFeatureListStr": "routing, battery, beaming, waking", "zwShowWakeIntervalUI": true, "zwModelId": 0, "zwClassIds": [ 4, 7, 1 ], "zwModelName": "Notification Sensor", "zwShowDumpDevToLog": false, "zwEndpointClassMapStr": "- none -", "zwLibType": 3, "SupportsBatteryLevel": true, "zwShowSubmitModelInfoUI": true, "zwEncryptionStatusStr": "Not Supported", "zwProtoVersMajor": 4, "zwEndpointDevTypeMap": {}, "userWakeInterval": 60, "indigoObjVersion": 10, "zwClassInstanceCountMapStr": "- none -", "version": "5.01", "zwClassCmdMap": { "c134": 1, "c90": 1, "c133": 1, "c132": 2, "c89": 1, "c94": 1, "c128": 1, "c122": 1, "c115": 1, "c114": 1, "c113": 1, "c32": 1 }, "SupportsOnState": true, "zwClassCmdMapStr": "20v1 80v1 84v2 85v1 86v1 71v1 72v1 73v1 59v1 5Av1 7Av1 5Ev1", "userEnergyPollingEnabled": false, "zwShowMainUI": true, "zwAppVersMinor": 1, "userPollInterval": 0, "zwDevSubIndex": 0, "zwShowManualModifyConfigParmUI": true, "zwManufactureName": "Unknown", "zwEncryptClassCmdMap": {}, "address": 17, "zwEndpointClassMap": {}, "zwConfigValsStr": "- none -", "zwClassName": "Notification Sensor", "zwEndpointDevTypeMapStr": "- none -", "zwNodeNeighborsStr": "1, 15, 16", "zwModelDefnVers": 0, "zwManufactureId": 0, "SupportsSensorValue": false, "zwAssociationsMap": { "g1": [ 1 ] }, "zwShowPollingUI": false, "zwAssociationsMapStr": "1:[1]", "zwShowEnergyPollingUI": false, "zwWakeInterval": 60, "userPollAfterActivity": true, "zwClassCmdBase": 0, "zwAppVersMajor": 5, "zwProtoVersMinor": 5, "zwNodeNeighbors": [ 1, 15, 16 ], "userPollingEnabled": true }, "description": "", "onState": true, "energyAccumTotal": null, "address": "17", "sharedProps": {}, "folderId": 1145028206, "supportsOnState": true, "sensorValue": null, "energyCurLevel": null, "name": "017 - Door/Window Sensor", "lastChanged": "2021-11-18T10:56:35", "enabled": true, "pluginId": "com.perceptiveautomation.indigoplugin.zwave", "deviceTypeId": "zwOnOffSensorType", "supportsAllLightsOnOff": false, "displayStateImageSel": "SensorOn", "displayStateValRaw": true, "model": "Notification Sensor" } ``` This will be a python dictionary with all details about a device. One other feature to note here: by default, Python datetime objects are not serializable by the JSON module (odd oversight we believe). We have included a JSON encoder class that will solve this problem: [`indigo.utils.IndigoJSONEncoder`](../utils.md#indigojsonencoder). Just specify that as the cls parameter to a JSON dump call and any datetime class that's encountered during JSON encoding will be correctly converted to a string in the resulting JSON string: json.dumps(python_dict_with_datetime, cls=indigo.utils.JSONDateEncoder) --- Indigo User Guide (https://docs.indigodomo.com/2025.2/user/) --- # Indigo User Guide Welcome to the Indigo {{ version }} User Guide — everything you need to set up, use, and maintain Indigo, no programming required. ## New to Indigo? Start with the [Getting Started guide](getting-started/index.md), which walks you through installing Indigo, connecting your hardware interface, adding your first devices, and creating your first automations. Then read the [Concept Overview](concepts/index.md) to understand Indigo's building blocks — devices, triggers, schedules, action groups, variables, and control pages — and take the [tour of the Mac client](mac-client/index.md). ## Setting up your hardware Indigo supports several hardware interfaces, which can be active simultaneously. Read the guide for the technology you use: [Z-Wave®](interfaces/z-wave/index.md), [Insteon](interfaces/insteon/index.md), or [X10](interfaces/x10/index.md). The [Virtual Devices Interface](interfaces/virtual-devices.md) helps you integrate devices from different technologies — and 3rd party plugins — together. ## Accessing Indigo remotely The [Indigo Web Server](remote-access/web-server.md) and [Indigo Touch for Web](remote-access/touch-for-web.md) put your home in your browser; [Indigo Touch for iOS](https://www.indigodomo.com/touch.html) puts it on your iPhone, iPad, and Apple Watch; and the [Indigo Reflector](remote-access/reflector.md) makes them reachable from anywhere without router configuration. ## Going further The Advanced Automation section covers techniques that make automations dynamic: [substitutions](automation/substitutions.md), [event data passing](automation/event-data.md), [fetching URLs from actions](automation/get-contents-of-url.md), and [Apple Shortcuts integration](automation/apple-shortcuts.md). Indigo also ships with a collection of [bundled plugins](../plugins/index.md) — Alexa voice control, email, weather, timers, and more. ## Maintaining your system When the time comes: [upgrading Indigo](maintenance/upgrading.md), [moving to another Mac](maintenance/moving.md), [transferring a license](maintenance/license-transfer.md), or [uninstalling](maintenance/uninstalling.md). ## Beyond the User Guide Want to script Indigo with Python, build a plugin, or integrate an external system? See the [Scripting](../scripting/index.md), [Plugin Development](../plugin-dev/index.md), and [Integration APIs](../api/index.md) sections. --- Glossary (https://docs.indigodomo.com/2025.2/user/glossary/) --- # Glossary of Terms There tends to be a lot of home automation specific and other jargon used in documents, wiki articles, and in the support forums. This is where we'll capture those so it's easy to find the definition you need. | Term | Definition | |---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Action | Indigo actions are discrete tasks that cause things to happen -- such as turning on a light, changing a setpoint, writing information to the log, and so on. Indigo ships with many standard actions and plugin developers can create actions that apply to their plugins. | | Action Group | [Action groups](concepts/actions.md#action-groups) are collections of actions that may be reused (and modified) easily between multiple triggers, schedules, and control pages and executed via various clients (the Mac Client, the Indigo Web Server (IWS) web pages, Indigo Touch, etc). | | API | An API (Application Programming Interface) is a set of rules and protocols that allow different applications to exchange information with one another. It helps developers and users to integrate, leverage, or share data and processes from other systems without having to create their own. | | AppleScript | A macOS scripting language that allows applications to be controlled and automated. AppleScript is considered a legacy scripting language in Indigo. | | Association | A process used to link two or more Z-Wave devices together so they essentially react as a single device. | | Control Page | [Control pages](concepts/control-pages.md#control-pages) are user-created interfaces to control their Indigo system - for instance you could create a graphical floor plan with light icons in the various rooms. | | Condition | In Indigo, a condition is a logical test that's used to determine whether something should happen. For example, you might only want something to happen at nighttime, or only when all the windows are closed. | | Controller | In Indigo, a controller is a device that controls another device. Z-Wave controllers include various peripherals such as USB sticks -- which send commands from Indigo to a device (like a dimmer) or from the device to Indigo. A controller may also be a responder (i.e. Insteon KeypadLinc). We've adopted the term from various Insteon documentation. | | Developer | A person who writes plugins or scripts for the Indigo Server (IS). | | Exclude/Exclusion | Used to remove a device from a Z-Wave controller. | | Include/Inclusion | Used to add a device to a Z-Wave controller. | | Insteon | A home automation protocol developed by SmartHome that uses a dual-band (powerline + RF) mesh network so devices can communicate directly with one another. Indigo supports Insteon devices via a USB PLM (PowerLinc Modem). | | Insteon Link | A connection between two Insteon devices such that one device controls the other based on some input - a button press, etc. | | Device | A [device](concepts/devices.md#devices) is any "thing" that Indigo can interact with - usually it's some kind of hardware (light switch, appliance module, motion sensor, etc.), but devices can also be other non-hardware things (iTunes server, calendar, etc.). Devices can also be "virtual" objects supported by plugins that have the potential to do all kinds of useful things. | | I/O Device | Any device that has binary or analog inputs or outputs. These devices are generally used to interface at a low-level to other devices (security systems, sensors, etc). | | Indigo Object Model | The Indigo Object Model (IOM) is how the various objects in Indigo are modeled in Python objects for scripters and plugin developers. | | Interface | Indigo provides support for several different interface types, including: Z-Wave, Insteon/X10 Powerline, X10 RF, and Virtual Devices (software-based interface). Additional interfaces may be supported through user-submitted plugins. | | JSON | JSON (JavaScript Object Notation) is a lightweight, human-readable data format used to represent structured information as key-value pairs and lists. It is widely used for data exchange between Indigo plugins, scripts, and external APIs and web services. | | Node | A node is used to refer to a single device on a Z-Wave network. Nodes are assigned a unique node number by the Z-Wave controller. | | Object | In Indigo, an object is any item managed by the server — such as a device, trigger, schedule, action group, control page, or variable. Objects are accessible to scripters and plugin developers via the [Indigo Object Model (IOM)](../scripting/iom-concepts.md). | | Plugin | Indigo supports ways to extend its functionality such as Indigo Server (IS) plugins. IS plugins can extend Indigo by adding additional devices types, trigger events, and actions. | | Python | The programming language used to write Indigo scripts and plugins. Indigo embeds a Python interpreter, allowing scripters and developers to automate logic, interact with the [Indigo Object Model (IOM)](../scripting/iom-concepts.md), and extend Indigo's capabilities beyond its built-in features. | | Reflector | Indigo's secure tunnel service for remote access without the need for a VPN. | | Responder | In Indigo terminology, it's a device that responds to commands. A responder may also be a controller (i.e. Insteon KeypadLinc). We've adopted the term from various Insteon documentation. | | REST | REST (Representational State Transfer) is an architectural style for web APIs that uses standard HTTP methods (GET, POST, PUT, DELETE) to interact with resources. The Indigo Web Server (IWS) exposes a RESTful API that allows external applications and scripts to query and control Indigo objects over the network. Indigo's REST API has been deprecated in favor of the websocket and HTTP APIs. | | RF | RF (Radio Frequency) interfaces use wireless radio waves to communicate with the Indigo Server. | | Schedule | A [schedule](concepts/schedules.md#schedules) is similar to a trigger, but the event that causes the execution of the actions is a temporal event of some kind. Either a fixed point in time (5/2/2011 at 1:00pm) or more likely some repeating time (every day at 1:00pm). | | Scripter | Someone who uses Python scripts to implement automation logic in Indigo via embedded or file-based script actions and script conditions. | | Sync/Synchronize | A process initiated in Indigo to refresh a device's settings, states and other information in the Indigo server. Not all devices support a synchronization and battery devices that support synchronization will be updated at the next polling time. | | Trigger | A [trigger](concepts/triggers.md#triggers) is generally some kind of "event" that occurs. Indigo can use that event to execute actions in response. For example, turn on an exhaust fan if the humidity exceeds a certain level. | | Variable | A [variable](concepts/variables.md#variables) is a place where your home automation logic can store information that changes during the normal operation of your home and that can be used in other parts of your system: for instance, you can have a variable that represents whether your home is occupied or not - then you can have special automation logic that takes place when that variable changes. Indigo variables store values as plain text. | | Webhook | an HTTP request that lets external services trigger Indigo actions. | | X10 | One of the earliest home automation protocols, X10 sends on/off and dim commands over a home's existing electrical wiring (powerline). Indigo supports X10 devices through compatible controllers. X10 is considered a legacy protocol in Indigo. | | XML | XML (Extensible Markup Language) is a structured text format that uses nested tags to represent data in a human- and machine-readable way. Indigo uses XML extensively for plugin device definitions, action configurations, and preferences files. | | Z-Wave | A low-power wireless mesh networking protocol designed specifically for home automation. Z-Wave devices communicate in the sub-GHz band (908 MHz in North America) and support two-way communication. Indigo supports Z-Wave devices via a Z-Wave USB controller (often called a Z-Stick). | --- Apple Shortcuts (https://docs.indigodomo.com/2025.2/user/automation/apple-shortcuts/) --- # Using Apple Shortcuts with Indigo !!! abstract "In this guide" How to use Apple Shortcuts with Indigo's HTTP API to control devices, run action groups, and query status from iOS, iPadOS, and macOS. Covers API key setup, building GET and POST shortcut actions, using the Indigo Reflector for remote access, and a known compatibility issue with macOS Sonoma 14.7.5. Apple's Shortcuts application allows you to create custom workflows on macOS, iOS and iPadOS (you can't currently run shortcuts on tvOS). You can also use Apple Shortcuts with Indigo to call many functions and use Indigo events to fire Apple shortcuts. Using Indigo's [HTTP API](../../api/http.md) with Apple's Shortcuts app is easy and there are different methods to accomplish this (this document uses Indigo's v2 API). !!! warning Unfortunately, it looks like [Apple broke the shortcuts command line tool](https://discussions.apple.com/thread/256038658?sortBy=rank) (which we use to run shortcuts) in Sonoma 14.7.5. If you rely on shortcuts in Indigo you'll probably want to skip that release. We tested on Sequoia 15.3.1 and it works correctly. If you'd like to ask questions or share your Shortcuts success stories with others, use the [Apple Shortcuts Forum](https://forums.indigodomo.com/viewforum.php?f=298) * The shortcuts app for macOS is only available for Monterey (12.x) and newer versions. It has been available for iOS and iPadOS since version 12. ## Obtaining an API Key In order to use authorization keys as used in the following examples, you'll need an active Indigo Up-To-Date subscription and an activated reflector. 1. Log into [your Indigo account](https://www.indigodomo.com/account/authorizations)'s Authorizations Page. 1. Go to `Add an API Key`. 1. Select the Server that you want to use to accept the key. 1. Click on the `Add API Key` button. !!! important in order to use API Key authentication, you MUST have enabled *`Enable OAuth and API Key authentication`* in the Indigo ["Start Local Server"](../getting-started/installation.md#starting-indigo-server) dialog box.
## Building Your First Shortcut Building your shortcut in the Shortcuts app is largely the same whether you do it on macOS or iOS, and -- depending on the type of activity you want to initiate -- only takes a couple of steps. The information you'll need depends on what you want to do, but all the API events are constructed in the same way. For this example, we'll use the macOS Shortcuts App to build our shortcut (you can also do it on iOS and iPadOS). At first, this looks like a lot of steps, but there's actually only a few pieces of information required. **All values below are entered without quotes.** For our first example, we'll perform a simple Indigo Device Toggle. You'll need: - The base URL: `https://MY-REFLECTOR-NAME.indigodomo.net/v2/api/command/` (replace `MY-REFLECTOR-NAME` with the name of your active reflector), - Your authentication key: `c2xr7q35-9385-10f9-3652-12z765j99vnv` (replace with your valid key. This one is fake.), - The command you want to execute -- such as: `indigo.device.toggle`, - The Indigo ID of the device (or action, or variable, etc.) you want the command to apply to: `123456`, and - Any parameters you want to include (some are optional, some are required.) ### The Steps 1. New shortcut (`+` at the top) 1. In the search box on the right side, type "Get Contents of URL". 1. Drag (or double-click) the action to add it to the editor. 1. Click on the `URL` field and enter the base URL above. If using an external IP address or reflector make sure to use `https:` and not just `http:`. 1. Click on "Show More". 1. Select Method: `Post`. 1. Expand the Headers section by clicking on the `>`. 1. Within the Headers section, click the `+`. 1. Under Key: `Authorization`. 1. Under Value: `Bearer 2x7a35-9385-10f9-3652-12765:99vnv`. (The word `Bearer`, one space and then your API key.) 1. Make sure that `Request Body` is set to `JSON`. 1. Within the Request Body section, click the `+`. 1. Under Key: `message` (make sure the `Type` is set to `Text`). 1. Under Value: `indigo.device.toggle`. 1. Within the Request Body section, click the `+`. 1. Under Key: `objectId`. 1. Under Value: `123456` (your device ID, make sure the `Type` is set to `Number`.) At the top of the Shortcut editor window, you can change "Get Contents of URL" to something descriptive like "Toggle Living Room Lamp". Likewise, you can click on the icon to customize it to your taste. Finally, if you'd like, you can add the Shortcut to your dock (or home screen on iOS), a shortcuts widget, or simply fire them from within the Shortcuts app. ![Shortcuts Get Contents of URL Image](../../images/screenshot_2023-02-24_at_12.31.40_pm.png) Once you've built and tested your first Shortcut, it's easy to duplicate and edit additional shortcuts. All HTTP API commands should work in a similar way. ### Optional Parameters In order to add parameters to the command (for those calls that take parameters -- not all commands do), you include those as a dictionary attached to the command payload like this: !!! note "optional" here refers to our toggle device example. For other API commands, parameters may be required. ![Shortcuts Get Contents of URL Optional Parameters Image](../../images/screenshot_2023-02-08_at_4.17.30_pm.png) Of course, you can add additional Shortcuts Apps and Actions to the workflow as needed. ## Action Group Shortcuts There is currently only one API command for Action Groups -- `indigo.actionGroup.execute` -- which requires two payload elements: - The command: `indigo.actionGroup.execute` (Text) - The Action ID: 123456 (Number) ![Action Groups Shortcuts Image](../../images/screenshot_2023-02-08_at_10.36.32_pm.png) ## Variable Shortcuts ### Get a Variable Value This example shows how to obtain a variable's value and do something with it -- in this case, speak the value aloud. The action you take in your shortcut could be any number of things. ![Get a Variable Value Image](../../images/screenshot_2023-02-22_at_5.18.39_pm.png) - You can get an individual variable object by referencing its Indigo ID directly `%%https://MY-REFLECTOR-NAME.indigodomo.net/v2/api/indigo.variables/MY-VAR-ID%%` where you replace `MY-REFLECTOR-NAME` with your active reflector name and `MY-VAR-ID` with your variable's ID. - The "trick" to getting the variable's value into the `speak` action is to tell your shortcut what kind of data it is. To do this, click on `Get Contents of URL` **in the speak action**, set the data type to `Dictionary`, and set `Get Value for Key` to `value` (be sure to hit return or tab to get the setting `value` to stick.) ![Get Contents of URL Image](../../images/screenshot_2023-02-22_at_5.19.04_pm.png) ### Update a Variable Value There is currently only one API command for Variables -- `indigo.variable.updateValue` -- which requires three payload elements: - The command: `indigo.variable.updateValue` (Text) - The Action ID: 123456 (Number) - A parameters dictionary that has one key/value pair (required): - `key`: value, - the new variable value which must be set to Text because all Indigo variable values are stored as text. ![Update Variable Value JSON Image](../../images/screenshot_2023-02-13_at_6.55.07_am.png) ### Variable Value as a Conditional You can include conditions in your shortcuts and take action -- or not -- based on the condition. For example, turn on a light -- but only if it's dark outside. ![Variable Value as a Conditional Image](../../images/screenshot_2023-02-22_at_5.44.27_pm.png) - As with the other examples, set up your `Get Contents of URL` action to get a variable's value -- in this example, the built-in variable `isDaylight`, - Take the returned dictionary and extract the value of the `isDaylight` variable in a `Text` action, - Add an `If` condition and, if the Text value is equal to `false` (Indigo variable values are always strings/text) then run your next shortcut action. Alternatively, you could send another command directly to the Indigo server (which is essentially what your linked shortcut is doing). - If the Text value is not `false`, the `Otherwise` action will be executed. ## Running an External Python Script In most instances, it's recommended that you use Indigo's built in script actions, such as: [Execute Script](../concepts/actions.md#script-and-file-actions), [Run Shell Script](../concepts/actions.md#script-and-file-actions), and [Run Apple Shortcut](../concepts/actions.md#run-apple-shortcut). In some instances, however, you might want to use a shortcut to run a script outside the Indigo environment. Luckily, this is easy using Apple's Shortcuts ***Run Shell Script*** action. The example below prompts the user for text, passes the text to the script, receives the script's output, and sends that output to a macOS notification. This very basic example is simply to show how to pass input to your script and pass the script's output on to other steps. Note the use of *`sys.argv[1]`*. This statement is reading the second argument passed to the script (the first argument is its source--the shortcut itself--which we won't need). The second argument -- *`sys.argv[1]`* -- is passed as a string, and that's the bit we need. You'll need to account for whatever format your inputs and outputs turn out to be. These are the two most important settings to this example: 1. Shell: *`Python 3`* 1. Pass Input: *`as arguments`* ![Apple Shortcut Run Shell Script Image](../../images/apple_shortcut_run_shell_script.png) The rest is up to you. !!! note The Run Shell Script action is not supported on iOS or tvOS. ## Using Your Shortcuts How you use your shortcuts is a matter of taste -- and you can always run them from the shortcuts app -- but here are a few other suggestions to get you started: - Save shortcuts to your Home Screen in iOS (using the Share menu), - Using a Shortcuts Widget, - Create a folder of your shortcuts on iOS to mimic a widget, - Add shortcuts to the Dock in macOS (File menu), - Using Siri: "Hey Siri -- Living Room Lamp" (name of the shortcut). ### Location Based Shortcuts You can run location-based shortcuts too, but they require "additional" steps and at least part of the automation must be built on an iPhone or iPad. There are several options to choose from, including: - Time of Day, - Alarm, - Sleep, - Arrive, - Leave, - Before I Commute, and - and many others. Location-based shortcuts are not currently available in macOS (so you'll need to set up the location control piece on iPhone or iPad). The `Leave` and `Arrive` shortcuts **cannot** be run automatically. They require confirmation each time they are run. There is currently no way to turn this requirement off. !!! tip Using the Indigo Reflector service is recommended (the URL you use for a location-based shortcut can't be one that only works on your local network.) 1. Review your automation. 1. Create the shortcut using the steps outlined above using macOS or iOS. For example, you could update a variable value set to `Home` or `Away`. 1. In the Shortcuts app for iOS (or iPadOS), select `Automations` from the tab at the bottom. 1. Click the `+` at the top. 1. Choose "Create Personal Automation". 1. Choose `Arrive` or `Leave`. 1. On the Location setting, select `Choose`. 1. Select "Current Location" or whatever location you choose, then select `Done`. You can also adjust the range of the trigger in the bottom map panel (the minimum range is about 100 meters or 328 feet). 1. Select `Next`. 1. Search for and select "Run Shortcut". 1. Next to "Run", tap the word `Shortcut` and select the shortcut you created in step 1. Add any next actions as needed (not covered in this example). 1. Select `Done` Now, your iPhone (or iPad) should run your shortcut when you Arrive/Leave that location. If you don't like the requirement to confirm the shortcut each time, there are other apps that allow for URL calls to be fired based on location. ## Firing Shortcuts From Indigo Using shortcuts to make Indigo *do something* is an awesome feature. But you can also use Indigo to run your shortcuts as well. ### Run from Indigo Action The easiest way to run a shortcut is via an Indigo Action. Create a new Action and select `*Server Actions*` > `*Run Apple Script*`. You can optionally add text as input to the shortcut if you wish; otherwise, leave the Shortcut Input field blank. ![Run Shortcut Action Image](../../images/run_shortcut_action_2023_1.png) ### Run from the Command Line When calling a shortcut from the "Run Shell Script" action, it's best to use the full path to the target: `%%/usr/bin/shortcuts%%` to make sure Indigo can find it. ![Firing Shortcuts From Indigo Image](../../images/screenshot_2023-02-16_at_12.44.05_pm-2.png) This specific approach requires the shortcut to be accessible by the Indigo server machine. If you don't have access to your shortcuts on the server, there are other ways to do this such as using Indigo to send a text message and linking the shortcut to Messages. Note that the Run Shell Script action doesn't support pipes, so if you want to send data to the shortcut, read on. ### Run Shell Script Action Sometimes, you may want to pass information to your shortcut from Indigo. For example, you can have Indigo fire a Notification on the server machine that includes real-time information. In this example, Indigo is asking the shortcut to run, and then the shortcut requests the pertinent data through the HTTP API. 1. Run the shortcut using a Run Shell Script Action. 1. Configure the shortcut to request the text value from Indigo using the appropriate API method outlined above. ![Run Shell Script Action Image](../../images/screenshot_2023-02-16_at_10.58.59_am.png) ### Run Python Script Action If you'd like to avoid the round trip of the example above, you can pass data directly to a shortcut using a simple Python script. In this example, we'll use Indigo's built-in Run Embedded Script action (the script should take less than 10 seconds to complete; otherwise, use a linked script to avoid the time limitation). The following example is the bare minimum required to use this approach. #### os.system() ```python import os val = indigo.variables[123456].value # The value to pass. In this case, a variable value. os.system(f'/usr/bin/shortcuts run "Post Event to Calendar" <<< \"{val}\"') # Will return 0 on success; 256 on error. ``` Note the escaped double-quotes for the shortcut payload. These are important to ensure that payloads with embedded spaces are sent as an encapsulated string object. For more information on the *`os.system()`* Python command, take a look at the [official docs](https://docs.python.org/3/library/os.html#os.system). **Apple Shortcut** - Get Text From - Shortcut Input - Calendar - Add New Event ![Post Event to Calendar Image](../../images/screenshot_2023-03-13_at_6.56.59_pm.png) #### subprocess.run() When using the *`os.system()`* command, the function returns a 16-bit value (*`0`* on success / *`256`* on error) to let you know if the command was successful. If you'd like to get a value back to Indigo after running the shortcut, one way is to use the more robust *`subprocess.run()`* function. This function supports a **bytes object** return for more elaborate scripting capabilities. Here is an example of a call using the *`subprocess.run()`* function: ```python import subprocess result = subprocess.run(['/usr/bin/shortcuts', 'run', 'My Shortcut'], input=b"Input Text", check=True, capture_output=True).stdout # 'result' is a bytes object. ``` Notice that the input is also a bytes object and will need to be decoded to use it. With this command, the shortcut will receive *`b"Input Text"`* as input, and anything returned by the shortcut will be stored in the *`result`* variable. If the call is unsuccessful, subprocess will throw the relevant error trace. This type of call would be useful for things like getting a list of upcoming appointments or reminders. For example, you could send the number of appointments you want in the *`subprocess`* call and get the appointment details back in the result. ### External Shell Scripts You can also accomplish the same result using a shell script. 1. Create your Notification shortcut named "Test Alert": 1. Create a new shortcut and add the "Show Notification" action. 1. For `Attachment`, select "Shortcut Input". 1. Double-click "Shortcut Input" and select Type: `Text` It should look something like this: ![External Shell Scripts Image](../../images/screenshot_2023-02-16_at_10.40.56_am.png) Using a plain text editor, create a new file called `script1.sh` (or whatever), enter the following text, and save it. ```bash #!/bin/bash # Note that redirects such as `pipe` aren't supported in the Indigo Run Shell # Script dialog and will require a script file method. echo "$1" | /usr/bin/shortcuts run "Test Alert" ``` We need to make our script executable, so in Terminal, head to the folder where your script is saved and type: `chmod +x script1.sh` and hit return. Now we can run our shell script from Indigo. 1. Under Actions, create a new Action (or add a new Action to an existing Action Group). 1. Select Type: `Run Shell Script`. 1. Select `Edit Action Settings`. 1. Enter the full path to your script and the custom message you want to send: `/Users/username/Temp/script1.sh "Hello world."` replacing `/Users/username/Temp/` with the path to your script. 1. If you like, you can elect to have the result saved to an Indigo Variable (optional). 1. Select `Save`, and then `OK`. From within Indigo, execute your action. The custom message you sent (in this example, "Hello world.") will be sent to the script and will appear in the resulting notification. ![Run Shell Script Action Image](../../images/screenshot_2023-02-16_at_10.58.59_am.png) You probably want to send something more interesting than `Hello world.`, for example the value of some variable. We can do this using Indigo's substitutions feature. `/Users/username/Temp/script1.sh "%%v:568909175%%"` Notice the quotes around the substitution. If you don't enclose the substitution in quotes, you'll get the variable text value only up to the first space. Quotes will ensure the whole value is sent. ![Test Alert Image](../../images/screenshot_2023-02-16_at_11.09.20_am.png) The opportunities here are only limited by the boundaries of the Shortcuts app. For example, you could add the value of a variable to your Calendar or send the value to a text message. ## Firing Your Shortcuts with Siri Of course, you can have Siri run your shortcut by saying the name of the shortcut you want to run. To run the `Toggle Living Room Lamp` shortcut, say, "Hey Siri. Toggle Living Room Lamp". However, once you begin to have a lot of shortcuts, it can be tough to remember the exact name of every shortcut in your collection. One thing that can help is to create multiple shortcuts with similar names that all point to the same "original" shortcut. That way, if you make a change to the original shortcut, you don't need to change the others (if you change the name of the original shortcut, the Shortcuts app will update the others). | Siri Phrase | Action taken | |----------------------|---------------------------------------| | "Party Time" | sets lights, temperature, tv, whatever | | "Let's Party" | Runs Party Time shortcut | | "It's Party Time" | Runs Party Time shortcut | | "I'm Having a Party" | Runs Party Time shortcut | ![Firing Your Shortcuts With Siri Image](../../images/screenshot_2023-02-18_at_7.46.27_pm.png) ## Ideas ### Calendar - Create a separate calendar called "Indigo Events" to track events over time. - Add weather information to your Calendar. ### Clock - Create an alarm on your phone. ### Home - Use Indigo to trigger events with your HomeKit devices. - Use HomeKit to trigger events with your Indigo server. ### Messages - Send messages with important information about your Indigo server. - Trigger your Indigo server by texting a phrase. ## Tips These tips can help make using shortcuts to control Indigo easier: - If you save your shortcuts to the Home Screen in iOS, be sure you're happy with the look of the icons before you add a bunch of them. Changes you make to the icons later in the Shortcuts app won't update home screen bookmarks automatically (they will change automatically in widgets). - Put your most commonly used shortcuts first because widgets only display a few at a time. - You can create shortcuts in the iOS app, but it is **much easier** to create them in macOS. - With location-based automations, some (but not all) notifications can be silenced. When available, you will see a toggle for "Ask Before Running". If you set this to `off`, a new toggle will appear that says, "Notify When Run". If this is also set to `off`, then notifications for this automation will not be sent. Presently, there is no global setting to turn off all automation notifications. - Create folders in the shortcuts app that mimic the folders in Indigo. If you find that you can't drag a shortcut to a new folder, it may help to create a "dummy" shortcut into the folder first. - If using a local address such as `localhost:8176` or `10.0.1.123:8176` make sure to use `%%http://%%` and not `%%https://%%`) - To use your shortcuts with Apple Watch, edit your shortcut, click on the **ⓘ** icon and select "Show on Apple Watch". - **Indigo servers running versions of macOS prior to Monterey do not have local access to the Shortcuts app.** ## Reference ### Command Line Commands Here are a few commands you can run from the command line with the Shortcuts App that may be useful: | Command | Result | |--------------------------------------------------------------------------------------------------------------------|-------------------------------------------------| | `shortcuts run "My Shortcut"` | Run shortcut named "My Shortcut" | | `%%shortcuts run "My Shortcut" <<< '{"message":"indigo.device.toggle", "objectId":"123456", "Arg":"Arg Value"}'%%` | Pass a dictionary to Indigo. | | `shortcuts list` | List available shortcuts. | | `shortcuts list --f` | List available folders. | | `%%shortcuts list -f Living\ Room%%` | List all shortcuts in the "Living Room" folder. | | `man shortcuts` | List of available commands. | ### Apple Documentation [Shortcuts User Guide Mac](https://support.apple.com/guide/shortcuts-mac/welcome/mac) [Shortcuts User Guide iOS/iPadOS](https://support.apple.com/guide/shortcuts/welcome/ios) --- Event Data Passing (https://docs.indigodomo.com/2025.2/user/automation/event-data/) --- # Events Data Passing !!! abstract "In this guide" How Indigo passes structured data from the event that fired — trigger details, device state changes, plugin metadata — to Python scripts and action groups. Covers the default `event_data` dictionary keys, plugin-specific additions, accessing data in embedded scripts, and chaining data through action group calls. Many users ask, "How do I know what caused an action to fire?" Our server architecture never passed through any source data before. But, now we do! All built-in triggers and schedules now pass through data that's specific to the event. Here are the basics. Events now pass an *`indigo.Dict`* that contains data about the firing event, be it a trigger, schedule, or plugin provided event that your plugin may provide. By default, every event dictionary will contain the following: ```json { "event-indigo-id": 1214985350, # the ID of the trigger, schedule, action group, etc "event-type": "Trigger", # the event type - trigger, schedule, action group, etc. "source": "server", # the source of the event (see description below) "timestamp": "2025-08-07T14:32:21", # ISO formatted datetime string } ``` The ID is the *`event-indigo-id`* for the instance passing the data - it could be a Trigger, a Schedule, an Action Group (more on this later). The *`event-type`* is just the IOM class name. The *`timestamp`* is an ISO formatted datetime that the event fired, so you can use *`datetime.fromisoformat()`* to convert it into a native *`datetime`* instance. The *`source`* item is a little more tricky. Here are the possible values: 1. *`server`* - this is what will show if the server performed the event on its own during the normal course of operations. A device state change trigger, a schedule fires, etc. 1. *`python`* - this is what the source would be if the operation was started via an IOM command. So if you did a *`indigo.actionGroup.execute(12345)`* this is what the source would be. 1. *`api-http`* - similar to *`python`* above, but if the command came in through the HTTP API. 1. *`api-websocket`* - similar to *`api-http`*, but the command would have come through a websocket. Plugin supplied events (for instance, the Z-Wave Command Received event, email received event, or any event that your plugin may provide) will **add** the following plugin specific information: ```json { "event-plugin-event-id": "zwaveCommand", # ID of the plugin supplied event "event-plugin-id": "com.perceptiveautomation.indigoplugin.zwave", # plugin id "event-plugin-name": "Z-Wave", # plugin name "event-type": "PluginEventTrigger", # will always be this class name } ``` Plugin events have more data that's specific to the plugin: the event id (from your Events.xml file), your plugin ID, the name. The *`event-type`* for a plugin event will always be *`PluginEventTrigger`*. ## How do you pass data through? If your plugin supplies events, then you are aware that in your event handling code you eventually will call the *`execute()`* method on the event that the user configures, something like this: ```python indigo.trigger.execute(trigger_instance) ``` To pass through any extra triggering data that you might want to add, just add it: ```python message = {"somekey": "some value"} indigo.trigger.execute(trigger_instance, trigger_data=message) ``` `message` can be either an `indigo.Dict` or a normal python *`dict`* instance, the server will convert it to an *`indigo.Dict`* before passing it along. It's just that simple. Some notes on standards for your data: - You won't want to duplicate any of the above built-in names as it would get overwritten. - You probably want to name your keys with something that's easily identified with your plugin. For instance, the Z-Wave plugin uses the prefix `zwavecmd-` for it's keys that describe the event data that it passes through. - We tried to use dashes `-` as separators rather than underscores - we think that's a better option for string keys. - Keys should follow the restrictions on `indigo.Dict` keys (alphanumeric, dash, underscore, starting with a letter). FYI, you can pass arbitrary data around using the IOM and the various `.execute()` actions on Triggers, Schedules, and Action Groups. Just pass through *`trigger_data`*, *`schedule_data`*, *`event_data`* respectively to those function calls when performing them through the IOM. ## How will users use the data? The data is passed first to any **conditional scripts**. The data can be accessed like this: ```python # event_data is prepopulated with the indigo.Dict from the originating event if event_data["source"] == "api-http": # the triggering event came from the HTTP API, so you may want to look at something # here. pass ``` Regardless of where the data came from (trigger, schedule, etc.) the variable name will be *`event_data`*. Then, once the conditions have been evaluated, the *`event_data`* will become available to actions. We've updated the built-in actions where appropriate to use the data. We've added a new **Insert Event Data into Variable** action which will allow the user to insert all or part of the event data into the specified variable. If they specify a path (see the **box** library description above) and the result of the path is a simple type (string, bool, int, float) then that value will be inserted into the variable. If the result of the path is a collection (dict or list) or the user doesn't specify the path, then the collection will be converted to JSON and the JSON string will be inserted into the variable. There is also a new substitution, *`%%e:"path"%%`* that can be used anywhere a substitution is valid. If the user wants the whole thing, they would just use the empty string *`%%e:""%%`* and the entire string will be inserted. Again, simple types will be inserted directly, complex types will be inserted as JSON. Embedded script actions will also receive the data in the same way that conditional scripts do. **Note**: external (linked) scripts will not get the data in this release. We will gauge demand moving forward to determine if and when to add it there. ## How will you get the data in your actions? You can get the data directly in your actions as well. Event data will be passed to the action method handler if you modify your method signature: ```python def run_shortcut(self: indigo.PluginBase, action: any, dev: any, caller_waiting_for_result: bool, event_data: Optional[indigo.Dict]) -> Optional[indigo.Dict]: ``` This is a complete action handler with type hints. While we don't envision a scenario where *`event_data`* is passed `None`, it's possible that it may happen or could be optional at some point in the future. Your code should make sure that the data is present before assuming anything. ## Examples of How Events Data Can Be Used There are likely a lot of different scenarios where *`event_data`* is useful, but it may be helpful to see a complete working example all in one place. Here is a simple example just to show how straightforward the mechanism can be. Create an Action: - Create an Action Group called "Log Event Data" - Select **Server Actions** > **Script and File Actions** > **Execute Script** - Select Embedded Python and enter the following short script into the code block. Notice that we didn't do anything special to access the *`event_data`* payload. It's automatically supplied by the host process when the script is called **due to an event**. ```python indigo.server.log(f"{event_data}") ``` - Select OK Create a Trigger to fire the action: - Create a Trigger called "Log Event Data" - You can link it to an event, but for this example the event type doesn't matter - You can also set a Condition, but for this example the condition doesn't matter either - On the Actions tab, select **Server Actions** > **Execute Actions Group** - Select the "Log Event Data" Action you created above - Select OK Now, from the Actions list in Indigo, highlight the "Log Event Data" Trigger and select **Execute Actions Only**. When your Trigger fires the script, something similar to the following should appear in the Events log: ```text Trigger Event Data Trigger Action Group Event Data Example Script EventDataDict : (dict) event-indigo-id : 553162605 (integer) event-type : DeviceStateChangeTrigger (string) source : server (string) timestamp : 2025-10-31T10:18:37 (string) ``` It's that simple. ## Get Creative The new *`event_data`* mechanism opens up a lot of possibilities for plugin developers. One thing that comes to mind almost immediately would be for plugins that provide virtual devices/wrappers/shims to allow the user to specify a path in the data then map that into either a custom state or a property (onState, etc.) Combine this functionality with [Webhook functionality](../../api/webhooks.md) and it would be possible to have a webhook directly update a virtual device. This would reduce a lot of glue code necessary now in handling those types of things. --- Get Contents of URL (https://docs.indigodomo.com/2025.2/user/automation/get-contents-of-url/) --- # Get Contents of URL Action !!! abstract "In this guide" How to use the Get Contents of URL action to fetch data from HTTP endpoints and store the response in an Indigo variable. Covers the supported HTTP methods, authentication options, custom headers, the request body field with substitution support, and worked examples for common REST APIs. The Get Contents of URL Action allows users to query APIs and other URLs and save the results of the query to an Indigo variable for display or for further processing. ![Get Contents of URL Action Image](../../images/get_contents_of_url_action.png) - `Enter the URL` - use this field to enter the URL of the target resource. - `Method` - use this option to... The action supports all the major resource I/O methods including: `*GET*`, `*POST*`, `*PUT*`, `*PATCH*` and `*DELETE*`. - `HTTP Auth` - if the target resource requires authentication (as most do), use this option to enter the necessary authentication details. - `Auth Type` - use this option to select the authentication method used by the target resource. The control supports both `*BASIC*` and `*DIGEST*` auth types. - `Username` - use this field to enter the username of the target resource. - `Password` - use this field to enter the password of the target resource. - `Show Headers` - There are two major sections to the Show Headers control: the first set of controls is used to edit or delete an existing header key/value pair, and the second set is for adding a new header. The checkbox is only used to show/hide the controls; any added headers are used even if the checkbox is unchecked. - `Header` - use this option to select from a list of headers for the action. The list will be empty if no headers have been added. Use the add header controls to add a new header. - `Key` - use this option to create your header key. You can create multiple keys, but only one at a time. - `Value` - use this option to create your header value. You can create multiple values, but only one at a time. - `Body` - use this field to enter the body message (including Indigo substitutions). Anything in this field will be inserted into the body of the HTTP message. We use it exactly as it comes from the field with no post-processing other than the normal device and variable substitutions. - `Store Result in Variable` - use this option to store the results of the URL call to an Indigo variable. If enabled, you can select from a list of available variables. Note that Indigo stores all variable values as strings (text), so any value saved to a variable using the Get Contents from URL action will be coerced into a string. ## Examples Here are a couple of examples to get you started. ### Example 1 *`Enter the URL`* -> https://api.weather.gov/stations/KATT/observations/latest *`Method`* -> Get *`HTTP Auth`* -> False *`Show Headers`* -> False *`Body`* -> None *`Store result in variable`* -> True *`Variable`* -> [choose the appropriate variable] ### Example 2 *`Enter the URL`* -> https://httpbin.org/bearer *`Method`* -> Get *`HTTP Auth`* -> False *`Show Headers`* -> True Add a Header (be sure to click the *`Add Header`* button: *`Key`* -> Authorization *`Value`* -> Bearer indigo123 *`Body`* -> None *`Store result in variable`* -> True *`Variable`* -> [choose the appropriate variable] --- Substitutions (https://docs.indigodomo.com/2025.2/user/automation/substitutions/) --- # Substitutions !!! abstract "In this guide" How to use Indigo's substitution syntax to embed dynamic values — device states, variable contents, timestamps, and trigger event data — directly into action parameters, URLs, and plugin fields. Covers the substitution string format for each object type with examples, and notes where substitutions are and are not supported. One of Indigo's powerful features is substitutions. Substitutions are special codes that are used to reference other Indigo objects -- like devices, variables, and events -- to get values related to them. For example, you might use a variable substitution to get the current value of the variable you're referencing. All substitution expressions have a similar format: | Object | Substitution String | Example | Target | | --- | --- | --- | --- | | Devices | %%d:DEVICEID:STATEID%% | %%d:12345678:onOffState%% | the current onOffState of device 12345678 | | Variables | %%v:VARIABLEID%% | %%v:234566789%% | the current value of variable 234566789 | | Timestamps | %%t:"FORMATSTRING"%% | %%t:"%Y-%M-%D %H:%M"%% | the current time based on the provided *`datetime`* format specifier *`"%Y-%M-%D %H:%M"`* | | Events | %%e:"PATH"%% | %%e:"a-list.[2].dict-in-list"%% | replaces the specified path string *`"a-list.[2].dict-in-list"`*with the corresponding value from the related *`event_data`* | Substitutions are used extensively throughout Indigo and Indigo Plugins. For example, you can use a device or variable substitution as a part of a Control Page Refreshing URL input like this: *`http:*www.example.com/images/%%v:2345678%%.jpg`* which will substitute the current value of variable 2345678 as the image filename. !!! note While available in a wide array of instances, substitutions are not universally supported. You should confirm that substitutions are supported in each instance before attempting to use them. ## Device Substitutions Device substitutions allow you to reference a particular state's current value of the target device. Device states can have different value types -- like strings, numbers, booleans, etc. -- so it's important to ensure that the substitution will return a value type appropriate to your use case. | Substitution String | Example | Target | | --- | --- | --- | | %%d:DEVICEID:STATEID%% | %%d:12345678:onOffState%% | the current onOffState of device 12345678 | | Example | Result | | | %%d:12345678:brightnessLevel%% | 100 (integer) | | | %%d:12345678:onOffState%% | on (on/off boolean) | | | %%d:12345678:hvacFanModeIsAuto%% | true (boolean) | | ## Event Substitutions Event substitutions allow you to reference a particular data element of an event-data payload. Event substitutions are somewhat of a special case and have a [separate page dedicated to them](../../scripting/reference/event-data-paths.md). | Substitution String | Example | Target | | --- | --- | --- | | %%e:"PATH"%% | %%e:"a-list[2].dict-in-list"%% | replaces the specified path string *`"a-list[2].dict-in-list"`*with the corresponding value from the related *`event_data`* | ## Plugin Substitutions Many Indigo plugins support substitutions-- plugin developers are encouraged to make it clear where substitutions are permitted and to explain how they're used in the plugin's documentation. ## Timestamp Substitutions Timestamp substitutions allow you to reference the current date/time based on the provided format specifier. There is a considerable number of online resources that explain the various datetime format specifiers. Do a search for "python datetime format specifiers" (there are some differences between programming languages), so be sure to search for ***python*** specifiers. | Substitution String | Example | Target | | --- | --- | --- | | %%t:"FORMATSTRING"%% | %%t:"%Y-%M-%D %H:%M"%% | the current time based on the provided *`datetime`* format specifier *`"%Y-%M-%D %H:%M"`* | | Example | Result | | | %%t:"%Y-%m-%d %H:%M"%% | "2025-10-22 13:03" | | | %%t:"%Y-%m-%d"%% | "2025-10-22" | | | %%t:"%H:%M:%S"%% | "13:03:49" | | ## Variable Substitutions Variable substitutions allow you to reference the current value of the target variable. Since variables values are always strings, a variable substitution will always return a string. | Substitution String | Example | Target | | --- | --- | --- | | %%v:VARIABLEID%% | %%v:234566789%% | the current value of variable 234566789 | | Example | Result | | | %%v:12345678%% | "My variable value" | | | %%v:2345678%% | "Another value" | | --- Core Concepts (https://docs.indigodomo.com/2025.2/user/concepts/) --- # Core Concepts Indigo has several high-level objects that you interact with: Devices, Triggers, Schedules, Action Groups, Control Pages, and Variables. While some of these objects are obvious, others aren't, so let's create some definitions (each one has a section with more detail below): | Object | Definition | | | --- | --- | --- | | [Devices](devices.md#devices) | A device is any "thing" that Indigo can interact with - usually it's some kind of hardware (light switch, appliance module, motion sensor, etc), but devices can also be other non-hardware things (iTunes server, calendar, etc). | | | [Triggers](triggers.md#triggers) | A trigger is generally some kind of "event" that occurs. Indigo can use that event to execute actions in response. | | | [Schedules](schedules.md#schedules) | A schedule is similar to a trigger, but the event that causes the execution of the actions is a temporal event of some kind. Either a fixed point in time (5/2/2011 at 1:00pm) or more likely some repeating time (every day at 1:00pm). | | | [Action Groups](actions.md#action-groups) | Action Groups are collections of actions that may be reused (and modified) easily between multiple triggers, schedules, and control pages and executed via various clients (the Mac Client, the Indigo Web Server (IWS) web pages, Indigo Touch, etc). | | [Control Pages](control-pages.md#control-pages) | Control Pages are user-created interfaces to control their Indigo system - for instance you could create a graphical floor plan with light icons in the various rooms. | | [Variables](variables.md#variables) | A variable is a place where your home automation logic can store information that changes during the normal operation of your home and that can be used in other parts of your system: for instance, you can have a variable that represents whether your home is occupied or not - then you can have special automation logic that takes place when that variable changes. | | That is the very high-level definition of the primary objects in Indigo. If you don't find what you're looking for there, check out our [Glossary Of Terms](../glossary.md) which includes just about every term we can think of that you might run across. Next, we want to go into a little more detail about each of the main object types to help you understand when and why you would want to use them. ## Reading On You work with these objects through Indigo's clients: the [Mac Client](../mac-client/index.md) (where all configuration happens), [Indigo Touch for Web](../remote-access/touch-for-web.md) in any browser, and [Indigo Touch for iOS](https://www.indigodomo.com/touch.html) on your iPhone, iPad, and Apple Watch. Each object has its own chapter: [Devices](devices.md), [Triggers](triggers.md), [Schedules](schedules.md), [Actions & Action Groups](actions.md), [Variables](variables.md), [Control Pages](control-pages.md), and [Conditions](conditions.md) — which restrict *when* triggers and schedules execute. Finally, [Managing Plugins](plugins.md) covers what plugins are and how to install and manage them. --- *Z-Wave® is a registered trademark of Sigma Designs, Inc. Indigo's support of Z-Wave hardware is neither endorsed nor certified by Sigma Designs.* --- Actions & Action Groups (https://docs.indigodomo.com/2025.2/user/concepts/actions/) --- # Actions & Action Groups ## Actions Actions are the individual commands that Indigo will perform: turn on a light, send an email, etc. You can specify as many actions as you like for each [Trigger](triggers.md#triggers), [Schedule](schedules.md#schedules), [Action Group](#action-groups), and [Control Page](control-pages.md#control-pages) element. On any dialog that has an area where you select actions (Triggers, Schedules, Action Groups, and Control Pages), The first thing you'll see is the `Type` popup: ![Action Type Menu Image](../../images/action_type_menu.png) Actions are grouped into 6 main categories (some of those categories have subcategories) and then below those there is a category for each plugin that provides actions that aren't integrated into other menus. ### Device Actions ![Device Actions Type Menu Image](../../images/device_actions_type_menu.png) The `Device Actions` category has 7 subcategories. Plugins can also add a submenu to this category for the actions they define that work directly on devices. By default, Indigo ships with the [Airfoil Pro](../../plugins/airfoilpro.md) and [Timers and Pesters](../../plugins/timersandpesters.md) plugins, which add subcategories to this menu. You may have other menus as well if you've installed 3rd party plugins. #### Universal Controls ![Universal Controls Menu Image](../../images/universal_controls_menu.png) These controls are either universal across most devices or are available on some specific types of devices (KeypadLincs for instance). - `Request Full Status Update` - ask the device to reply with all possible status information. This is dependent on the capabilities of the device. - `Request Energy Update` - ask the device to reply with only its energy usage information. This is dependent on the capabilities of the device - many do not support energy monitoring. - `Reset Energy Usage` - ask the device to reset the running total of energy usage. This is dependent on the capabilities of the device - many do not support energy monitoring. #### Light/Appliance Controls { #light-appliance-controls } ![Light Controls Menu Image](../../images/light_controls_menu.png) These controls are for lights and on/off (sometimes called relay) devices. - `All Off` - turn off all light and appliance devices. The `Devices:` popup lets you select `All Insteon/X10`, `All Insteon`, `All X10`, or a specific X10 house code. - `All Lights On` - turn on all light devices with the same `Devices:` options as `All Off` - `All Lights Off` - turn off all light devices with the same `Devices:` options as `All Off` - `Turn On` - turn on a specific device with an optional complementary `Auto-off after X minutes` action - `Turn Off` - turn of a specific device with an optional complementary `Auto-on after X minutes` action - `Toggle On/Off` - turn on the device if it's off or off if it's on - `Set Brightness` - set the brightness of a lamp device to a specific percentage (from 0-100) - `Brighten by %` - increase the brightness of a lamp device by a specific percentage (from 0-100) - `Dim by %` - increase the brightness of a lamp device by a specific percentage (from 0-100) - `Match Brightness to Device` - set the brightness of any number of dimmer devices to the value of the selected dimmer device - so you can quickly and easily create a very simple scene. - `Match Brightness to Variable` - set the brightness of any number of dimmer devices to the value of the selected variable. - `Start Brighten` - for Insteon and Z-Wave dimmers, start to brighten the load (using the device's specific ramp rate). You can pair this with an `End Brighten/Dim` command or you can just allow it to brighten all the way to 100%. - `Start Dim` - for Insteon and Z-Wave dimmers, start to dim the load (using the device's specific ramp rate). You can pair this with an `End Brighten/Dim` command or you can just allow it to dim until it's finished. Insteon devices will end up at 0% (off), but some Z-Wave devices may stop at 1% rather than being completely off. - `End Brighten/Dim` - for Insteon and Z-Wave dimmers, end a previously issued `Start Brighten` or `Start Dim` and update the brightness of the device in Indigo at whatever level the dimmer was at when it stopped. - `Match On State to Device` - set the on state of any number of on/off or dimmer devices to the value of the selected device which supports an on state. - `Match On State to Keypad LED State` - set the on state of any number of on/off or dimmer devices to the value of the selected Insteon KeypadLinc button. - `Match On State to Variable` - set the on state of any number of on/off or dimmer devices to the value of the selected variable. - `Set RGBW Levels` - set the RGBW levels for lights that support setting their color. #### Sprinkler Controls { #sprinkler-controls } ![Sprinkler Action Image](../../images/sprinkler_action.png) These are the options for controlling your sprinkler: ![Sprinkler Schedule Action Image](../../images/sprinkler_schedule_action.png) - `Run Schedule` - selecting this action will show you the zone list (shown above - note the list will scroll to show all available zones for the sprinkler) that will allow you to set the duration for each zone and optionally multiply those durations by the selected variable. This last option is useful if you change durations based on time of year - you can change the variable value but keep the existing schedule and it'll adjust the duration as appropriate. - `Pause Schedule` - this action will pause the current schedule (if for instance you're walking to your car and don't want to get wet) - `Resume Schedule` - this action will resume a previously paused schedule (once you're in your car) - `Stop (all zones off & clear schedule)` - this action will completely stop the schedule - `Activate Previous Zone` - this action will cause the schedule to back up one zone - `Activate Next Zone` - this action will cause the schedule to jump to the next zone - `Turn on Specific Zone` - this action will turn on a specific zone for the maximum run time specified in the sprinkler's definition and will turn off after it's done #### Thermostat Controls { #thermostat-controls } ![Thermostat Actions Type Menu Image](../../images/thermostat_actions_type_menu.png) There are 13 basic actions available: - `Set Heat Setpoint` - set the heat setpoint to an absolute temperature - `Increase Heat Setpoint` - increase the heat setpoint by some number of degrees - `Decrease Heat Setpoint` - decrease the heat setpoint by some number of degrees - `Set Cool Setpoint` - set the cool setpoint to an absolute temperature - `Increase Cool Setpoint` - increase the cool setpoint by some number of degrees - `Decrease Cool Setpoint` - decrease the cool setpoint by some number of degrees - `Set Main Mode` - set the mode of the thermostat to one of the following: - `All Off` - set the HVAC unit so that both heat and cool setpoints are ignored - the unit will not come on at all - `Heat On` - activate the HVAC unit so that only the heat setpoint is used - `Cool On`- activate the HVAC unit so that only the cool setpoint is used - `Heat/Cool On` - activate the HVAC unit so that both heat and cool setpoints are used - `Run Heat Program` - tell the thermostat to run the heat program that is programmed directly into the thermostat (see the thermostat documentation for details) - `Run Cool Program` - tell the thermostat to run the cool program that is programmed directly into the thermostat (see the thermostat documentation for details) - `Run Heat/Cool Program` - tell the thermostat to run the heat/cool program that is programmed directly into the thermostat (see the thermostat documentation for details) - `Set Fan Mode` - set the fan mode of the thermostat to one of the following: - `Fan Auto On` - set the fan so that it only runs as needed - `Fan Always On` - turn the fan on so it'll continuously run - `Get All Status` - get all information from the thermostat - `Get Current Mode` - get the current mode - `Get Ambient Temperature` - update the temperature - `Get Humidity` - get the humidity - `Get Setpoints` - get the setpoints - `Cycle Through Thermostat Modes` - this will allow you to easily cycle through thermostat modes in this order: Off, Cool, Heat, Auto. Especially useful in conjunction with the "Thermostat Mode+.png" image on a control page - just add this as a server action and you have a simple control for adjusting the thermostat mode - each time you tap/click the image, the thermostat selected will cycle to the next mode just like pressing the Mode button on the thermostat itself (if it has one). - `Toggle Thermostat Fan Mode` - like the action above, this method will toggle between the two fan modes: Fan On (always on) and Fan Auto (automatic). Again, useful with the "Thermostat Fan Mode+.png" image file. v1 Insteon thermostat adaptors didn't broadcast out changes to update its internal state representations for the thermostats - which is why there are so many options to get status. Indigo catches the v2 thermostat update broadcasts so the need to manually get updates should be reduced. #### Fan Speed Controls ![Fan Speed Control Actions Image](../../images/fan_speed_control_actions.png) Here are the 7 actions for fan speed control: - `Set Fan Speed` - set the speed of the device to a specific level - `Increase Fan Speed` - increase the fan speed by some # of units - for instance, on a FanLinc that's currently on `Low`, increasing the fan speed by 1 will set it to `Medium` - `Decrease Fan Speed` - decrease the fan speed similarly to the above Increase Fan Speed - `Turn Fan On (resume last speed)` - this will turn the fan on to its last speed setting - `Turn Fan Off` - turn the fan off completely - `Toggle Fan On/Off` - toggle between on and off - `Cycle Through Fan Speeds` - use this action to cycle through the fan speeds in highest to lowest speed order. It's basically an electronic version of pulling a fan's chain. #### Input / Output Device Controls { #input-output-device-controls } ![I/O Action Image](../../images/io_action.png) There are eight actions available: - `Turn On Output` - turn on the specified output - `Turn Off Output` - turn off the specified output - `Turn Off All Outputs` - turn off all outputs - `Get All Status` - get the status of all inputs and outputs - `Get Binary Outputs Status` - get the status of all the binary outputs - `Get Binary Inputs Status` - get the status of all the binary inputs - `Get Analog Inputs Values` - get the voltage value of all the analog inputs - `Get Sensor Inputs Values` - get the value of the 1-wire sensor bus (only available on some I/O devices) #### Virtual Device Controls ![Virtual Devices Controls Image](../../images/virtual_devices_controls.png) There are a few controls specific to Virtual Devices: - `Update Device Group Saved State` - updates the saved state of all devices in a device group so the next ON command will match their current settings. - `Set Virtual On/Off Device State` - explicitly sets the state of a virtual on/off device. Useful if the state gets out of sync or is not maintained by a variable. #### Brand Specific Controls ![Brand Specific Controls Image](../../images/brand_specific_controls.png) The Brand Specific Controls submenu contains submenus that hold device specific commands that are unique to a particular brand of device (manufacturer). There are two described below: ##### Insteon ![Insteon Specific Actions Image](../../images/insteon_specific_actions.png) - `Beep Device` - ask the device to beep. This function is dependent on the capabilities of the device. - `Turn On Single KeypadLinc Button ` - turn on one individual KeypadLinc button. - `Turn On Single KeypadLinc Button ` - turn off one individual KeypadLinc button. - `Set All KeypadLinc Buttons ` - turn on/off groups of buttons. Why not just have multiple actions using the built-in Turn ON/Turn OFF LED actions? Because each of those requires a lot of Insteon traffic - and if you need to set several buttons at once this action will do it in one (or two if you want to maintain some buttons) action(s). It's more efficient and easier to configure (one action versus potentially seven actions). Select the action you want to take for each button: `Turn On`, `Turn Off`, `Leave Alone`. The latter option will require that we query the KPL to find the states first so if you select that for any of the buttons the action may execute a bit slower than it would otherwise. **Note**: using this action, which is sending raw Insteon commands through the IndigoServer, will cause the KeypadLinc's button states in Indigo to become out of sync. This is because the server doesn't know that you're changing the button states given that it's just a raw command message that it's being asked to send to the PowerLinc. If you need to keep the states in sync then add another action to do a status request to the KeypadLinc (after a short delay to avoid collisions). - `Set KeypadLinc Auto-Off Button Group ` - specify what buttons will go off automatically when you press any other button. Useful in conjunction with Toggle Mode below for creating "radio groups". See the [Fanlinc And Keypadlinc](../interfaces/insteon/fanlinc_and_keypadlinc.md) article for usage examples. - `Set KeypadLinc Button Toggle Mode ` - specify whether a button toggles (alternates between ON and OFF when pressed) or whether it sends a single command anytime it's pressed (can send either ON or OFF). Useful in conjunction with Auto-Off groups above for creating "radio groups". See the [Fanlinc And Keypadlinc](../interfaces/insteon/fanlinc_and_keypadlinc.md) article for usage examples. - `Set LED Brightness ` - set the brightness of the LEDs on certain devices. Newer KeypadLincs are supported as well as some SwitchLinc models. Unfortunately there isn't really a way to tell you which devices are supported so you'll just have to try it and see if it works. - `Set i3 Dimmer/Relay Mode ` - set the behavior for i3 modules that support both on/off and dimmable loads. - `Set i3 Dial Off Behavior ` - set the behavior when an i3 Dial is fully rotated counterclockwise: off or dim to 1%. - `Set Motion Sensor LED Brightness ` - set the brightness of the LED that flashes inside the motion sensor when motion is detected. While the brightness value is between 0 and 255, 0 does not mean the LED is completely off - it's just very dim. Note: only revision 2 Motion Sensors with jumper 5 set can be configured. - `Set Motion Sensor Timeout ` - set the timeout value between the time the motion sensor stops detecting motion and when it sends the OFF command. The timeout values work like this: 0 is equal to 30 seconds and 255 is equal to 2 hours. Values in between are proportional to those values. Note: only revision 2 Motion Sensors with jumper 5 set can be configured. - `Set Motion Sensor Day/Night Sensitivity ` - set the sensitivity for when the motion sensor detects changes from dawn to dusk and vice versa. The sensitivity values work like this: 0 will make the sensor register day all the time and 255 is equal to night all the time. Values in between are proportional to those values. **Note**: only revision 2 Motion Sensors with jumper 5 set can be configured and Motion Sensor II models will interpret 0 as 3. - `Set I/O Linc Momentary Mode ` - set the momentary mode of an I/O Linc to A, B, C, or None (the built-in UI only sets A or None). - `Set I/O Linc Momentary Duration ` - set the duration the output will be on before it automatically goes off (if momentary mode is turned on with the above command). - `Configure SynchroLinc ` - configure the Trigger Watts, Threshold Watts, and Delay Seconds in a SynchroLinc. Here are the details of those settings: - Trigger Watts (0 to 1800 watts in 0.5 watt steps): the wattage needed before the SynchroLinc broadcasts. - Threshold Watts (aka hysteresis, 0 to 127.5 watts in 0.5 watt steps): tolerance before on/off toggle is sent. - Delay Seconds (0.15 to 38.25 seconds): prevents message flooding if thresholdWatts is too low. - `Set Siren Alarm Sound` - configure the sound that will be played the next time the alarm is activated. Choose between chime (doorbell) or siren (loud). The sound will change if this action is called while the siren is sounding. - `Set Siren LED Mode` - configure what the LED on the siren does. Choices are: On Solid, Blink on Insteon Traffic, Off - `Set Load Sense for OutletLinc` - configure the load sense on either outlet in the OutletLinc (dual outlet only) ##### HomeSeer The HomeSeer 200+ series of devices (WD200+, WS200+, FC200+, and HSM200 as of this release) allow the various LEDs on those devices to be controlled in a variety of ways. These actions enable controlling those features. ![Homeseer Specific Actions Image](../../images/homeseer_specific_actions.png) - `Set LED Mode` - set the mode of the LEDs on the switch. The default `Normal (load status)` behavior is for them to represent the brightness to which the switch is set. You can set the switch to `Status (custom status)` which will allow you to individually turn on and off each LED and set its color. You can also set the bottom LED's behavior. - `Set LED Color and On/Off State` - when the switch is in `Status` mode, you can use this action to turn on/off and set the color of any of the LEDs. - `Set LEDs Blinking Behavior` - start/stop LEDs from blinking. ##### Inovelli Several recent Inovelli devices (LZW30, LZW30-SN, LZW31, LZW31-SN and LZW36 as of this release) allow the various LEDs on those devices to be controlled in a variety of ways. These actions enable controlling those features. ![Inovelli Specific Actions Image](../../images/inovelli_specific_actions.png) - `Set LED Brightness when Off` - this will set how bright the LED is when the device is off (nice for night time to easily locate switch in the dark). - `Set LED Brightness when On` - this will set how bright the LED is when the device is on. - `Set LED Color` - sets the color of the LED. - `Set Notification` - some Inovelli devices allow you to set what they refer to as a notification. This is a combination of color, brightness, effect (pulse, flash, etc), and duration. The net effect is that you can change the behavior of a device's LED temporarily (with or without an automatic timeout) to act as a visual notification. When the notification times out (or is explicitly cleared) it will revert to its previous setting. - `Clear Notification` - clears a previously set notification (does nothing if no notification is active on a device). ##### Zooz The Zooz ZEN30 allows the various LEDs on it to be controlled in a variety of ways. These actions enable controlling those features. ![Zooz Specific Actions Image](../../images/zooz_specific_actions.png) - `Set Default Brightness` - set the brightness that the dimmer device will come on to. Note this only applies to manual operation - Z-Wave ON commands will always result in the switch returning to its previous brightness. - `Set LED Brightness` - set how bright the LEDs are. - `Set LED Color` - set the color of the LEDs. ### Server Actions ![Server Actions Type Menu Image](../../images/server_actions_type_menu_2023_1.png) The Server Actions category has three actions and three subcategories, described below: #### Execute Action Group { #execute-action-group } Executes a specified Action Group. #### Remove Delayed Actions { #remove-delayed-actions } Removes delayed actions, with the following options: - `Remove all delayed actions` - removes all delayed actions regardless of delay type - `Remove for device` - removes any delays for the selected device - `Remove for trigger` - removes any delays from the selected trigger - `Remove for schedule` - removes any delays from the selected schedule #### Reset Interface Connections { #reset-interface-connections } Resets the Insteon and X10 RF interfaces. #### Script and File Actions ![Script Actions Type Menu Image](../../images/script_actions_type_menu_2023_1.png) ##### Execute Script { #execute-script } ![Execute Script Action Image](../../images/execute_script_action.png) The `Execute Script` action allows you to execute a Python script as an embedded script or stored in a script file. In general, you should use embedded scripts for scripts that are short and very quick to execute. Embedded scripts will be limited to 10 seconds of execution time - if they run longer than that they will be killed. If you have a script that is a long running script you should save it in a separate file and execute it from the file by selecting the File radio button then selecting the file. Scripts executed from files are executed in their own process and are therefore much less likely to adversely effect the server process if they don't work as expected. For embedded scripts, you can click the `Compile` button and we'll do our best to check the script for syntax errors. Click the `Run` button to have the script executed immediately. ##### Open File ![Open File Action Image](../../images/open_file_action.png) This will open the specified file (full path using *nix slashes ("/")) using the default application. If it's the path to an application (e.g. "/Applications/TextEdit.app") then it will launch that app. You can also add command-line options and include [Indigo substitutions](../automation/substitutions.md). Note: file paths that contain spaces will need to have the spaces escaped with a backslash - /some\ path/that\ has/escaped\ spaces/. ##### Run Shell Script ![Run Shell Script Action Image](../../images/run_shell_script_action.png) This will run the specified script file optionally with the output being inserted into the specified variable. The script must be marked executable and **a valid** shebang (#!/path/to/shell) must be specified at the top of the script. You can also add command-line options and include markup that will do variable (%%v:VARIDHERE%%) and device state (%%d:DEVIDHERE:STATEIDHERE%%) substitutions. Note: file paths that contain spaces will need to have the spaces escaped with a backslash - /some\ path/that\ has/escaped\ spaces/. **Note**: if you're running a Python script you'll probably just want to use the Execute Script action since script files run from this plugin won't have access to the IOM (*`import indigo`*, which is Python-only). #### Log Actions ![Log Actions Type Menu Image](../../images/log_actions_type_menu_2023_1.png) ##### Write to Log ![](../../images/write_to_log.png){ width=600 } This will write the specified text into the event log - optionally with the specified type string. The **Text to Log** field supports the various [Indigo substitutions](../automation/substitutions.md). In addition, you can select from three logging levels: `Info (normal)`, `Warning`, or `Error`. The log message will be appropriately colored based on the chosen logging level. ##### Email Event Log Data ![Email Log Action Image](../../images/email_log_action.png) This action will send an email to the specified email addresses that contains the specified number of lines from the log file. This is useful for debugging problems (among other things). Tip: this action is also available interactively by selecting the `Help->Email Log...` menu item. ##### Enable/Disable/Reload Actions { #enable-device } ![Enable Type Menu Image](../../images/enable_type_menu_2023_1.png) `Enable Device` - This action will enable Indigo communication with the selected device. You can specify an optional complementary `Auto-disable after X minutes` action. `Enable Trigger` - This action will enable processing of the selected trigger. You can specify an optional complementary `Auto-disable after X minutes` action. `Enable Schedule` - This action will enable processing of the selected schedule. You can specify an optional complementary `Auto-disable after X minutes` action. `Disable Device` - This action will disable Indigo communication with the selected device. You can specify an optional complementary `Auto-enable after X minutes` action. `Disable Trigger` - This action will disable processing of the selected trigger. You can specify an optional complementary `Auto-enable after X minutes` action. `Disable Schedule` - This action will disable processing of the selected schedule. You can specify an optional complementary `Auto-enable after X minutes` action. `Reload Plugin` - This action will restart the specified plugin. You must know the ID of the plugin: you can copy the ID in a [plug's submenu on the Plugins menu](plugins.md#individual-plugin-submenu) or from the plugin's detail page in the [Plugin Store](https://www.indigodomo.com/pluginstore/). ![Reload Plugin Dialog Image](../../images/reload_plugin_dialog.png) #### Get Contents of URL Use this action to query an API and (optionally) save the results to an Indigo variable. For more details on this action type, refer to the [Get Contents of URL Action](../automation/get-contents-of-url.md) page. #### Run Apple Shortcut Use this action type to allow Indigo to fire Apple Shortcuts, provided your server is running a version of macOS that supports the Shortcuts app. The configuration dialog will present a list of all Shortcuts on the server machine as well as an input field to pass optional text to the selected shortcut when it's run. ![](../../images/run_apple_shortcut.png){ width=600 } The mechanism in Shortcuts to get a dictionary from input is extremely picky about the JSON that it's fed. When checked, the checkbox in this dialog explicitly encodes the input as JSON to make things work better. It defaults to off so existing shortcuts won't be encoded. If you pass an [event substitution](../automation/substitutions.md) in the input field like *`%%e:"some_data[0].value"%%`*, you will need to tick the Send JSON checkbox before the shortcut will work. You can also optionally save any return data to a variable. ### Variable Actions ![Variable Actions Type Menu Image](../../images/variable_actions_type_menu.png) There are five actions that are specific to variables: #### Modify Variable { #modify-variable } ![Modify Variable Action Image](../../images/modify_variable_action.png) Select a variable to modify, then select the options. #### Insert Device State into Variable ![Insert Device State Into Variable Action Image](../../images/insert_device_state_into_variable_action.png) This will insert the selected state of the selected device into the specified variable. After you specify the device, click the `Edit Action Settings...` button and it will open a dialog (shown above) with a list of all possible states for the device you selected and a popup of all variables. You can create a trigger that executes when a device's state changes and have it insert the new state into the variable. #### Insert Timestamp into Variable ![Insert Timestamp Into Variable Action Image](../../images/insert_timestamp_into_variable_action.png) Click the `Edit Action Settings...` button and it will open a dialog (shown above). Here you will select the variable and, optionally, the format string as defined in the [Python datetime string formatting](http://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior) documentation (see the chart at the bottom for format specifiers). The format in the `Format string` field is the default format. #### Insert Event Data into Variable ![Instert Event Data into Variable](../../images/insert_event_data_into_variable.png){ width=600 } Click the `Edit Action Settings...` button and it will open a dialog (shown above). Here you will select the variable and, optionally, the `Path string` if you want to access specific parts of the event data. More information can be found on the [Path Strings](../../scripting/reference/event-data-paths.md) page. #### Toggle Variable ![Toggle Variable Action Image](../../images/toggle_variable_action.png) Click the `Edit Action Settings...` button and it will open a dialog (shown above). Here you will select the variable and the values that will be toggled. The first 4 options (`true/false`, `on/off`, `yes/no`, `enabled/disabled`) on the `Toggle values` menu are self explanatory (although note that values will be converted to lower-case for comparisons). The last option, `Custom Values`, will allow you to specify the values. We will first try to match the custom variables without converting to lowercase (some unicode characters behave oddly when lowercased) and if we don't find a match then we'll convert. Finally, if nothing matches, we'll just set the value to the first value. #### Set Variable to Variable ![Set Variable to Variable Action Image](../../images/set_variable_to_variable_action.png) Click the `Edit Action Settings...` button and it will open a dialog (shown above). Here you will select the destination and source variables. ### Notification Actions ![Notification Actions Type Menu Image](../../images/notification_actions_type_menu.png) In the standard install of Indigo, there is one action: Send Email. Plugins are allowed to add menu items to this category so there may be more options if you've installed some 3rd party plugins. #### Send Email { #send-email } Email support is handled by the Email+ plugin - see [the plugin's documentation](../../plugins/email.md) for details on how to configure and use it. #### Send Indigo Log Email Use this action to send the Indigo log in an email. ### Z-Wave Actions ![Z-Wave Actions Type Menu Image](../../images/zwave_actions_type_menu.png) #### Modify Configuration Parameter ![Z-Wave Modify Configuration Parameter Action Image](../../images/zwave_modify_config_param_action.png) Some Z-Wave devices provide configuration options through the use of configuration parameters. These are generally outlined in the documentation that comes with a device. Indigo often times support setting these parameters directly in the device config dialog, but because of the sheer number of Z-Wave devices we can't add every one. This menu item will allow you to set any config parameter that a device accepts. **Note**: this process can cause your device to not function correctly if incorrect parameters are entered so you'll want to make sure you are very careful to use only the params specified for the specific device. #### Send Raw Z-Wave Command ![Z-Wave Send Raw Command Action Image](../../images/zwave_send_raw_command_action.png) This menu selection can be used to send arbitrary Z-Wave protocol-level commands to any Z-Wave device. This is generally only useful when Support instructs you to do so. Note battery operated devices allow for the option to queue the command to be sent the next time the device wakes. #### Inclusion Mode Commands This set of actions all require no parameters - they simply control the inclusion process. - `Start Controller Inclusion Mode` - this will tell the Z-Wave interface to start looking for inclusion requests on the network, and when one is seen it will include the device **without** encryption (recommended for the majority of device types for performance reasons) - `Start Controller Inclusion Mode with Encryption` - this will tell the Z-Wave interface to start looking for inclusion requests on the network, and when one is seen it will include the device **with** encryption (recommended only for the most sensitive device types like locks) - `Start Controller Exclusion Mode` - this will tell the Z-Wave interface to start looking for exclusion requests on the network, and when one is seen it will exclude the device (remove it from the network) - `Stop Inclusion / Exclusion` - this will tell the Z-Wave interface to stop looking for inclusion and exclusion requests on the network #### Start Z-Wave Network Optimize ![Z-Wave Network Optimize Action Image](../../images/zwave_network_optimize_action.png) Indigo can optimize your Z-Wave network by having devices rediscover which devices they are close enough to communicate with. This information is then reported back to the Z-Wave Controller so network routing tables can be updated. You can specify `All Devices` (which will go through all of your devices - this can be quite time consuming so use during low-traffic times) or you can specify a single device. There is a corresponding `Stop Z-Wave Network Optimize` action that will stop the optimization process. ### Insteon Actions ![Insteon Actions Type Menu Image](../../images/insteon_actions_type_menu.png) #### Execute Insteon Scene { #execute-insteon-scene } ![Insteon Scene Action Image](../../images/insteon_scene_action.png) There are nine commands that you can send to an Insteon scene: - `Scene On` - send the ON command to every device in the group - if the device has an adjustable ramp rate and/or brightness value, those are honored - `Scene Instant On (to 100%)` - send the ON command to every device in the group - but ignore ramp rates and default brightness values (everything comes on full immediately) - `Scene Off` - send the OFF command to every device in the group - if the device has an adjustable ramp rate it will be honored - `Scene Instant Off` - send the OFF command to every device in the group - ignore ramp rates (everything goes off immediately) - `Scene Increase by 3%` - will send a command to the scene to increase all members by 3% - `Scene Decrease by 3%` - will send a command to the scene to decrease all members by 3% - `Scene Start Increase` - will send a command to the scene to begin ramping the devices in the scene up (brighten for lighting loads). The ramping will continue until the `Scene Stop Increase/Decrease` command is sent. - `Scene Start Decrease` - will send a command to the scene to begin ramping the devices in the scene down (dim for lighting loads). The ramping will continue until the `Scene Stop Increase/Decrease` command is sent. - `Scene Stop Increase/Decrease` - will send a command to the scene to stop any ramping started by the Scene Start commands above. The `Scene:` popup represents the Insteon scene number - any name that you've assigned to that scene will also show up in the text field beside it (you can change the name there also if you like). Use the `Send On` and `Send Off` buttons to test your scene commands. The table will show you what devices are in the scene. The columns are pretty self explanatory perhaps with the exception of the Status column. That column shows the status of the device's **settings** in the scene (edited, added, deleted, etc.) because you can modify scenes without actually performing the link sync. If you have a scene that isn't working correctly the value in that column might help you understand why it isn't working. Finally, if you need to add, modify, or delete a device from the scene, click on the `Modify this Scene...` button. See the [Indigo and Insteon Link Management](../interfaces/insteon/index.md#managing-insteon-links) page for more details. #### Send Raw Insteon Command ![Insteon Raw Action Image](../../images/insteon_raw_action.png) This action will allow you to send a raw Insteon command to any Insteon device. You can send standard messages (2 bytes) or extended messages (16 bytes). You can also have the results of the command inserted into a variable for later processing. ### Plugin Actions Plugins may provide actions that can perform a variety of tasks. See the plugin's documentation for more details. You can [check the list of built-in plugins](../../plugins/index.md) for actions they provide. ### Action Options ![Lower Action Dialog Image](../../images/lower_action_dialog.png) There are three options to each action (and in fact are the only configurable options if you select `None` as the action `Type:`): - `Delay by hours:minutes:seconds` - this will delay the execution of the action by some amount of time (not over 24 hours) - `Override previous delay` - this will automatically delete any previous delays specified for the action above - `Speak` - if you enter text in the text box Indigo will use the voice synthesis to speak the text ## Managing Multiple Actions { #managing-multiple-actions } ![Lower Action Dialog Image](../../images/lower_action_dialog.png) Use the `Add New` button below the `Speak:` text area and the separator if you would like to add an additional action. You can then use the `Prev` and `Next` buttons to select which action's settings are being displayed. Press the `Show All` button to see a list of all the actions: ![Multiple Actions List Image](../../images/multiple_actions_list.png) You can then Duplicate or Delete the selected action, or use the `Up` / `Down` buttons to reorder the list of actions. To edit a specific action's settings, double-click on the action or press the `Edit...` button. ****Important!**** While you can order the actions in any order you like, Indigo will attempt to execute all actions in parallel. It's not always possible for various reasons, but that's the intent. If you want to order the execution, then you'll need to add delays which will delay the action's execution from the time of the event. So, if you have 3 actions and you want the first to execute immediately, the second to execute a minute after the event, and the third to execute two minutes after the event, then add a one minute delay to the second and a two minute delay after the third. ## Action Groups { #action-groups } Action Groups are groups of actions that can be specified separately from [Trigger](triggers.md#triggers), [Schedule](schedules.md#schedules), and [Control Page](control-pages.md#control-pages) elements, so that they can be reused. For instance, if you have a group of lights you turn on at the same time, you can create an action group for those lights and just execute that group as part of the various triggers, schedules and control page elements. If you need to add a light, you only need to add it to the group rather than edit each individual trigger, schedule, etc. that needs to control that group. Action groups are also optionally shown in remote clients so it's an easy way to control some collection of devices from those clients without having to adjust each one manually. > It is important to note that individual actions are run independently. If you need your actions to be executed in a specific order, you can use "Delay by" (see image). Very often, even short delays will be enough). To create a new action group: 1. Select `Action Group List` from the `View` menu. 1. Press the `New...` button at the top of the main window. 1. `Name` the group, for example "going to bed." 1. Optionally give the group some `Notes`. 1. Select the action `Type:` and select the various options. (See the [actions](#actions) section above for details on all of the action's settings, and information on [managing multiple actions](#managing-multiple-actions) to the Action Group) ![Action Group Dialog Image](../../images/action_group_dialog.png) Indigo includes a built-in Web server that allows remote execution of your Action Groups from [Indigo Touch](http://www.indigodomo.com/touch) or any modern Web browser (Safari, Firefox, Opera). Follow the instructions in the [Starting Indigo Server](../getting-started/installation.md#starting-indigo-server) section of the Getting Started guide to make sure that the following options are enabled: `Start and connect to Indigo Server on this computer`, `Allow remote access`, and `Enable iPhone, iPod touch, and remote Web browser access`. The `Display in remote UI` option at the bottom of the dialog will make this action group show up in remote clients like Indigo Touch or the web pages. When you see them in those UIs you can tap/click the action group and it will be executed so it's an easy way to control scenes from those clients. !!! tip "TIP" if the Action Group is in a folder, that folder must also be marked for [Remote Display](../mac-client/home-window.md#outline-view). --- Conditions (https://docs.indigodomo.com/2025.2/user/concepts/conditions/) --- # Conditions { #conditions } On the Trigger and Schedule dialog, there's a tab named `Condition`. If you select this tab, you'll see something like this: ![Conditions Tab Image](../../images/conditions_tab.png) Conditions allow you to specify extra logic that's evaluated at execution time to determine if the actions associated with the trigger or schedule should be executed. Most conditions will be set to `Always` - in other words, there are no additional conditions associated with the trigger or schedule. However, there are many situations where you need to factor in other information at execution time that can help determine if the actions should be performed. A simple example might be a motion sensor triggering a light - you might want motion detected by the motion sensor to turn on a light, but only between the hours of 6pm and 11pm. You can currently do that by creating a schedule that enables a trigger at 6pm and another one that disables the trigger at 11pm. But that's 3 moving parts (two schedules and the trigger). Using a condition, you can reduce that to just a single trigger and the following condition: ![Motion Sensor Condition Example Image](../../images/motion_sensor_condition_example.png) This condition says that if the current time is greater than 6pm and less than 11:00pm then the actions will be executed. Another example that can't be accomplished easily without writing a script is having multiple conditions. For instance, let's say you have a sprinkler schedule that runs periodically. However, there are several conditions in which you don't want the sprinkler to run: 1. if you've left your windows open 1. if it's too cold outside 1. if it has rained more than 3/4 of an inch in the last 24 hours. Let's assume that you have a script running that populates Indigo variables with data from a weather station: "rain_total" holds the rain total for the last 24 hours, and "outside_temp" holds the temperature in Fahrenheit. Further, let's say you have a variable, "windows_open", that's either true or false indicating whether the windows in your house are open or not. Given all of these conditions, here's how the condition editor would look: ![Sprinkler Condition Image](../../images/sprinkler_condition.png) This is a negative condition - if `None` of the rules are true then the actions will execute. It could also be written as an `All` rule if you switched the tests around: ![Sprinkler Condition Using All Image](../../images/sprinkler_condition_using_all.png) This version says that if `All` the conditions are true (windows aren't open, temperature is greater than 40, and rain total is less than 3/4 of an inch) the actions will execute. Note for variable comparisons: `is true` will evaluate **true** if the value is one of these: "true", "on", "yes", and "1" and will evaluate **false** if it's anything else. `is false` will evaluate **true** if the value is one of these: "false", "off", "no", and "0" and will evaluate **false** if it's anything else. To add a rule, click the plus (+) button next to a rule to add a rule directly below it. Click the minus (-) button to remove a rule. To create a sub-rule group, hold down the option key on your keyboard and the plus button becomes an ellipsis (…) button. Clicking on this will create a sub-rule group (Any, All, None). For instance, here's an arbitrarily complex (but nonsensical) rule with several sub-groups: ![Complex Condition Rule Image](../../images/complex_condition_rule.png) To reorder rules, simply drag them around. As you can see, the condition rule editor is extremely powerful - you can create complex multiple conditional logic without resorting to a script. And it's available to Lite users as well (whereas script conditions aren't). The Insert into Event Log Window button will put a textual representation of your rule into the event log for handy copy/paste into a forum post - this will help others see what your logic is to assist in debugging: ```text "All" "of the following rules are true" "If dark" "Any" "of the following rules are true" "If current date" "is between" 8/1 "and" 8/31 "If variable" houseMode "is equal to" "value" "away" "None" "of the following rules are true" "If variable" test1 "is equal to" "variable" test2 "If variable" test3 "is between" "5" "and" "10" ``` ## Condition Scripts If, however, you still can't express your conditional logic using the rule editor, you can still select the `If Python script returns True:` radio button and write a Python script that programmatically returns **True** or **False**. For example, ```text if [some condition evaluates as True]: return True # the condition passed so your trigger or schedule will execute else: return False # the condition didn't pass so your trigger or schedule will not execute ``` The `*return False*` component is optional since only a `*True*` return will be acted upon. In Indigo {{ version }}+, you can access the new Event Data dictionary that's passed through the chain to perform custom logic. For instance, you can create a simple condition script that will look at the event data from a Z-Wave Command Received trigger to decide if you want the actions to process or not. This is what the event_data dictionary would look like from a Z-Wave Command Received trigger: ```text event_data = { "event-indigo-id": 886317539, "event-plugin-event-id": "zwaveCommand", "event-plugin-id": "com.perceptiveautomation.indigoplugin.zwave", "event-plugin-name": "Z-Wave", "event-type": "PluginEventTrigger", "timestamp": "2025-07-25T17:18:25", "zwavecmd-device-id": 1191650674, "zwavecmd-node-id": 2, "zwavecmd-scene-id": 255 } ``` The script could look something like this: ```text # Devices that you want to continue processing my_device_list = [1234567890, 837603829, 1191650674] if event_data["zwavecmd-device-id"] in my_device_list: return True return False ``` The `event_data` dictionary is automatically made available to your script, so you can just get the Indigo device ID out of that dict and see if it's in the list of device IDs in your condition script. If it is, it will continue processing, if not it will stop processing. --- Control Pages (https://docs.indigodomo.com/2025.2/user/concepts/control-pages/) --- # Control Pages Control pages are graphical pages that you use to control Indigo, either through a web browser or Indigo Touch. Indigo has built-in control pages available on the web (Indigo Touch has built-in base functionality that matches them), and also allows you to design custom graphical pages - from the background image and/or color to the placement of text labels, control images, and what actions clicking/tapping on those images performs. ## Control Page Editor { #control-page-editor } To create a new Control Page, select `View->Control Pages`, then click the `New...` button above the control page list. You will be shown the control page editor window: ![Control Page Editor Image](../../images/control_page_editor.png) The editor window has 4 areas - the first is the the global control area. In this area you'll find buttons to create new page elements (labels, controls, etc), duplicate existing elements, delete elements, and turn on/off some features of the editor. Select `Snap to grid` to have page elements snap to a grid that overlays the design area (see the next item). Select `Show grid` to show the aforementioned grid. Select Edit z-order to show you the order of the page elements and allow you to change them. Higher numbered elements are drawn last, so if you have overlapping controls the one with the highest order will be drawn last. The second area is the design area. This is where you'll graphically lay out your control page. It operates much like many drawing programs - you select objects and drag them around to position them. The default new control page contains two elements: a graphical server status icon and a server status text area. By the way, you can have only one of each of these element types on your control page. The white area is the visible area that you'll see on the web page or in Indigo Touch. The gray area is just the image border area - items in the gray area will not show on the control page. If you have the `Edit z-order` checkbox described above checked, you'll see controls like this for each page element: ![Page Element Examples Image](../../images/page_element_examples.png) In the example above, you see 4 page elements: the graphical server status element, the server status text area, a device state element using a ceiling fan image, and another device state element using a lightbulb image. The ceiling fan element is selected, as you can tell by the blue brackets at each corner of the image. Because I also have the z-order checkbox selected, you see the `z: #` next to each element, and above and below that for the selected element you see up/down arrows, one with a line above/below and one without. The arrows that aren't pointing to lines will move the element up/down one step at a time. The arrows that point to lines will move the element to the top/bottom of the element hierarchy. If you have a page element that's showing a textual description, then you'll notice a little resize icon at the lower left corner of the text - this control will allow you to adjust the width of the text field so that it can accommodate longer test. Text can only be a single line at the moment but you can make it as wide as the entire page. The third area is the page element detail area. This area changes based on what's selected in the design area and what options are selected for the page element. If no page element is selected in the design area, then you'll see the information about the page itself: ![Control Page Information Image](../../images/control_page_information.png) This is where you set global information about the page itself. The `Page Name:` and `Description:` fields are pretty self-explanatory. Check the `Hide Tab Bar at bottom of Indigo Touch when shown` to have the bottom navigation bar hidden when this page is viewed in Indigo Touch. You can use a `Background image:` by selecting it from the popup, such as a floorplan or other image. You can add your own image by creating a PNG file and adding it to the backgrounds folder (to open that folder in the Finder just click the `Show Folder` button - and if you add an image while Indigo is running, click the `Refresh` button to have it added to the popup). **Note**: some versions of Indigo don't like spaces in the background image name so just replace spaces with another character such as an underscore. If you aren't using a background image, your background image is smaller than the total size of the page, or if your background image has transparency identified, you can specify the color of the rest of the background by clicking on the `Background color:` colorwell. Finally, you can have the page size constrained to the size of the background image or you can specify your own page size. Here are a few design tips for designing pages for Indigo Touch on various devices: - iPhone/iPod touch - Viewable Portrait size: 320x416 - Viewable Landscape size: 480x268 - iPad - Viewable Portrait size: 768x960 - Viewable Landscape size: 1024x704 If you look closely at the numbers, you'll see that on the iPad, the Indigo Touch header that's displayed above the control page is 64pix regardless of screen orientation. However, it appears that the header is decreased to 52px when in landscape mode on the iPhone/iPod touch. This is verified in both iOS 3.x and 4.x. [Page element details](#page-element-details) will vary based on what's displayed - we'll go into those details a bit later. The final section is the bottom control area - the `Help`, `Cancel`, `Browser Preview`, and `Save and Close` buttons are also pretty self-explanatory (the Help button may have gotten you to this page in fact). ### Other Editor Features The Control Page editor now supports Cut/Copy/Paste between control pages as well as drag and drop. You can drag and drop page elements between pages and drop devices on a control page to add them. You can also drag control page elements to the Finder, which will create a clipping file. You can then switch databases, and drag those clipping files back onto a control page and it will import them. The clipping files will initially be titled with the XML text that's being exported, but you can change the name of the clipping file - so if you're sending it to someone else you can give it a more descriptive name. This is a good way to share groups of page elements with others. The Control Page list in the [Home Window](../mac-client/home-window.md#home-window) will allow you to drag Control Pages out to the Finder. This will create a clipping file that can then be dragged back onto the Control Page list (for instance, in another database) and the whole control page will be recreated. Note, however, that if the target doesn't have the same devices, variables, action groups, and images, you'll need to edit the resulting page elements. The clipping files will initially be titled with the XML text that's being exported, but you can change the name of the clipping file - so if you're sending it to someone else you can give it a more descriptive name. This is a good way to share control pages with others. To import a control page, just drag it from the Finder to the Control Page list. ### Page Element Details As we stated above, each page element represents some kind of object - also called controls. Specifically, there 6 different types of controls. #### Device State ![Device State Page Element Image](../../images/device_state_page_element.png) When you select `Device State` from the `Display:` popup, you're shown a list of devices in the `For:` popup. Once you've selected a device, the next popup will show the list of states for that device. The next row of options allow you to have the control represented by an image or in text. Images may be used to represent the state of most common devices - we provide quite a few different images. If you'd like to use custom images, check out the section below on creating and using [custom images](#custom-images-on-control-pages). The next row of options is for the `Caption:` field - static text that can be used as a label for the control. It can be placed on any side of the control (left, right, above, below) as well as centered on top (good for buttons). The next two options, `[Client action:](#client-actions)` and `[Server action:](actions.md#server-actions)` allow you to specify what happens when the control is interacted with (via click or tap) and are describe in separate sections below. #### Variable Value ![Variable Value Page Element Image](../../images/variable_value_page_element.png) Variable Value controls have the exact same options as [device state](#device-state) controls, but rather than use the device state it uses the value of the variable you selected in the `For:` popup to select the correct image or as the text to display. #### Static Image/Caption ![Static Image Page Element Image](../../images/static_image_page_element.png) Static Image/Caption controls are simpler than the other controls: they can have an image and/or static text caption. #### Refreshing Image URL ![Refreshing Image URL Page Element Image](../../images/refreshing_image_url_page_element.png) Refreshing Image URL controls allow you to specify a URL to an image that is refreshed periodically (specified in the `Refresh rate:` popup). Just specify the image size, URL, and refresh rate. You can include markup that will do variable (%%v:VARIDHERE%%) and device state (%%d:DEVIDHERE:STATEIDHERE%%) substitutions as a part of a refreshing image URL. Note: file paths that contain spaces will need to have the spaces escaped with a backslash - *`/some\ path/that\ has/escaped\ spaces/`*. When you use substitutions in refreshing image URLs, you will receive a warning in the event log (when you edit the control page -- not each time the control page is displayed): *`Warning (client) control page image URL contains a substitution: you will have to manually specify the image size for it to display correctly`* As the warning suggests, you'll have to manually set the width and height of the control to match whatever the refreshing image will be after the substitution or the image will be distorted when it's displayed. #### Server Status Text ![Server Status Text Page Element Image](../../images/server_status_text_page_element.png) This control will show the latest status update message from the server as text. You can have only one of these controls on your control page. #### Server Status Icon ![Server Status Icon Page Element Image](../../images/server_status_icon_page_element.png) This control will show a spinning icon when the server is performing some request for the control page. You can have only one of these controls on your control page. #### Client Actions ![Client Actions Control Page Popup Image](../../images/client_actions_cp_popup.png) For every page element, you can have one of three client actions performed whenever the page element is clicked/touched: 1. `Popup UI Controls` - this will cause the client to pop up a dialog with the controls appropriate for the device or variable selected (it doesn't do anything for other page element types). 1. `Advance to Control Page` - this will cause the client to display the selected control page. This version will leave bread crumbs so you can come back to the current page using the back button (a new browser window if the `Opens new window` checkbox is checked). 1. `Replace with Control Page` - this will cause the client to display the selected control page. This version will replace the current page such that the back button won't go back to the current page (no bread crumbs). 1. `Back to Previous Page` - this will cause the client to go back to the previous page. Useful if you want to create your own navigation. 1. `Back to Control Page List` - this will cause the client to go back to the list of available control pages. Useful if you want to create your own navigation. 1. `Go to External URL` - this will cause the client to open the provided URL in a browser window (a new browser window if the `Opens new window` checkbox is checked). In Indigo Touch, it will cause Mobile Safari to open and show the page specified by the URL. #### Server Actions Every page element may also have server actions performed - these are the typical [actions](actions.md#actions) described above. Just like [Triggers](triggers.md#triggers) and [Schedules](schedules.md#schedules), you can specify a single action or multiple actions. ### Show in Browser In the main Control Pages view in the Indigo Client UI, you can select a Control Page and then click on *`Show in Browser`* to view the page in your default browser. Once that's done, you can take note of the URL in your browser's address field should you want to copy it and create a bookmark or other link to load the page directly in the future. The direct URL will look something like, *`http:*my_indigo_ip:8176/web/controlpage.html?id=123456789`* where *`123456789`// is the Indigo ID of the page. ## Custom Images on Control Pages { #custom-images-on-control-pages } There are several sub-folders to add custom images to control pages inside: ```text /Library/Application Support/Perceptive Automation/Indigo YYYY.R/Web Assets/images/ ``` Note Indigo 7 and earlier stores the images inside: ```text /Library/Application Support/Perceptive Automation/Indigo X.Y/IndigoWebServer/images/ ``` And also note that's the Library folder at the top level of your hard drive, not the one in your user directory. You'll find a couple of directories under there: - `backgrounds` - put images here that you want to use for control page backgrounds - `controls` - in this directory, there are 3 more: - `devices` - in this directory, put in images to represent devices. In Indigo 4.1 and higher, we've added some heuristics that will allow you to add many more images. See [Image Selection Heuristics](#image-selection-heuristics) below for details. - `static` - in this directory, you can just put static images that are shown when you select `Static Image / Caption` from the `Display:` popup in the control page editor. - `variable` - in this directory, you can put images that represent variables. In Indigo 4.1 and higher, we've added some heuristics that will allow you to add many more images. See [Image Selection Heuristics](#image-selection-heuristics) below for details. So, adding images is as simple as inserting them into the correct directory above based on what you want to use them for and restarting the Indigo Server. Images should be in the PNG format. When upgrading Indigo, v7 and above should automatically move over any custom images, but you may need to move them over manually if upgrading from an older version. Also, if you edit an existing custom image, you may need to restart the client (or clear the cache in Indigo Touch from the settings dialog) in order to see the changes. ### Image Selection Heuristics Before v4.1, we had a simple mechanism for selecting images based on values: for devices, you could have a file named `MyDeviceImage.png`, which would be shown if the device was OFF, and `MyDeviceImage+on.png`, which would be shown if the device was ON. Likewise, for variables, you could create an image called `MyVariableImage.png`, which would show when false, and `MyVariableImage+true.png` which would show when the variable value was true. We've expanded the image selection criteria so that it can find much more interesting images based on values. To signify that an image should use these more complex image heuristics (described below), end the base file name with a "+": `ImageName+.png`. The "+" at the end is a hint to the Indigo Web Server and Indigo Touch that it may need to contact the server for the right image. The heuristic works like this now: 1. Search for an image of the form `ImageName+VALUE.png`, where VALUE is the current value. For ON/OFF type devices it will be on and off; just like before. For devices that have numerical state values (brightness, temperature, etc.) it will be the numerical value for that state. For variables, it's a bit different: it will be an exact match of the current value of the variable. So if your current variable value is "summer", then we'll look for an image named `ImageName+summer.png`. Spaces should work correctly as well, but other special characters may cause problems, so be careful as you plan specific values. If the variable value is empty then the base image `ImageName+.png` will be used. 2. If a match isn't found above, then Indigo attempts to find a numeric match in the following way: search for an image with a name that is the closest increment counting by 5. So, for instance, if the value of the device or variable is 13 (and there was no exact match to `ImageName+13.png`), we'll look for an image named `ImageName+15.png`. If that isn't matched, we'll look for the next closest increment counting by ten (in this example, `ImageName+10.png` since 13 is closer to 10 than to 20). Next, we'll look for the next increment counting by 20 (`ImageName+20.png`). Finally, if that isn't matched, we'll look for the next increment by counting by 25 (`ImageName+25.png`). This will work for variables that are valid integers (whole numbers) as well. 3. If neither #1 nor #2 match, then the intention was to have the base image `ImageName+.png` displayed. (A bug in image selection in Indigo 5 and fixed in later versions however will result in no image. A workaround is to add an image named `ImageName+true.png` which will be used in this case.) Note that the dimensions of all the images with the same base image name must be the same. If they are not the layout of some of the images will be incorrect. We think this will give you much more flexibility in displaying images that match your needs. This works for all control pages regardless of whether they're viewed in a web browser or in Indigo Touch. --- Deletion Dependencies (https://docs.indigodomo.com/2025.2/user/concepts/deletion-dependencies/) --- # Deletion Dependencies !!! abstract "In this guide" When deleting an Indigo object, a confirmation dialog shows any dependent objects that will also be affected or deleted. You can also inspect dependencies at any time by right-clicking an object and choosing Show Dependencies. When an object (Device, Trigger, Schedule, Action Group, Control Page, or Variable) is deleted, you will be presented a dialog to confirm. If there are any other objects that are dependent on the object you're deleting, the dialog will show you the dependencies. ![Dependency Sheet Image](../../images/depencency_sheet.png) If you double-click on the dependency, the edit dialog for that object will open so you can change/remove it. If you want the dependent objects to be deleted, just click the `Delete` button. Specifically - if the object being deleted will render the dependent object useless. For instance, a trigger event that's defined with the device you're deleting in the device state changed definition, then the entire object will be deleted - in this case the trigger. Same with conditions. If the object is used in one of several actions or if it's used on a control page, just the action or page element will be deleted. You may also right-click on any object to pop up the contextual menu and a new menu item, Show Dependencies, will allow you to open a separate window showing the dependencies for the object: ![Dependency Window Image](../../images/dependency_window.png) This should be a big help in figuring out what a given object's dependencies are and allow you to quickly change those relationships. --- Devices (https://docs.indigodomo.com/2025.2/user/concepts/devices/) --- # Devices { #devices } Devices in Indigo are "things" that Indigo can interact with. Not only can Indigo interact with Z-Wave®, Insteon, and X10 devices but Indigo also supports devices provided by third party plugins. This greatly expands the kind of devices that can be defined in Indigo. There are two primary ways you can use a device in Indigo: you can use changes in its various states to [trigger](triggers.md#triggers) some actions (e.g. when the motion sensor detects motion) and you can tell a device to perform some action (e.g. turn on the porch light). Indigo also provides you various ways to interactively control these devices and inspect their state (Indigo Touch, Indigo Web Server web pages, etc.) Here is the device dialog: ![Device Dialog New Image](../../images/device_dialog_new.png) The first thing that you need to select is the `Type`. Indigo includes support for the following device types: [Z-Wave](../interfaces/z-wave/index.md), [Insteon](../interfaces/insteon/index.md), and [X10](../interfaces/x10/index.md) (click on the types for details of how to manage devices of that type). Indigo also has a [Virtual Devices interface type](../interfaces/virtual-devices.md) and the following plugins which add additional device types: - [EasyDAQ Relay Card](../../plugins/easydaq_1.md) - [NOAA Weather](../../plugins/noaaweather.md) - [Timers and Pesters](../../plugins/timersandpesters.md) If you add other 3rd party plugins that supply devices, they will show up on the `Type` menu as well. There are over 95 3rd party plugins listed on our [Plugin List](http://www.indigodomo.com/plugins) that cover many other types of devices, including alarm panels, media servers, A/V equipment, and much more. If you're a programmer and would like to develop plugins, check out our [Plugin Development](../../plugin-dev/index.md) section for all the docs you need to get started building Indigo plugins. Once you've selected a device type, the fields and buttons between the `Type` popup and the tab view at the bottom of the screen will adjust based on the type of device that you select. The tab view will show at least one tab (titled "Settings") for every device. Here's an example of a Z-Wave module: ![Device Dialog Z-Wave Image](../../images/device_dialog_zwave.png) Inside each tab, you have the `Name` field, which represents the unique name of this device. Next you have the `Notes` field - it's a free-form text field that you can put anything into you want - perhaps to help describe what features you're using, where it's physically located, or significant triggers that use it. You can put whatever you like in that field. ## Device Options At the bottom of the tab, you have two options for each device: 1. `Enable Indigo communication` checkbox - this is useful if you have a device that you're temporarily taking out of service or moving. If you uncheck this checkbox, Indigo will not attempt to communicate with it in any way - so you won't receive errors in the Event Log when any action is taken for this device. 1. `Display in remote UI` checkbox which, as it says, lets you see this device in remote client applications. ## Devices with Multiple Personalities Some devices have multiple "personalities" - in other words a single physical device can actually represent multiple devices. The Insteon FanLinc is one example: it's a single module that has Fan Speed controls and Dimmer controls. In Indigo, each of these is represented as a different device in the various device lists. However, when you edit one of them, each device will be represented in a single dialog with multiple tabs: ![Fanlinc Dimmer Image](../../images/fanlinc_dimmer.png) ![Fanlinc Fanspeed Image](../../images/fanlinc_fanspeed.png) ![Device Dialog Multiple Personalities Image](../../images/device_dialog_multiple_personalities.png) To get started adding devices to Indigo, visit the page that's appropriate for the technology you're using: [Z-Wave](../interfaces/z-wave/index.md), [Insteon](../interfaces/insteon/index.md), [X10](../interfaces/x10/index.md), or visit the documentation for the [plugin](http://www.indigodomo.com/plugins) that supports the devices you want to add. --- Managing Plugins (https://docs.indigodomo.com/2025.2/user/concepts/plugins/) --- # Plugins ## Managing Plugins Indigo includes the ability to use plugins developed by 3rd party developers (see our [Plugin Store](https://www.indigodomo.com/pluginstore/) for available plugins) and includes a few useful plugins with the Indigo installation. ### Installing/Updating Plugins { #installing-updating-plugins } Installing new plugins is pretty simple (updating plugins is the same process, it will just replace the existing one if there is an older version installed). We're going to describe a couple of ways of installing them primarily based on where you get the plugin. While you may get a plugin from anywhere, we recommending getting them from our [Plugin Store](https://www.indigodomo.com/pluginstore/). To download and install a plugin from the store, this is the process (we're using Safari in these steps, so you may need to adjust accordingly if using another browser): 1. In Safari on your Indigo Server Mac, go to the [Plugin Store](https://www.indigodomo.com/pluginstore/) and navigate to the Plugin you want to install. ![Plugin Store Detail Image](../../images/pluginstore_detail.png) 1. Make sure that the plugin is supported by this Indigo release (look for the **Requires** field). This is an important step because there may be older plugins which aren't compatible with the current version of Indigo. 1. Click the `Download Latest Release` button, which will download the plugin. If the release isn't compatible with your version of Indigo, you can click on the **Releases** tab, and look through the list for a version that works with your version of Indigo (click on it and then click the `Download this release` link). 1. When the download is complete, click on the down arrow in the title bar which will show the plugin download.![Plugin Store Safari Download Image](../../images/pluginstore_safari_download.png) 1. Most plugins will show the plugin directly as the above image. If it does, double click it and proceed to step 8 below. 1. If you see a folder icon (usually with an odd name) in the download dropdown rather than the plugin icon, double-click on the folder and it'll switch to the Finder with the download folder open. 1. In the Finder window that's now showing, you should see the plugin which will end in `.indigoPlugin`. Double-click that file.
![Plugin Install Permission Image](../../images/plugin_install_permission.png) 1. You will be switched to the Indigo Client app, and you'll a dialog window which asks you if you want to install and enable the plugin. You will see a different dialog if the version number is lower than an already-installed version; for safety, Indigo will ask you to confirm that you want to downgrade the plugin. 1. **NOTE**: due to a bug in some macOS releases, this step may try to launch a previous version of Indigo. If it does, quit it, then right-click the plugin file, select the `Open With` menu item, then select Indigo {{ version }}.![Open With Menu Item Image](../../images/openwithmenuitem.png) 1. Click the `Install and Enable` button. 1. Done! Many plugins require some kind of configuration for the plugin itself. If that's the case, then when you click the `Install and Enable` button, the plugin's configuration dialog may automatically popup: ![Plugin Configuration Dialog](../../images/plugin_config_dialog.png) Complete the dialog as necessary (sometimes, as with the example above, you don't have to do anything) and click the `Save` button. **Note**: if you click the `Cancel` button, the plugin may not be fully operational until you completely fill out the dialog and save it. That's it. As you can see, the experience using the Plugin Store in Safari is straight-forward. However, you may find plugins in other places. When you get a file that ends in `.indigoPlugin` on your Indigo Server Mac, you can always double-click it in the Finder and then you jump to step 8 above. We encourage all of our 3rd party plugin developers to put their plugins in the Plugin Store so it's easy to find them - you should be cautious about installing plugins that you get from other locations. ### Plugin Menus in Indigo There are a variety of menus in Indigo which will reflect plugin functionality. The first is the main Plugins menu in the Indigo menu bar. #### Main Plugins Menu This is the meta-menu for many things related to plugins. ![Plugins Menu Image](../../images/plugins_menu.png) The `Reload Libraries and Attachments` menu item will allow scripters to add Python libraries to specific locations and use them in Indigo (see [Shared Classes and Methods in Python Files](../../scripting/tutorial.md#shared-classes-and-methods-in-python-files-python-modules) for details). The next section of the menu is dedicated to each plugin that's installed on your system. As you can see, you can tell at a glance the status of the plugin itself: - ![Gray Dot Image](../../images/idpng_dot_gray_2x.png) a gray dot means the plugin is disabled - ![Green Dot No Arrow Image](../../images/idpng_dot_green_2x.png) a green dot with no arrows means the plugin is enabled and up to date. - ![Green Dot Black Arrow Image](../../images/idpng_dot_greenblackarrow_2x.png) a green dot with a black arrow means the plugin is enabled and there is an update available that will work with your Indigo version - ![Green Dot Red Arrow Image](../../images/idpng_dot_greenredarrow_2x.png) a green dot with a red arrow means the plugin is enabled and there is an update available but it won't work with your version of Indigo There are yellow versions of the dots - the meaning is the same as the green version except it's indicating that the plugin is using an API version that was deprecated with Indigo 2023. You should check the [Plugin Store](https://pluginstore.indigodomo.com) for an updated version or contact the plugin developer. - ![Yellow Dot With No Arrows Image](../../images/idpng_dot_yellow_2x.png) a yellow dot with no arrows means the plugin is enabled but needs updating before the next Indigo release - ![Green Dot with Black Arrow Image](../../images/idpng_dot_yellowblackarrow_2x.png) a yellow dot with a black arrow means the plugin is enabled and there is an update available that will work with your Indigo version - you should install the update before upgrading to the next version of Indigo - ![Green Dot with Red Arrow Image](../../images/idpng_dot_yellowredarrow_2x.png) a yellow dot with a red arrow means the plugin is enabled and there is an update available but it won't work with your version of Indigo There is also a red version of the dot, which can mean several things. - ![Red Dot Image](../../images/idpng_dot_red_2x.png) the plugin is not compatible with your version of Indigo and we are not aware of a version that will work with your version of Indigo. - ![Red Dot Image](../../images/idpng_dot_red_2x.png) the plugin has failed due to a catastrophic error that has caused the plugin to crash or has caused Indigo to stop the plugin. An error will be output to the log to explain the problem. - ![Red Dot Image](../../images/idpng_dot_red_2x.png) the plugin's *`self.stopPlugin()`* method has been called for any reason. We'll discuss how to change/deal with those states later, but this menu provides a very simple visual dashboard for plugin status. The `Manage Plugins...` menu item will open the [the Plugins tab of the Indigo Preferences](#the-plugins-tab-of-the-indigo-preferences) and is discussed below. The `Plugin Store` menu item will open the [Plugin Store](https://www.indigodomo.com/pluginstore/) in your default browser, and the `Show Scripts Folder` will open the Scripts folder (where you can save external scripts in a version-agnostic way). The `Open Scripting Shell` menu item will launch the Terminal app and open a window with a Python shell running with all the Indigo API loaded. See the [Indigo Scripting Tutorial](../../scripting/tutorial.md) for more information. #### Individual Plugin Submenu Each plugin installed will have its own submenu on the `Plugins` menu. For instance, here's the 3rd party [Harmony Hub plugin](https://www.indigodomo.com/pluginstore/32/) submenu when it's enabled: ![Plugin Enabled Submenu Image](../../images/plugin_enabled_submenu.png) The top section will be available for every plugin - the first menu item will Enable/Disable a plugin and is a toggle - select `Enable` to enable the plugin and `Disable` to disable the plugin. - the `Reload` menu item will reload the plugin and will only be present if the plugin is enabled. - there may be a couple more menus if you have the `Enable debugging menus` item selected in [#the Plugins tab of the Indigo Preferences](#the-plugins-tab-of-the-indigo-preferences) (and are discussed in that section). The next section contains: - the `Configure...` menu item will open a config dialog if the plugin has plugin-specific configuration items (if not or if the plugin is disabled, it will not be present). - the `Show in Plugin Store...` menu item will open the plugin's entry in the Plugin Store in the default browser. If there is an available plugin update that will work on your version of Indigo, the menu title will be `Download New Version...` which will also open the plugin's entry in the Plugin Store in the default browser so you can download the new version, and if there is an incompatible version the menu title will be `Incompatible Version Available...` which will drop a sheet showing what version of Indigo is required to run the new plugin version. This menu item will always show, but if it's grayed out it means the plugin isn't from the [Plugin Store](https://www.indigodomo.com/pluginstore/). - the `Copy Plugin ID` menu item will always show, and when selected will copy the plugin's unique ID for use in scripts. - the `About...` menu item will always show the current version of the plugin and will always be enabled. Any sections below this are specific to the plugin and will contain commands that the plugin presents to the user. If the plugin is disabled, no additional sections will be displayed. #### Other Menus with Plugin Items There are several other places where plugins may insert menu items in the Indigo Mac UI. The first place is in the [Device Create/Edit window](devices.md#devices): ![Plugin New Device Image](../../images/plugin_new_device.png) The top half of that menu allows you to create devices from the built-in interfaces. Anything in the bottom half represents plugins that supply new device types that you can create and use in the same way you work with built-in devices (switches, thermostats, etc.). The next place is in the [Trigger Create/Edit dialog](triggers.md#triggers): ![Plugin Events Image](../../images/plugin_events.png) The last section of that menu will show plugins that supply custom events that you can trigger from. The final place that plugins can add menu items is in the [Action Create/Edit window](actions.md#actions): ![Plugin Actions Image](../../images/plugin_actions.png) There are actually 3 places where plugins can add menu items to the actions windows: The first is at the bottom of the [Device Actions submenu](actions.md#device-actions): menus here are for plugin actions that act on devices. The second place is on the [Notification Action submenu](actions.md#notification-actions): plugins that supply some kind of notification action will put their actions on this submenu. Finally, at the bottom of the [Action Type menu](actions.md#actions) itself: this is where plugins will add actions that don't fit into the other two categories. As you can see, plugin integration in Indigo is quite extensive. ### The Plugins tab of the Indigo Preferences ![Plugins Tab Image](../../images/plugins_tab.png) You can also enable/disable a plugin and download a new plugin version from the Plugins tab in the Indigo Preferences. You open the config window choosing the `Indigo {{ version }}->Preferences...` menu item and clicking the `Plugins` tab or by selecting the `Plugins->Manage Plugins...` menu item discussed above. You can enable/disable the plugin from this list (by checking the `Enabled` checkbox) and you can double-click on the plugin's name (or select it and click the `Configure...` button) to open the configuration dialog for the plugin (if there is one and the plugin is enabled). Again, if this is the first time the plugin has been enabled this will open the configuration dialog for the individual plugin if the plugin supports a one. You can also see the current version of the plugin and the version of the most recent release if there is a newer version. If the newer version is not compatible with your version of Indigo, it will show up in red. Double click the new version number open the [Plugin Store](https://www.indigodomo.com/pluginstore/) entry for that plugin in your default browser where you can download the new version and install (see [Installing Plugins](#installing-updating-plugins) above for the process). At the bottom of this tab, you'll notice a section labeled `Development`. There are 2 options here to help plugin developers with the various tasks needed to develop and test plugins: - the `Enable debugging menus` checkbox will add two new menus to the plugin's menus: `Reload in Debugger` will reload the plugin and connect to the selected debugger; `Reload in Interactive Shell` will open a terminal window with a Python shell that's connected to the plugin so the developer can run commands and inspect arbitrary objects in the running plugin. - the `Use debugger` popup is used to tell Indigo which debugger to start the plugin up when you use the above menu items. See the [Python Debugger Support](https://forums.indigodomo.com/viewtopic.php?f=2&t=17039#p126068) forum post for further details on how to use these features to aid in developing a plugin. ### Uninstalling a Plugin If you would like to permanently remove/uninstall a plugin (rather than just disabling it by unchecking the Enabled button or using the menu item for the plugin), you may do so by following these steps: 1. In the Indigo app, select the `Help->Show Indigo Server Install Folder` menu item. This will switch you to the Finder and open a window to the Indigo install folder. 1. Switch back to Indigo and shut down the Indigo Server by selecting the `Indigo {{ version }}->Stop Server` menu item in the Mac client (you can leave the client app running). 1. In the Finder window opened in step 1, you'll see two folders: `Plugins` and `Plugins (Disabled)`. Depending on whether the plugin is enabled or not will determine which folder it's in. Open the appropriate folder and delete the unwanted plugin. Check to make sure that somehow there aren't plugins in both locations. 1. Switch back to the Indigo {{ version }} Mac client and click on the `Start Local Server...` button in the `Server Connection Status` dialog. The plugin will no longer show in the Plugins tab or in the Plugins menu. --- Schedules (https://docs.indigodomo.com/2025.2/user/concepts/schedules/) --- # Schedules { #schedules } Schedules are collections of actions that are executed based on a time/date (temporal) specification - they are "scheduled" to execute. Here is the Schedule dialog: ![Schedule Dialog Image](../../images/schedule_dialog.png) As with [trigger dialog](triggers.md#triggers), the schedule dialog has 3 tabs: `Schedule` is for specifying the temporal settings; `Condition` is for specifying further conditions that can determine if the actions are executed, and finally the `Actions` that will execute. [Conditions](conditions.md#conditions) and [Actions](actions.md#action-groups) are discussed in a later section. The two main sections of the `Schedule` tab separate the temporal settings into two parts - the time of day and the date. ## Time The `Time` section allows you to specify the time of day that the schedule will execute. There are 4 main options: - Absolute time - in the case of the figure above, the schedule will execute at 2:18pm local time - Time relative to Sunrise - so some number of minutes before or after sunrise depending on the value in the text box (use negative numbers for before sunrise). For more specific options click the `Customize` button (see image below): - `At sunrise` is the default (0 is inserted into the text box on the main dialog) - X `minutes before sunrise` (-X is inserted into the text box on the main dialog) - X `minutes after sunrise` (X is inserted into the text box on the main dialog) - `Force trigger time to Y at the earliest` will cause the trigger to execute at the specified time if the minutes before sunrise specification is earlier than Y - `Force trigger time to Y at the latest` will cause the trigger to execute at the specified time if the minutes after sunrise specification is later than Y ![Schedule Time Customize Sheet Image](../../images/schedule_time_customize_sheet.png) - Time relative to Sunset - works the same as the above option but with respect to Sunset rather than Sunrise - Every X hours Y minutes Z seconds - this is the typical repeating setting. You can also randomize all of the above settings by entering a non-zero value in the `Randomize by` text box. Each execution will add the randomized amount of time. Use this setting to create schedules that have that "lived in" look. ## Date The Date section allows you to specify the date(s) on which the schedule will execute. There are 5 primary options: - Absolute Date - execute this schedule on a specific date - Repeating every number of days - the default option repeats every day though you can change that to every X number of days - Absolute days of the week - execute only on certain days of the week (not that darkened days are selected, lightened aren't) - Absolute days of the month - specify each day of the month on which to execute the schedule separated by a comma - Fixed days of the month - specify advanced day settings such as third Thursday of the month Below those 5 options are a couple of modifiers which may or may not be available based on the primary option you've selected: - Repeat allows you to specify repetition. This one changes based on what you have selected above: - For absolute date you can specify yearly repetitions (e.g. every year, every 3 years, etc.) - Disabled for repeating every number of days since that already specifies repetition - For Absolute days of the week you can specify weekly repetitions (e.g. every week, every 6 weeks, etc.) - For Absolute days of the month and Fixed days of the month you can specify monthly repetitions (e.g. every month, every 4 weeks, etc.) - Start on allows you to specify some date in the future to begin on (disabled for the first option since that specifies an absolute date) - Optional End on date so you can specify an absolute date on which to stop any repetition. ## Other Options There are two options at the bottom of the dialog. The first is `Hide executions in Event Log` which will suppress any log messages indicating that this schedule has executed. This especially useful for schedules that repeat very frequently (every X minutes for instance). The second is `Automatically delete after next execution` which will do exactly that - after the next time that the schedule executes it will be deleted **regardless** of the time or date settings. --- Triggers (https://docs.indigodomo.com/2025.2/user/concepts/triggers/) --- # Triggers { #triggers } A trigger is an action (or collection of actions) that Indigo executes when some "event" occurs - the event "triggers" the actions. For instance, when a motion sensor detects motion, that's an event. When Indigo gets a signal from the motion sensor that it has detected motion, it will look for triggers that need to be executed based on that event. Here's the Trigger dialog: ![Trigger Dialog Image](../../images/trigger_dialog.png) We'll look at each of the specific trigger types next, but first we'd like to point out a couple of other features. First, you'll notice that there are three tabs in the dialog: Trigger, Condition, and Actions. The first tab lets you define the trigger event. The second allows you to specify conditions which will be evaluated at runtime to determine whether the actions should be executed. See the [Conditions](conditions.md#conditions) section for more information. Lastly, the Actions tab allows you to define the actions that this trigger will execute. See [Actions and Action Groups](actions.md#action-groups) for more information. Let's look at the various built-in events that Indigo can use in triggers along with their dialogs. ## Device State Changed ![Device State Changed Trigger Image](../../images/device_state_changed_trigger.png) Use the Type `Device State Changed` to trigger an action whenever a device's state changes. For example, you could create a Trigger Action for whenever a specific light's brightness becomes greater than 75% or for when your thermostat's temperature drops below 55 degrees. A device state can change as a result of the following: a direct Insteon or X10 command sent to that device from a remote control or motion detector, a device action Indigo has sent the device, or the reception of a new status state from the device itself. Unlike Insteon modules, not all X10 modules transmit their current states when they are changed directly at the device itself. For example, in order for Indigo to know that you have turned a hallway light on at the light switch itself, the light switch module must be a 2-way module that can transmit X10 signals back to Indigo. We recommend that our users only purchase these 2-way X10 modules in cases where Indigo needs to be aware of the status changes triggered at the device itself. Each X10 transmitter in your system will reduce the X10 signal strength throughout your home wiring. !!! tip "TIP" Read our online [troubleshooting information page](../troubleshooting/powerline-signal-troubleshooting.md) if you are having problems reliably sending or receiving Insteon or X10 commands. ## Variable Changed ![Variable Changed Trigger Image](../../images/variable_changed_trigger.png) Use the Type `Variable Changed` to trigger an action whenever an Indigo variable value changes. Variable values can change as a result of a Modify Variable action or from the user directly modifying the value. See the [Variables](variables.md#variables) section for more information about using variables. Note: `becomes true` will fire when the value becomes "true", "on", "yes", and "1" (if it wasn't one of those values previously). `becomes false` will fire when the value becomes "false", "off", "no", and "0" (if it wasn't one of those values previously). Any other value will be neither true nor false and neither will fire. ## Email Event ![Email Event Image](../../images/email_event_2023_1.png) Use the Type `Email Event` to trigger an action based on emails sent to Indigo. There are three email event types to choose from. - `String Match in Email` - use this option to trigger an event when an email is received that matches a particular string pattern you choose. Select "Edit Event Settings" to choose the Email device, match in `Message Text`, `Message Subject`, or `Message From`, and the string pattern to match. Note that this is an exact text match, so this trigger will only fire when the string matches the target text 100 percent. - `Regex Pattern Match in Email` - use this option to trigger an event when an email is received that matches a portion of the text pattern you choose. The match is done using a Regular Expression pattern match, which allows greater flexibility and the ability to match on a portion of the target text. Select "Edit Event Settings" to choose the Email device, match in `Message Text`, `Message Subject`, or `Message From`, and the regex pattern to match. For more information on regular expressions, visit https://www.regular-expressions.info/tutorial.html. - `Server Connection Error` - use this option to trigger an event when the connection to a specific email server is lost. Select "Edit Event Settings" to select the email device you want to monitor. See the configuring email settings section for more information about having Indigo send and receive emails. ## Indigo Server Startup Use the Type `Indigo Server Startup` to trigger an action when the Indigo Server process is first launched. There are no options for this type of trigger. ## Power Failure Use the Type `Power Failure` to trigger an action whenever the computer interface detects a power failure. For Indigo to receive this information from the computer interface, the computer running Indigo must be connected to an uninterruptible power supply (UPS). Otherwise, the command from the interface will be sent to a computer with no power. There are no options for this type of trigger. ## Interface Connection Initialized Use the Type `Interface Connection Initialized` to trigger an action whenever the communication between Indigo and the interface is successfully started. There are no options for this type of trigger. ## Interface Connection Failure Use the Type `Interface Connection Failure` to trigger an action whenever the communication between Indigo and the interface fails. An unplugged USB cable or a malfunctioning interface can cause this error. There are no options for this type of trigger. ## Z-Wave Command Received ![Z-Wave Command Received Trigger Image](../../images/zwave_command_received_trigger.png) Use the Type `Z-Wave Command Received` to trigger an action when Z-Wave messages are sent from a device, like a scene controller or motion sensor, and then received by the Z-Wave interface. Each device type will have different options based on its capability ### Match Raw Packet One option that is available for all Z-Wave devices is the `Match Raw Packet` option. This allows you to specify a pattern to watch for in all incoming Z-Wave messages. This is a pretty technical option, but allows for a lot of flexibility if you can decipher the incoming messsages. In the `Match bytes` field you can specify specific hexadecimal bytes in an incoming message, and you can include ***** (asterisk) to match 0 or more bytes and **?** (question mark) to match exactly one byte. For example: `* 0x7D 0x84 0x07 *` would trigger on this message received from a device: `0x01 0x08 0x00 0x04 0x00 0x7D 0x02 0x84 0x07 0x0F` The `Write Recent Packets to Log` button will write the last 60 seconds of incoming packets to the Event Log window. You can then copy/paste the packet you want to match into the `Match bytes` field. ## Insteon Command Received ![Insteon Command Received Trigger](../../images/insteon_command_received_trigger.png) Use the Type `Insteon Command Received` to trigger an action when Insteon commands are sent from a device, like a KeypadLinc, and then received by the PowerLinc interface. Select the Insteon command from the `Received` popup that you want to cause the trigger, along with the `Device` from which the command was sent. For devices with multiple buttons, like the KeypadLinc and ControLinc, you can also choose which button press causes the trigger via the `Using button popup. !!! tip "TIP" The `Double Tab On` and `Double Tap Off` command types are useful for triggering lighting scenes at a wall switch (like the SwitchLinc). For example, you could create a `Double Tap Off` trigger action that turns off all the lights in the house when a SwitchLinc near the back door is pressed twice. !!! tip "TIP" Read our online [troubleshooting information page](../troubleshooting/powerline-signal-troubleshooting.md) if you are having problems reliably sending or receiving Insteon commands. ## X10/RF Command Received ![X10 Command Received Trigger Image](../../images/x10_command_received_trigger.png) Use the Type `X10/RF Command Received` to trigger an action when X10 commands are sent from a device, like a PalmPad, SwitchLinc 2-Way Dimmer, etc, and then received by the X10 or RF interface. Select the X10 command from the Received popup that you want to cause the trigger, along with the `Device` or X10 `Address` for that command. Choose `A/V Button Pressed` from the `Received` popup to trigger an action using one of the X10 universal remote controls, such as those included in the X10 "Entertainment Anywhere" kits. !!! tip "TIP" Read our online [troubleshooting information page](../troubleshooting/powerline-signal-troubleshooting.md) if you are having problems reliably sending or receiving X10 commands. ## Plugin Events { #plugin-events } Plugins may define events as well - these will be listed below the `X10/RF Command Received` event in the `Type` popup. ## Web Server Event ![](../../images/webhook_trigger.png){ width=500 } Use the type *`Web Server Event`* to trigger an action when a webhook is called. Set the type *`Web Server Event`* and set the Event to *`Webhook`*. | Field | Description | | --- | --- | | Webhook ID | This is a random code generated by Indigo. It's used to identify which webhook is associated with the event. You can use the code provided or use one of your own. NOTE: if someone has your Reflector URL and the webhook ID, they can cause the event to fire. If you use your own ID, it's best not to make it easily guessable. | | Webhook Method
[POST] | Use *`POST`* to indicate that the webhook is sending information to Indigo and not expecting data in return (your call will be | | POST Processing
[JSON] | this type of webhook will accept a POST and interpret the payload as JSON. | | POST Processing
[HTML Form] | this type of webhook will accept a POST with optional form data, which will be converted into a dict of name value pairs and passed through as data. | | Webhook Method
[GET] | this type of webhook will accept a GET and will pass through any query arguments as the data element. GET webhooks do not have settings for processing the associated payload. | You can find more information on the [Webhooks](../../api/webhooks.md) page. You can find more information about using substitutions on the Indigo [substitutions page](../automation/substitutions.md). --- Variables (https://docs.indigodomo.com/2025.2/user/concepts/variables/) --- # Variables { #variables } Variables are used to hold information that can help your home automation logic. You can use variable information to display on control pages, as part of your conditional logic (see [Conditions](conditions.md#conditions) below), or as a trigger for some other action. To manage your variables, select `Window->Variable List` and you'll see the Variable List window: ![Variable Window Image](../../images/variable_window.png) This window is broken up into 3 sections. The top section lets you create new variables, duplicate existing variables, and delete variables. It also lets you search your variable list (either name or value) by typing some text into the search box. The middle section is the actual variable table. It actually has two parts: the table header and the table itself. If you right click on the table header, it will allow you to customize what columns show up in the table. To change a variable `Name`, just double click on the name and type in a new name. Note that variable names can contain only alpha-numeric characters and underscores "_" and must be unique. Variable values may contain pretty much anything and can be changed by double-clicking on them. The `Remote Display` column indicates whether a variable will be shown in the built-in variable list in remote clients. The `ID` column shows the unique identifier for the variable - you'll need this if you're planning on writing Python scripts. If you right-click a variable, you can toggle the Remote Display attribute or copy the variable id to the clipboard (for easy pasting into scripts). You can also select the `Show Dependencies` option to open a window that shows all other objects that are dependent on that variable. See [Deletion Dependencies](deletion-dependencies.md) for more information. The bottom section are the folder control buttons. Clicking on the plus (`+`) will add a new folder, and clicking on the minus (`-`) will delete the selected folder. If the folder isn't empty when you try to delete it, a sheet will come down prompting you to decide if you want to delete all the enclosed variables or if you want to move them out of the folder first. If you right click on a folder in the list, you can toggle whether the folder shows up in remote clients and you can copy the unique folder id to the clipboard for use in scripts. --- Getting Started Guide (https://docs.indigodomo.com/2025.2/user/getting-started/) --- # Getting Started !!! abstract "In this article" This guide walks you through everything needed to get Indigo running: verifying Mac requirements, connecting hardware interface devices, adding your first Z-Wave, Insteon, or X10 devices, and creating basic automations. Read through all topics in this section before setting up your system to understand the full scope of what's required. ## Welcome to Indigo {{ version }}! Indigo is a powerful Mac-based home control server that integrates an assortment of popular Z-Wave®, Insteon and X10 hardware devices, as well as a variety of other hardware via 3rd party plugins, to provide monitoring and control of your home. Depending on your needs and budget, you can create a simple system that controls only a couple of lights or you can automate your entire home. We recommend that you read through all of the topics in this Introduction section to get a firm grasp of the pieces required to begin your home automation experience. ### Indigo Software and Mac Requirements To install the Indigo software, you'll need a Mac that meets these OS and hardware requirements: - [Mac OS X 10.13](https://en.wikipedia.org/wiki/MacOS_Sierra) or higher - [Any Mac capable of running Mac OS X 10.13](https://en.wikipedia.org/wiki/MacOS_Sierra#System_requirements) Indigo requires you to leave your Mac running all the time (though the display can go to sleep) in order to control your home automation; you should take this into consideration when choosing a location for your Mac. ### Indigo Home Automation Technology Support The other major piece of the home automation puzzle are the devices that you want to control. Lights, thermostats, sprinklers, door locks, motion sensors, alarm panels, A/V equipment, etc. Indigo supports a large variety of these devices. Check out our [database of devices that have been tested with Indigo](https://www.indigodomo.com/devices/) (including via 3rd party plugins). Out of the box, Indigo supports the following Home Automation protocols (simultaneously) via separate [Interface Hardware](https://www.indigodomo.com/devices/interfaces/) devices: #### Z-Wave { #connecting-z-wave-interfaces } [Z-Wave](../interfaces/z-wave/about.md) is a very popular home automation technology that's used worldwide. There are [many different manufacturers](https://z-wavealliance.org/z-wave_alliance_member_companies/) of Z-Wave devices so the selection is quite good. To use Z-Wave with Indigo, you'll need a Z-Wave interface (often referred to as a dongle): Indigo supports Z-Wave via a variety of [Hardware Interfaces](https://www.indigodomo.com/devices/interfaces/). The types of Z-Wave devices that Indigo currently supports include: - ON/OFF devices (plug-in modules, switches, and outlets), - Dimmers (plug-in modules and switches), - Sensors (contact, magnetic, motion, temperature, etc.), - Thermostats, and - Locks. Other device types will be added over time. Check our [Compatible Devices list](https://www.indigodomo.com/devices/) to see if a specific module has been tested. Note however that just because a module isn't listed doesn't mean that it won't work - Z-Wave is architected such that devices that correctly support Z-Wave features should automatically work. There are so many different devices from many different manufacturers that we will never be able to test them all. If you have a device that isn't listed but works, please feel free to report it to us and we'll add it to the list. There is a very simple mechanism to report devices, described in [Editing a Z-Wave Device's Properties](../interfaces/z-wave/index.md#editing-a-z-wave-device-s-properties) towards the end of that section. #### Insteon { #connecting-insteon-and-x10-interfaces } [Insteon](../interfaces/insteon/index.md) is a protocol developed by SmartLabs, parent company of Smarthome.com, which is widely used in North America and is moving into other markets as well. There are a few 3rd party vendors that make Insteon hardware but most devices are made by SmartLabs. To use Insteon with Indigo, you'll need an Insteon interface. You can find a complete list of Insteon interfaces that are supported on our [Built-in Interface Hardware Support](https://www.indigodomo.com/devices/interfaces/) list. Indigo supports the vast majority of Insteon devices - see our [Compatible Devices list](http://www.indigodomo.com/devices/) for a complete list. #### X10 [X10](../interfaces/x10/index.md) is a legacy technology that works primarily over the power line though some devices are wireless (using the X10 RF protocol). We don't recommend anyone start a new home automation system using X10 because of its poor reliability and relative lack of X10 hardware - however, if you have existing X10 devices you can definitely use them with Indigo if you have a compatible X10 interface. You can find a complete list of X10 interfaces that are supported on our [Built-in Interface Hardware Support](https://www.indigodomo.com/devices/interfaces/) list. #### Other Devices If you don't have any of the above hardware, you can still install and use Indigo - particularly if you have hardware supported by one of the [many 3rd party plugins](https://www.indigodomo.com/pluginstore/). For instance: [RFXtrx433](http://www.indigodomo.com/pluginstore/17/) with support for Home Easy/Chacon, X10 RF, LightwaveRF, and many different types of sensors; [Ecobee](https://www.indigodomo.com/pluginstore/193/) and [a variety of TRV](http://www.indigodomo.com/pluginstore/201/) Thermostats; Alarm Panels; A/V equipment; etc. See our [Plugin Store](http://www.indigodomo.com/pluginstore/) for all available 3rd party plugins. Indigo also ships with some [useful plugins](../../plugins/index.md) out of the box. Some plugin developers have reported devices that have been tested with their plugin and Indigo: see our [Compatible Devices list](http://www.indigodomo.com/devices/) for those devices. ### Other Sources for Help For specific questions or discussions on the hardware above, we recommend you join us on our [online forum](https://forums.indigodomo.com/). You can also visit our [website](http://www.indigodomo.com/) for valuable [support resources](http://www.indigodomo.com/support/): - The [FAQ](http://www.indigodomo.com/indigo/faq.html) has answers to the most common questions - Add lots of extra functionality via 3rd Party Plugins available in our [Plugin Store](https://www.indigodomo.com/pluginstore/) - Visit our [User Contribution Library](http://www.indigodomo.com/library/index.php) to download the latest plugins, scripts, and icons/graphics for extending Indigo Now, you know what the parts of your system will be and where to go for more information and help. Now let's get started with the actual installation. ## Setting Up Indigo With the background above, work through these chapters in order: 1. **[Installation & Server Setup](installation.md)** — install the software, start and configure the Indigo Server, set your location. 2. **[Managing the Built-in Interfaces](interfaces.md)** — enable and configure the Z-Wave, Insteon, X10, and Virtual Devices interfaces, then add your devices using the per-technology guides. When your system is up and running, see [Accessing Indigo Remotely](remote-access.md) in the Remote Access section to reach your server from other Macs, Indigo Touch, and web browsers. ## Where to Go Next Congratulations! You should now have a basic functioning Indigo installation that's ready for you to start adding devices and defining your home automation logic. We suggest that you next go to the [Overview of Devices, Triggers, Schedules, Action Groups, Control Pages, and Variables](../concepts/index.md) - that will give you the information you need to begin using the features of Indigo. --- *Z-Wave® is a registered trademark of Sigma Designs, Inc. Indigo's support of Z-Wave hardware is neither endorsed nor certified by Sigma Designs.* --- Installation & Server Setup (https://docs.indigodomo.com/2025.2/user/getting-started/installation/) --- # Installation & Server Setup ## Installing Indigo Indigo can run as a standalone application on a single Mac or can be run in a client/server mode on two or more Macs. In either case, you must first run the Indigo installer on the main Mac (also referred to as the server Mac) to which the home automation [interface hardware](https://www.indigodomo.com/devices/interfaces/) (Z-Stick, PowerLinc, CM15, etc.) will be connected. Just [download](http://www.indigodomo.com/downloads) the latest Indigo installer (you must be logged in to your [Indigo Account](https://www.indigodomo.com/acccount/codes/) to see the downloads available to you). This should download the disk image. In Safari, click the downloads button (the down arrow to the right of the URL/search bar). You should see an entry titled "Indigo.dmg". Double-click the installer file to switch to the Finder. A dialog will show the disk image is being mounted. Once it's mounted, a window will open with the following files: 1. A ReadMe.html file - read this for any late breaking information about Indigo or the installation process. 1. The Indigo Installer.pkg file - double-click this to start the Indigo installation. Follow the instructions provided by the installer. On the Installation Type screen, press the Install button (do not use Customize) to install all the Indigo packages (Server, Server Scripts, Drivers, and Client) on your designated hard drive. Note: you must install and run Indigo from an account on your Mac that has administrator privileges. ### Location of Indigo Files after Installation The Indigo {{ version }} application can be found here: /Applications/Indigo {{ version }}.app And the Indigo database files, log files, scripts files, and other settings/support files are stored in: /Library/Application Support/Perceptive Automation/Indigo {{ version }}/ By default, database files are stored in the `Databases` folder in that folder. !!! note This is the Library folder at the top level of your hard drive, not the one in your User folder. Lion and greater hide these folders by default, but if you select the `Go->Go to Folder…` menu item in the Finder and paste in the path above it will open that folder in a Finder window. ### Installing Indigo Client on another Mac (optional) If you have only a single Mac, then your installation is now complete and you are ready to start Indigo. However, Indigo has been designed as a client/server application to allow remote control and configuration from anywhere (*extra network configuration is required if you want to access your server from a client over the Internet and is outside the scope of this manual - post on the online support forum for assistance). The client-only installation process installs only the files needed to remotely access the primary Indigo server running on your main Mac. 1. Copy the Indigo Installer.mpkg file to your other Mac. 1. Double click the Indigo Installer.mpkg icon to start the Indigo installation. 1. Follow the instructions provided by the installer process. On the Installation Type screen, press the Customize button and then deselect all the package options (Indigo Server, Indigo Plugins, Indigo Server Scripts, Indigo Drivers) except for the Indigo Client package. 1. Press the Install button to install just the Indigo Client package. 1. **Note**: It is **not** necessary to restart your Mac after installing the Indigo Client package only. If you have other Macs on your home network, then you can optionally repeat the above Indigo Client installation on each of them. Now you can [start Indigo](#starting-indigo-server) and configure the server, and then [remotely control](remote-access.md#remote-indigo-access) Indigo from your iPad, iPhone or iPod Touch using [Indigo Touch](http://www.indigodomo.com/touch), a Web browser or the Indigo Client. ### Repairing an Installation If you experience issues with your Indigo installation, such as file permissions or missing or corrupt python modules, you can attempt to repair those by rerunning the Indigo installer. This will reinstall the Indigo application and all of its dependencies but it will not touch your Indigo database, plugin preferences, or scripts you've added to the shared directories. This is the standard way to get Indigo back to it's installed state. ## Starting Indigo Server Indigo can run as a standalone application or can be run in a client/server mode. The latter allows the server to run invisibly in the background without the client application UI showing and is the recommended option. Regardless of which you are doing, the first step [after installation](#installing-indigo) is to launch Indigo and configure the local server. 1. Double click the `Indigo {{ version }}` application (`/Applications/Indigo {{ version }}.app`). 1. If this is the first time to run `Indigo {{ version }}` on this Mac, then press the `Start Local Server...` button in the connection status window. 1. If you want to reconfigure the Indigo Server on a Mac already running Indigo, then select the `Indigo {{ version }}->Start Local Server...` menu item. ![Start Server Dialog Image](../../images/start_server_dialog.png) !!! info "Password Field Behavior" The length of the entry in the password field does not reflect the length of the actual password entered. This is intentional and is meant to add a layer of security so a bad actor can't guess the true password's length.
### Standard startup mode Choose the `Standard Indigo Startup` radio button to start the Indigo Server process independently of the Indigo Client. This will allow the server to run in the background on your Mac with no visible UI, even when the Indigo Client is not running. This will also start the built-in Web server, allowing remote access from Web browsers on other computers and remote access from iPhones and iPod Touches. Use the `Auto start Indigo Server on user login` checkbox to have the Indigo Server automatically launched whenever your current OS X administrator user account is logged in. This option will also make sure the Indigo Server process is automatically relaunched if it crashes. Use the `Allow remote access` checkbox to enable [remote access](remote-access.md#remote-indigo-access) from other Macs and Web browsers. You must enter a username and password if you enable remote access. Use the `Override Web server port number:` checkbox to change the TCP/IP port number that the web server uses to serve content and browsers will use to browse the web control pages. Use the `Enable secure internet access via Indigo Reflector` checkbox for secure Web browser access from anywhere. This option requires a you to have configured a [Reflector](../remote-access/reflector.md), which handles maintaining the secure connection to Indigo Server automatically. Reflectors are included as part of your [Up-to-Date subscription](http://www.indigodomo.com/blog/2016/11/09/indigo-date/). - `Outgoing reflector port selection`: Some ISPs, especially satellite providers, may block incoming and outgoing connections -- particularly if they sit idle for a time (which, in our opinion is ridiculous) -- this setting provides a few options to hopefully work around these limitations. The setting you choose does not have any impact on performance. Further, the reflector port selection (or, in fact, the reflector itself) should have no impact on local web traffic performance (since the reflector isn't involved when a local network connection is made). If you're experiencing slowness with a local connection, check your router to ensure it isn't blocking or otherwise interfering with Bonjour traffic. Use the `Enable OAuth and API Key authentication` checkbox to enable 3rd party services like Alexa. It also will allow REST API calls to use an API key for authentication rather than a username/password. This is more secure since you can revoke a key if it gets compromised without having to change passwords in wherever you may need to use them. Use the `Enable remote Indigo client access` checkbox to allow [remote Indigo Clients](remote-access.md#remote-indigo-access) on other Macs to connect to the Indigo Server. Note that this only works on your local network - the Indigo Reflector service is only for Indigo Touch and other web access, not for configuration client connections. Use the `Override Indigo server port number:` checkbox to change the TCP/IP port number that the Mac OS X client uses to connect to the server. ### Custom single app startup This is a legacy setting and should only be used if instructed by Indigo Customer Support. ### Starting the Server Press the `Start Server` button, or the `Restart Server` button if the server is already running, to start the local Indigo Server. If `Allow remote access` is enabled, then the built-in Web server will also be started. The Indigo Client will automatically connect to the Indigo Server. If this is the first time to launch the Indigo Server on this Mac, then you will be prompted to accept the License Agreement and to enter your Registration Code. The Indigo Client will then load and display the current house database file. If you are running in client/server mode, then you can quit the Indigo Client at anytime and the Indigo Server will continue to run in the background processing your home control logic and schedules. Additionally, you can [remotely access](remote-access.md#remote-indigo-access) the Indigo Server from Indigo Clients on other Macs or from remote Web browsers that have internet access to the server Mac. If you are running in standalone mode, then quitting the Indigo Client will automatically quit the Indigo Server. If this is the first time you've started the server, you'll be prompted first to click through our End User License Agreement (EULA), then you'll see the Indigo Account Log In dialog: ![Login Dialog Image](../../images/login_dialog.png) and enter your Indigo Account username (or email) and password. The server will then continue to start up. If you purchased a retail license from a reseller, you must follow the directions supplied by your reseller. It may be in an email or they may include a printed sheet with your shipment. Usually this will just tell you to create a new Indigo Account (click the `Create New Account` button) and fill out the form. If you just want to run the client and connect to a server on a different Mac, then click the `Connect to Remote Server...` button. ### Backing Up Indigo Backing up your Indigo installation is very simple: just make sure that your backup program is backing up this folder: /Library/Application Support/Perceptive Automation/ Note: this is the Library folder at the top level of your hard drive, not the one in your User folder. Use the `Go->Go to Folder...` menu item in the Finder and paste in the path above to get there easily). Another thing to note is that it's possible that you've stored your database in a non-standard location, like for instance the Documents folder in the home folder for the account under which Indigo is installed. If that's the case you'll need to make sure that you've backed it up as well. You can tell the location of your database file by Command-clicking the database name in the title bar of the Home Window. Time Machine will do this by default. To recover, just recover that directory and then run the Indigo {{ version }} installer again (which will repair any permission issues that Time Machine may have introduced). ## General Configuration Settings { #general-configuration-settings } ![General Preferences Tab Image](../../images/general_prefs_tab.png) You can configure other Indigo settings by opening the preferences dialog (selecting the `Indigo {{ version }}->Preferences...` menu item) and clicking on the `General` tab. The first section of the tab is about update checking. The first checkbox will have the client check with our servers to see if there's an update to Indigo available when the client first starts up. If there is, it will let you know. The second checkbox will send anonymous information to us (and it really is anonymous) about your install - this helps us to better prioritize what future enhancements to add. The last checkbox will also check to see if there's a newer beta version available - if you aren't interested in getting betas then leave it unchecked. You can have Indigo check for updates immediately by selecting the `Indigo {{ version }}->Check for Updates...` menu item. The next section we talked about above - how many days of event log files to keep. The last section is a rather technical configuration parameter - it is possible to get into an unending (infinite) loop when you're setting up your triggers. For instance, if you have a trigger the fires on a variable change, and it changes the variable to some new value each time, that would cause the trigger to fire again. Setting this value will cause it to stop eventually. Leaving it set to 5 is probably the best idea. ## Specifying your Latitude and Longitude { #specifying-your-latitude-and-longitude } Indigo uses your current Latitude and Longitude coordinates to calculate precisely when sunset and sunrise will occur every day. Indigo automatically extracts your location from the System Preferences. To do this, it must have access to Location Services on your Mac (and WiFi must be turned on for Location Services to work). When Indigo first launches, you'll be prompted to allow access to Location data. If for some reason it's not working, check the Location Services section on the Privacy tab of the Security & Privacy section of your System Preferences and make sure that IndigoServer is enabled (has a checkbox beside it). ### Configuring your System Location 1. Choose `System Preferences` from the `Apple` menu. 1. Select the `Date & Time` icon. 1. Select the `Time Zone` panel. 1. If the current `Closet City` location is not near your location, then follow the instructions on the panel to choose your location. If you would like to precisely specify your location, then you can override the system location from within Indigo. ### Overriding the System Location By default, Indigo will use Location Services to determine the location of your Mac. You will be asked the first time you start your server to grant permission for **IndigoServer** to access you Mac's location. If Indigo doesn't seem to be running schedules at the right time, make sure that Indigo is authorized to access Location Services. Open the System Preferences, select the Security & Privacy preference, click on the Privacy tab and you should see IndigoServer in the list - make sure the check box beside it is enabled: ![Location Services Image](../../images/location_services.png) You can, however, manually specify the latitude and longitude: ![Longitude Latitude Tab Image](../../images/longlat_tab.png) - Choose the `Indigo {{ version }}->Preferences...` menu item, then make sure the `Sunset & Sunrise` tab is selected. - Select the `Override system location` checkbox. - Enter your exact `Latitude` and `Longitude` coordinates. ## Modifying System Settings for Continuous Operation Indigo requires your Mac to be on and awake for processing. You can, however, set the display to go to sleep: the Indigo Server will not need the display to be active to function. ## Modify Energy Saver Settings to Prevent Computer Sleep - Choose `System Preferences` from the `Apple` menu. - Select the `Energy Saver` icon. - Move the `Computer sleep:` slider to `Never`. - Uncheck `Put the hard disk(s) to sleep when possible`. - Select the `Start up automatically after a power failure` checkbox. --- Managing the Built-in Interfaces (https://docs.indigodomo.com/2025.2/user/getting-started/interfaces/) --- # Managing the Built-in Interfaces { #managing-the-built-in-interfaces } Indigo has 4 built in interfaces: Z-Wave, Insteon, X10, and Virtual Devices. You manage them from the Interfaces menu: ![Interfaces Menu Image](../../images/interfaces_menu.png) The first thing you'll notice is the dot beside the names in that menu. A green dot means that the interface is enabled. If the dot is gray, it means that it's disabled. This allows you to see what's enabled by just looking at the menu. Each interface (and its submenu) is described below. If you select the `Manage Interfaces...` menu item, it will open the Preferences dialog with the Interfaces tab selected: ![Interfaces Tab Image](../../images/interfaces_tab.png) You can enable/disable an interface just by clicking the checkbox (rather than selecting the Enable/Disable menu item on each interface's submenu). Likewise, if an interface has any configuration options, you can open those dialogs by selecting the interface and clicking on the `Configure...` button (rather than selecting the `Configure...` menu item on each interface's submenu). For detailed information specific to the interface, look to the appropriate guide for the technology: - [Configuring and Managing your Z-Wave Network](../interfaces/z-wave/index.md) - [Configuring and Managing your Insteon Network](../interfaces/insteon/index.md) - [Configuring and Using X10 devices](../interfaces/x10/index.md) - [Using the Virtual Devices Interface](../interfaces/virtual-devices.md) --- Accessing Indigo Remotely (https://docs.indigodomo.com/2025.2/user/getting-started/remote-access/) --- # Accessing Indigo Remotely ## Remote Indigo Access After you have [started the Indigo Server](installation.md#starting-indigo-server) in client/server mode, you can remotely access it from other Macs using the Indigo Client, from your iPad, iPhone or iPod Touch using [Indigo Touch](http://www.indigodomo.com/touch), or from any modern Web browser (Safari, Firefox, Opera). The following steps explain how to access the Indigo Server from **within the local area network** (LAN) of your house. Configuring your network to allow Indigo Server access from outside your home (on the other side of your router/cable modem) is more complex and will depend on your network topology, router type, and ISP features (static versus dynamic IP addresses). For this reason, we are only providing instructions on how to get local (in house) remote access to Indigo Server. If you desire easy remote Indigo Server access from outside your home, then you will want to [activate the reflector](../remote-access/reflector.md) that's part of your Up-to-Date subscription. This provides secure [Indigo Touch](http://www.indigodomo.com/touch) access from anywhere with no network configuration needed. As long as your Up-to-Date subscription is active you'll have remote access. Alternatively, you can configure your network by consulting your router user manual and, if your ISP does not provide a static IP address, using a dynamic DNS mapping service like DynDNS.com. Because of the potential complexity involved in manually configuring networks and routers for this type of access, Perceptive Automation cannot provide direct support answers about router port forwarding or IP discovery issues. Users having difficulty configuring their networking hardware should post on the [online support forum](https://forums.indigodomo.com/). Be sure and include details about the type of hardware you have and what steps you have tried. ## Modifying Firewall Settings If you are using the macOS built-in Firewall, and depending on how you have your firewall configured, you may be prompted by the firewall with the following dialogs the first time you start Indigo: ![Firewall Prompt Image](../../images/firewall_prompt.png) ![IPH Firewall Prompt Image](../../images/iph_firewall_prompt.png) You **must** click the Allow button in those dialogs (if they pop up) or Indigo will not function correctly. ## Discovering the Indigo Server IP Address (LAN only) The Indigo Web Server advertises itself via Bonjour, so [Indigo Touch](http://www.indigodomo.com/touch) and Safari (by clicking on the bookmarks icon, then selecting Bonjour) will automatically find any local servers. In addition to that, when connecting to a local server for the first time, [Indigo Touch](http://www.indigodomo.com/touch) will also automatically fetch Reflector settings for that connection so that you'll be able to connect outside your LAN via LTE/4G/3G/Edge. For other browsers to remotely access the Web server, we need to know the network IP address for the Mac running Indigo Server. This address will be used on the remote Mac or Web browser when connecting to the Indigo Server. By selecting the active network in the Network System Preference, you can see the IP address for your Indigo Server Mac. For WiFi, it might look something like this: ![Network WiFi Image](../../images/network_wifi.png) and a wired Ethernet connection might look something like this: ![Network Wired Image](../../images/network_wired.png) Note the IP address under the **Status** section. You now have the IP address for the Mac running Indigo Server. Depending on your home network setup, this IP address may change periodically, such as when the Indigo Server Mac or router is restarted. If this happens, then you can use the steps above to discover the IP address again, or you can configure a static (not dynamic) IP address for the Mac running Indigo Server (that is an exercise left to you as it would be totally dependent on your network configuration). ## Remote Access Using the Indigo Client 1. If you haven't already installed the client on the remote Mac, run the Indigo Installer on the remote Mac and on the **Installation Type** step click the **Customize** button. Unselect everything except the **Indigo Client** line and click **Install**. 1. Double click the `Indigo {{ version }}` application (inside `/Applications/Indigo {{ version }}.app`) on the remote Mac. 1. If this is the first time to run Indigo on this remote Mac, then press the `Connect to Remote Server...` button in the connection status window. 1. If you are wanting to connect to a different Indigo Server, then select the `Indigo {{ version }}->Connect to Remote Server...` menu item. 1. Enter the `IP address` for the Indigo Server Mac discovered in the section above. 1. If you overrode the Indigo Server port number (default: `1176`) in the Start Local Server dialog on the server Mac, then select the `Override default port number` checkbox and enter your custom port number. 1. Press the `Connect` button. ![Client Connect Dialog Image](../../images/client_connect_dialog.png) !!! warning "Indigo Mac Client and the Indigo Reflector" The Indigo Mac Client doesn't use the [Indigo Reflector](../remote-access/reflector.md) service. Connecting an Indigo Mac Client to a server over the internet is a complex topic that is well beyond this document – we suggest you search our online forums for others that have configured this type of access. ## Remote Access Using Indigo Touch [Indigo Touch](http://www.indigodomo.com/touch), the iPhone and iPod Touch application from Indigo Domotics, allows the user to view and control Devices, activate Action Groups, view Variables, and access custom Control Pages. One of the best features of [Indigo Touch](http://www.indigodomo.com/touch) is its ability to automatically detect and configure network connections. If your iPhone (or iPod Touch) is connected to your local wireless LAN, then it should automatically discover your server. In [Indigo Touch](http://www.indigodomo.com/touch), tap the `Settings` button. You should see the name of your Database in the list. Tap on it, and it will connect (it will ask for your username/password if you have one set, but it will remember it going forward so you won't have to type it in again). At this point, [Indigo Touch](http://www.indigodomo.com/touch) will also query the server to see if you have an [Indigo Reflector](../remote-access/reflector.md) set up for the server. This makes connecting to your home server via [Indigo Touch](http://www.indigodomo.com/touch) completely seamless. If you have a reflector set up and running, then [Indigo Touch](http://www.indigodomo.com/touch) will automatically attempt to connect no matter where you are or how you're connected. If you're local, it will use the local WiFi network, if you're on a remote WiFi network, it will attempt to use the reflector account. If you're iPhone is on LTE/4G/3G/Edge, it will also attempt to connect via your reflector account. This makes [Indigo Touch](http://www.indigodomo.com/touch) truly location agnostic! You can, however, configure a connection manually. Simply tap `Manually Add Server` on the `Settings` screen in [Indigo Touch](http://www.indigodomo.com/touch) and you can enter your host/ip address and port number. ## Remote Access Using a Web Browser Indigo, when run in client/server mode, will automatically start its built-in Web server on launch. This allows access from remote Macs, PCs, and other internet devices like the iOS devices, Android devices, etc. Any device that can run a modern Web browser (Safari, Chrome, Firefox) will work. To access the Indigo Web Server use the IP address you discovered in the section above along with the Indigo Web Server port number (default: 8176). The URL will look like this (substitute your server's correct IP address): http://192.168.1.23:8176/ See the [using Control Pages](../concepts/control-pages.md) section for information on how to create custom browser accessible interface pages. ## Indigo Touch and Your Reflector [Indigo Touch](http://www.indigodomo.com/touch.html) for iOS is transparently integrated with the Indigo Reflector service. When you use your iOS device (iPhone, iPad, etc.) to connect to Indigo while in your house (and on your local Wi-Fi network), Indigo Touch will automatically retrieve and remember your reflector address. You can press the settings (gear) icon on the top toolbar then find the `Reflector` item near the bottom to verify that it is working correctly. Once Indigo automatically detects your reflector address, it will seamlessly change between using the local Bonjour detected address and the remote reflector address. Just launch Indigo Touch and it works, no matter where you are! ## Reset Your Reflector's Activation If you are switching to a different reflector that you've asked us to create for you, or you've been instructed by support to reset your current reflector's activation, then follow these steps: 1. Shut down the Indigo Server (select `Indigo {{ version }}->Stop Server`) but don't quit the Indigo Client 1. Switch to your browser and [log out of your Indigo Account](http://www.indigodomo.com/account/logout/) 1. Go to the [reflector list](http://www.indigodomo.com/account/reflectors/) in your Indigo Account (you'll need to log back in) and click the `Reset` link beside your reflector's status (it should say *Activated* before your press *Reset*) 1. Switch back to the Indigo Client and click on the `Start Local Server` button You should now see an `Activate Reflector` button towards the bottom of the dialog. Click that, log in to your reflector account, and select the appropriate reflector. If the reflector you want to use doesn't show in the list of inactive reflectors, [contact us](http://www.indigodomo.com/#contact) with the name of the reflector you're trying to activate and what steps you've performed. ### Manual Reset If the procedure above doesn't work, and *​only* ​if instructed by support, follow these steps to manually reset your Indigo Client reflector settings: 1. Shut down the Indigo Server (select `Indigo {{ version }}->Stop Server`) but don't quit the Indigo Client 1. Switch to your browser and [log out of your Indigo Account](http://www.indigodomo.com/account/logout/) 1. Go to the [reflector list](http://www.indigodomo.com/account/reflectors/) in your Indigo Account (you'll need to log back in) and click the `Reset` link beside your reflector's status (it should say *Activated* before your press *Reset*). If the reflector has already been deactivated that is fine – just skip this step. 1. In the Finder, select `Go->Go to Folder…` 1. In the resulting dialog, copy and paste the following: `/Library/Application Support/Perceptive Automation/Indigo {{ version }}/Preferences/` 1. In the resulting Finder window, delete the folder named `PrismReflector` 1. Switch back to the Indigo Client and click on the `Start Local Server` button --- Virtual Devices (https://docs.indigodomo.com/2025.2/user/interfaces/virtual-devices/) --- # Virtual Devices Interface !!! abstract "In this guide" How to enable and use Indigo's Virtual Devices Interface to create non-physical device types — Device Groups, Aggregate and Average Sensors, Virtual Dimmers, and more — that track or aggregate the states of real hardware devices. Covers enabling the interface and configuring each device type. Indigo provides a Virtual Devices Interface type that provides users with several new device types, each discussed below. If you want to use Virtual Devices, make sure to enable them by selecting the `Interfaces->Virtual Devices->Enable` menu item. To add a virtual device to Indigo, follow these steps: 1. Select `DEVICES` in the Main Window outline view or select one of the sub-folders. 1. Click the `New...` button. You'll see the `Create New Device` dialog. 1. Select `Virtual Devices` from the Type popup menu. 1. Select the model of virtual device you want to create (described below). ## Device Groups With the addition of Z-Wave (and various plugins that define dimmer and relay devices) it has become apparent that we need to provide some way to create technology-agnostic groups of devices. You can, of course, control groups of devices through Action Groups, and for most cases that's good enough. However, there are some cases where you want a group to track the state of the devices it contains. So not only do you want to turn on/off a group, you want to know when any of the devices in the group "leave" the definition of the group by changing. So we've created a new "Device Group" device to help you with that. We've also added a twist to this device. When we create the device (and anytime later), we save off the current state of each device in the group. It's like taking a snapshot of each device. When you turn ON the group, we'll use the value that the device was when you last saved the device states. We think you'll really love this feature since you can set all the devices how you want them, then save the device states. To create a device group, you just create a new device, select `Virtual Devices` from the `Type` menu, select `Device Group` from the model menu, and you'll see the configuration dialog: ![Device Group Configuration Image](../../images/device_group_config.png) Select the devices you want to be in the group (only dimmer devices, relay (on/off) devices, and sensor devices that support an on/off state are available). To select multiple, hold down the command key and click the device in the list. Notice that the devices all have something in parentheses after the name - that's the current value of the device that will be saved. If it's a dimmer device, it shows the brightness and if it's a relay (On/Off) device, it shows whether it's on or off. When you save, that's the value that will be saved. These values will be used when the group is turned on. Next, you specify how the plugin will manage the ON state of the device group. The two choices are: - All devices are ON - the group state will be set to ON when all devices in the group are ON - Any device is ON - the group state will be set to ON when any of the devices in the group are ON Finally, you need to specify how Indigo will determine if a device is ON or OFF. For dimmer devices, you have the following options: - Brightness >= saved value - by selecting this option, the device will be considered ON when the brightness is greater than or equal to the value of the brightness saved for that device. Note that you can have a brightness set to 0 when the group was saved, in which case when the group is turned on the brightness will be set to 0. - Brightness > 0 (On) - by selecting this option, the device will be considered ON when the brightness is greater than 0. So it will be considered on even at very dim settings. For relay (on/off) and sensor devices, you have the following options: - Equal to saved value - by selecting this option, the device will be considered ON when it is equal to how it was set when you saved the state for that device. This way, you can add a device when it's off and turning on the group will in fact turn off the device. - On - this is the most obvious option - if the device is really ON, then it's considered ON. To use a device group, just use the standard `Control Light/Appliance` action to turn it ON/OFF, and create triggers on the group's ON and OFF state via the `Device State Changed` trigger type. Turning a device group on will set all the devices to their respective saved state. You can also update the states of the devices in the group at any time by using the `Update Device Group States` action or menu item. That's it! ## Sprinkler Group We've regularly heard that users have multiple sprinkler controllers and would like to treat them as a single controller. This Virtual Device does just that. You can specify up to 4 different physical sprinkler devices: ![Sprinkler Group Configuration Image](../../images/sprinkler_group_config.png) Indigo will treat the resulting group as a single sprinkler. You can create standard sprinkler schedules for it and never have to worry that there might be multiple sprinkler controllers running at the same time. ## Virtual On/Off Devices One of the things that we often see are people who want to create a device that can be turned ON and OFF, but don't have the knowledge or expertise to build a full-on plugin. And sometimes a full plugin would be overkill, particularly if the device is so custom that it really wouldn't be useful to anyone else. That's where the `Virtual ON/OFF Device` comes in. This device type will allow users create custom devices without the need to build an entire plugin. In fact, the first iteration of this device doesn't even require any programming at all! Virtual devices can be shown in the Indigo UI, the Indigo Web UI, and the Indigo Touch UI as on/off style devices and can be controlled using the normal on/off UI controls. To create a virtual device, you just create a new device, select `Device Collection` from the `Type` menu, select `Virtual On/Off Device` from the model menu, and you'll see the configuration dialog: ![Pseudo On Off Configuration Image](../../images/pseudo_onoff_config.png) ### Execution Models We intend to support several different ways that users can have virtual devices accomplish their tasks: - Action Groups - where the functions are carried out by Action Groups (with a little assist from Variables if you want) We've only implemented Action Groups, but in future releases we may add other execution models. #### Action Groups In order to make virtual devices simple enough for non-technical users to use them, we started with the Action Groups execution model. This means that you specify an action group to execute for each of the major tasks that a ON/OFF Device can perform: turn on, turn off, toggle, and get status. Here's the config dialog for Action Groups: ![Pseudo On Off Configuration Image](../../images/pseudo_onoff_config.png) The first thing you'll select are the action groups to execute when the device is turned on and off. So if you click the `Turn On` button or select the `Turn Off` action in the `Control Light/Appliance action`, for example, the appropriate action group will be executed. These two are the minimum requirements for this type of virtual device. Next, you can specify an action group that will toggle a device - so if there's some way for your action group to determine at runtime how a toggle should work then you can specify it. If not, just leave the checkbox unchecked. ##### Automatic State Maintenance Finally, your virtual device can maintain a state if you like. But, Action Groups can't directly manipulate state you say, right? Well, that's true. However, Action Groups can modify a variable. So, we've allowed you to select a variable and we'll monitor that variable for any change. If the value of the variable becomes "on" (or "true", "open", "1", "yes", "enabled"), we set the state of the virtual device to on. If the variable becomes "off" (or "false", "closed", "0", "no", "disabled"), then we set the state to off. If you set the value of the variable to anything else, we'll set the state of the device to whatever you entered for the variable value AND we'll mark it as having an error. This will cause the device to turn red in the device list to help show you that there's a problem. (Z-Wave devices will do this as well and eventually Insteon devices will too). So your device's state will mirror the value in the variable. You can also specify an action group that will be called when you request a status update via the UI or via an action. Note: if you don't enable `Supports Status`, your virtual device will always show as being "off" in the UI and triggers that watch for state change will never fire. That may be fine for your particular needs so we made it optional. ##### Scripting State Maintenance You can also set the on state of a virtual device: ```python virtual_devices_interface = indigo.server.getPlugin("com.perceptiveautomation.indigoplugin.devicecollection") if virtual_devices_interface.isEnabled(): virtual_devices_interface.executeAction('setVirtualDeviceState', deviceId=DEVICEIDHERE, props={'newValue': 'on'}) ``` Use **on** or **off** to set the state, anything else will be interpreted as an error and will show accordingly in the UI. So that's it - you can implement a very simple On/Off virtual device just by specifying a few action groups. ## Virtual Sensors Virtual Sensors are plugin devices that are linked to external Python scripts that send instructions to the Virtual Device so it can be used to track a desired status. The device also sends information back to the external script for further processing if needed. Say you want to use an external script to send a value to the Virtual Device based on some logic in your script and then fire an Indigo Trigger based on the result. You would create a Virtual Sensor, link it to your external script, and set up an update Action to tell the Virtual Sensor to request a status update from your script. ![Virtual Sensor Configuration Dialog Image](../../images/virtual_sensor_config_dialog.png) ### Supported States Virtual Devices support two states and **must** support at least one of the following: - On State - your device will have an on/off state. - Sensor Value - your device will have a sensor value. #### Script Locations You can add scripts directly to the *`/Library/Application Support/Perceptive Automation/Python3-includes`* folder, or you can point to a script in any location that Indigo has the authority to access. Python scripts that are saved to the above folder will be available in the Script File dropdown menu. Alternatively, you can check the Custom Location checkbox and a field will open for you to enter the full path to the script file. ##### Controlling Scripts You control the state of Virtual Sensor Devices with Python scripts (the device executes the controlling scripts directly). The controlling script must have a *`virtual_sensor_status()`* method which the Virtual Sensor Device will call, and **must** return a valid dictionary with the following possible keys (types shown in brackets): ```python # If you want to pass something to the Indigo Event Log, you will need to import the # logging module and access the parent plugin's logging instance. import logging logger = logging.getLogger("Plugin") def virtual_sensor_status(device: indigo.Device, action_props: dict) -> dict: """ This is the only method that the virtual sensor will call, unless you make calls to other parts of your script from here. :device: a copy of the virtual sensor device object [indigo.Device] :action_props: any action object properties provided [dict] :returns: updated sensor values [dict] """ payload = dict() payload['onState'] = True # must be a bool [True/False] payload['onStateUiValue'] = "Closed" # Can essentially be any valid string [string] payload['sensorValue'] = 32 # note that Indigo always stores this as a float [int or float] payload['sensorValueUiValue'] = "32º" # the value in Indigo's UI. [string] payload['icon'] = "indigo.kStateImageSel.TemperatureSensor" # i.e., [indigo.kStateImageSel] logger.info("Linked script executed.") return payload ``` `*device*` -- in this context, the `*device*` parameter will contain a copy of the Virtual Sensor device that is linked to the script. `*action_props*` -- in this context, the `*action_props*` parameter will contain any props passed from the Indigo Action that caused the Virtual Sensor device to update. If the device was updated by a *`Send Status Request`* or a "generic" Indigo Action refresh call, *`action_props`* will be an empty dictionary. The Virtual Sensor will adjust its states based on the values returned from the *`virtual_sensor_status`* method. When that method is executed, you can make calls to other parts of your script in order to determine what the payload values should be. If the update is completed successfully, the Virtual Sensor will provide a copy of the *`device`* object and an *`action_props`* dict when it requests an update from your script. ##### Triggers and Actions Virtual Devices will respond to an Indigo Action call. When the Update Virtual Sensor Action is called, the Virtual Device will reach out to your script and update its states accordingly. You can also refresh the Virtual Sensor device by making a call to Indigo's API. The `*indigo.actionGroup.execute*` API command message doesn't include action props, so the `*action_props*` property above will be an empty dict. You can execute this command message from both the Websocket and HTTP APIs. ```python { "id": "optional-custom-user-message", "message": "indigo.actionGroup.execute", "objectId": 123456789 } ``` The `*plugin.executeAction*` API command message can include optional props, and is currently only available in the HTTP API command space. ```python { "id": some-optional-message-ID, "message": "plugin.executeAction", "pluginId": "com.some.indigo.plugin", # the plugin's bundle identifier "actionId": "some_plugin_action", # the ID of the plugin action found in the plugin's Actions.xml file "deviceId": 12345678, # the device ID targeted by the plugin action (not all actions will require a device ID). "props": {"prop1": "foo", "prop2": "bar"}, "waitUntilDone": True } ``` --- Managing Insteon Devices (https://docs.indigodomo.com/2025.2/user/interfaces/insteon/) --- # Managing Your Insteon Network in Indigo !!! abstract "In this guide" How to connect an Insteon interface to Indigo, add and configure Insteon devices, and manage Insteon links so that devices can control each other without manual button-pressing at each fixture. Also covers replacing a failed device or PowerLinc controller while preserving your link database. Indigo provides a variety of tools for managing your Insteon network - not only can you add/control/delete devices, but you can also manage Insteon Links so that you don't need to walk around your house pressing buttons (see the [Managing Insteon Links](#managing-insteon-links) section for details). You can also [replace devices](#replacing-and-resyncing-devices) and even your [PowerLinc controller](#replacing-your-powerlinc) and Indigo will make sure that all links are modified so your network continues to work with as little manual intervention as possible. The first thing you should do, of course, is [connect and configure your Insteon interface](#connecting-insteon-and-x10-power-line-interfaces). Insteon is primarily a power line technology, and as such is susceptible to signal noise. Check out the [signal troubleshooting](../../troubleshooting/powerline-signal-troubleshooting.md) page for common causes and solutions to signal issues. ## Connecting Insteon and X10 Power Line Interfaces Connecting an Insteon (or X10 power line) interface to Indigo requires a couple of steps, described below. Check out our [supported interfaces list](http://www.indigodomo.com/devices/interfaces/) to see if the interface you want to use has been tested with Indigo. ### Install the FTDI VCP Drivers The **PowerLinc 2412U, [2413U](http://www.indigodomo.com/hardware/powerlinc2413u), and [2448A7H (Insteon RF USB Adaptor)](http://www.indigodomo.com/hardware/powerlinc2448)** interfaces should be plugged directly into one of your Mac's USB ports (preferably not into a USB hub port) and require the FTDI Virtual COM Port (VCP) driver to be installed. **If you are using Mac OS X 10.9 (Mavericks) or better, it has the driver already installed.** For earlier versions of the OS, you can get the driver installer at [FTDI's website](http://www.indigodomo.com/ftdiurl) - be sure to get the installer that's appropriate for the architecture of your machine (Intel or PowerPC). If you select **PowerLinc 2412U/2412S/2413U/2413S/2448** from the Interface type popup menu in the Preferences dialog (see below), then Indigo will alert you if the driver is not installed on your Mac. If you're using a PowerLinc 2412S/2413S with a separate USB to serial adapter, you can ignore that warning alert. **Note**: there are known issues with the FTDI driver and macOS High Sierra, Mojave, and Catalina on ***some*** Macs - check out our [blog post](http://www.indigodomo.com/blog/2018/01/25/high-sierra-driver-bug-workaround/) for details. The **PowerLinc 2414U/1132CU/1132U** (all of which have now been discontinued) and **CM15A** (aka CM15Pro) interfaces should also be plugged directly into your computer (preferably not into a USB hub port) - the Indigo installer automatically installed the drivers for these interfaces. Make sure you have restarted after the installation process. If you reinstall your OS, then you will need to rerun the Indigo installer for the driver. The other supported interfaces (**PowerLinc 2412S/2413S, CM11 / HD11, LynX-PLC**) are serial based, and will require a USB serial port adapter. If you are using one of the serial based interfaces, then make sure that the latest drivers for the USB serial adapter are installed on your system. Look on the adapter manufacturer's website for the latest driver downloads. If you don't already have a serial-to-USB adapter, we highly recommend getting one that uses the same FTDI chipset that the PowerLincs use since it seems to be the most reliable. **Note**: PowerLincs are quite sensitive to USB versions and hubs. It's known that there are often times failures on hubs (as mentioned above), but we also know that there are also failures on USB3 ports. In this last case, you may need to find a USB2 hub and use that for the PowerLinc. Check the [Interface Hardware](https://www.indigodomo.com/devices/interfaces/) list to see what interfaces we've actually tested. ### Connecting the Interface Plug the interface directly into an outlet (with the exception of the Insteon RF USB Adapter). Do not plug the interface into your computer's power strip because many power strips contain filters that will severely degrade the Insteon / X10 signal quality being received and transmitted by the interface. Additionally, uninterruptible power supplies (UPSs) can cause signal quality problems. If at all possible plug the interface into a different outlet than used by power strips and UPSs. Signal filters are available to isolate computer power strips and UPSs as well. If you are using the **CM11 / HD11** or **CM15A** interface, then do not install the battery into the interface. Indigo does not support uploading macros to these interfaces so the battery should not be used. ### Configuring Indigo to use Your Interface 1. Choose `Indigo {{ version }}->Preferences...` menu, then make sure the `Interfaces` tab is selected. 1. Select the `Enabled` checkbox next to the `Insteon / X10 Powerline Interface` item. 1. Double-click the `Insteon/X10 Power Line Interface` line in the table (you can also get to this dialog by selecting the `Interfaces->Insteon/X10 Power Line->Configure…` menu item) 1. Choose your `Interface type` in the popup menu. - If you are using the **PowerLinc 2412U/2413U/2448A7H** interface, then the `Serial port:` popup menu will be enabled and the [FTDI driver](http://www.indigodomo.com/ftdiurl) (see Install the Correct Drivers above) will create a new `Serial port:` popup menu item that looks like `usbserial-XXXXXXXX`. Select that item. **NOTE**: you ***must*** have the FTDI driver installed already. - If you are using the **PowerLinc 2414U/1132CU/1132U** or the **CM15** interface, then the `Serial port:` popup menu will be disabled because these interfaces do not use a virtual serial port driver. - If you are using one of the serial based interfaces (**PowerLinc 2412S, CM11 / HD11, LynX-PLC**), then the `Serial port:` popup menu will be enabled and you should choose which serial port adapter name the interface is connected to in the `Serial port:` popup menu. **NOTE`**: if no names are listed in the `Serial port:` popup menu, then you probably do not have the proper driver installed for your USB serial adapter. Download your adapter's driver from the driver manufacturer's website. 1. Optionally select the `Group addresses on transmission` checkbox to combine like-command transmissions together. For example A1-On, A3-On, A4-On, would be transmitted as A1, A3, A4, On. 1. Some interfaces, such as the PowerLinc V2 and LynX-PLC, have additional settings that can be accessed from the Interface Options... button. Generally, these options don't need to be changed unless you're troubleshooting a problem. ![Insteon X10 Configuration Dialog Image](../../../images/insteon_x10_config_dialog.png) If you click on the `Interface Options...` button, you'll see something like this: ![Insteon Advanced Options Image](../../../images/insteonadvancedoptions.png) You should only use these options on the recommendation of Indigo Support. ### Enabling and Disabling Communication Choose the `Interfaces->Insteon/X10 Power Line->Disable` (or `Enable`) menu item to disable/enable the interface. ## Adding and Managing Insteon Devices Indigo uses Smart Link Syncing to quickly define and link Insteon devices with the PowerLinc. This link syncing process will allow Indigo to control and see messages from the device. First, make sure that Indigo has enabled communication with the interface. If Indigo is not online with the interface, then choose the `Interfaces->Insteon/X10 Power Line->Enable` menu item. See [connecting the Interface](#connecting-insteon-and-x10-power-line-interfaces) above for more details. Additionally, make sure you have Insteon range extenders or other dual-band modules correctly installed on opposite power legs. If the range extenders or other dual-band devices are improperly installed or missing, then you may not be able to control some of your Insteon devices. See the instructions included with your Access Point RF pair for installation details. !!! tip "TIP" Access Point RFs do not bridge X10 signals, so if you also have X10 devices you will likely need an additional bridge. To add a new Insteon device: 1. Select Devices in the [Outline View of the Home window](../../mac-client/home-window.md#home-window). 1. Press the `New...` button at the top of the Home window and select `Insteon` from the `Type` popup. Here's the Insteon device dialog: ![Insteon Device Detail Image](../../../images/insteon_device_detail.png) The `Define and Sync...` button will open the `Define Insteon Define` dialog: ![Define Insteon Dialog Image](../../../images/define_insteon_dialog.png) This is how you initially add an Insteon device to Indigo. The directions on this dialog are quite self-explanatory. When you click the `Start` button you'll see the checkboxes turn green when each step is complete, and the `Close` button will enable when the process is complete. The sync process can take a few minutes to complete. Lamp and appliance devices normally take less than a minute, but KeypadLincs and ControLincs can take a few minutes because of the additional link information needed for all the buttons. If the sync process fails or does not complete, then make sure that the device is correctly wired or plugged in, both Access Point RFs are installed and on opposite power legs, and that you do not have an uninterruptible power supply (UPS) or surge protector strip plugged into the outlet the PowerLinc is using. If syncing still fails and the device is portable (ApplianceLinc or LampLinc), then try plugging it directly into the pass through outlet on the PowerLinc. See our [signal troubleshooting tips](../../troubleshooting/powerline-signal-troubleshooting.md) for additional help. After the sync dialog is closed, some devices, such as the KeypadLinc, EZRain, Thermostat Adapter, Motion Sensor, and EZIO8SA, have additional custom settings shown in the main device dialog. ## Replacing and Resyncing Devices ### Resyncing Links The `Re-Sync Links...` button on the device edit dialog will open the `Synchronize Insteon Device Links` dialog: ![Start Sync Dialog Image](../../../images/start_sync_dialog.png) Use this dialog to resync a device when its links have been altered outside of Indigo. You may be asked to use this dialog by technical support when troubleshooting device problems. ### Replacing a Device To replace a device that's malfunctioned, just open the edit dialog for the device and click the `Define and Sync...` button again to open the `Define Insteon Device` dialog. Enter the address of the replacement module (and make sure it has been installed) and click the `Start` button just like you did when you originally added the device. This will maintain all the links that you created to and from the device as well as maintain any Triggers, Schedules, Conditions, Actions, and Control Pages that might use the device. ## Managing Insteon Links The last button, `Manage Links...` is how Indigo allows you to remotely manage Insteon links without having to walk around pressing set buttons (there is also a menu item on the Insteon/X10 Power Line submenu to access the dialog). The Insteon protocol has linking built-in as part of its core functionality. The idea is that one device, a controller in Insteon speak, can be linked to another device, a responder, then from that point forward the controller device sends its command directly to the responder device. So, for instance, you can link a KeypadLinc button to a SwitchLinc so that when you press the button on the KPL the SwitchLinc responds. That's normally done by pressing various buttons on each device in specific ways until the link is established. The manual linking method is useful if you don't have a software-controlled environment. However, since you've decided to have Indigo automate your home, you do have software to help you manage your automation needs. We've worked very hard to allow Indigo to perform the majority of link management tasks remotely so you don't have to walk around pressing buttons. For some background on Insteon links, we have put together a separate page that discusses [Insteon Scenes](insteon_links.md). We highly recommend that you read through that page as it will give you a better understanding of what links are and how they work. ### Managing Insteon Device Links The primary place that you'll manage links between devices is in the Manage Insteon Device Links dialog. Select the `Interfaces->Insteon/X10 Power Line->Manage Device Links` menu item and the dialog will pop up: ![Device Links Dialog Image](../../../images/device_links_dialog.png) The dialog may look slightly different depending on the roles that the device selected in the popup at the top can play. As we mentioned earlier, devices that can control other devices are called "controllers" and devices that can be controlled are called "responders". Many devices are both controllers and responders (the example above is a SwitchLinc, which is both). These links are also called groups and/or scenes, depending on usage, so keep that in mind. For example, you can use Indigo to remotely program the button on a RemoteLinc to: set a ceiling fan (FanLinc) to Medium, brighten a dimmer to 75% over 2 seconds, turn off an on/off device. ![Device Links Example Image](../../../images/device_links_example.png) Indigo automatically creates the links to define the scene in most remote modules. This means you do not have to press-and-hold the set button or up paddle on most remote modules. Indigo will do it all remotely for you with a single press of the Sync Now button. You can also define Indigo initiated scenes to control multiple remote modules in unison. See the defining [Insteon scenes section](#defining-the-scene) for more information on this capability. #### Responder Modules vs. Controller Modules Every Insteon module is either a *responder module*, a *controller module* or both: - *Responder modules* respond to incoming Insteon commands by controlling a load (light or appliance), a thermostat setting, sprinkler valves, low-voltage relays, etc. Examples of responder modules include: LampLinc, ApplianceLinc, and the EZRain sprinkler controller. - *Controller modules* send outgoing Insteon commands onto the power line or via RF to modules that are responders. Examples of controller modules include: RemoteLinc, ControLinc, and the PowerLinc computer interface. - Some modules are both *responders* and *controllers*. For example, a KeypadLinc can respond to a RemoteLinc, but it can also control other modules, such as a LampLinc. #### Defining the Scene between Modules First, select the module you want to edit. You can choose to edit either the controller module in the scene (ex: RemoteLinc) or the responder module in the scene (ex: LampLinc). 1. Select the `Interfaces->Insteon/X10 Power Line->Manage Device Links` menu item. 1. Select the module's device name to edit in the `Show links used by device` popup control. If the module you selected is a controller (RemoteLinc, KeypadLinc, SwitchLinc, etc.), then you can add or edit responders: 1. If you want to add a new responder module to the scene, then press the `New Link to Responder` button. If you want to change the settings (brightness, ramp rate duration, etc.) of an existing responder module in the scene, then select that module's link in the table with the `Link to Responder Device` column title. 1. Choose the button or group number that identifies the scene you are defining in the controller (ex: RemoteLinc button #1) from the `broadcast of button/group number` popup control. 1. Choose the responder module's device name from the responder popup control (ex: LampLinc). 1. When responding to a scene command, most modules will control the main load (light or appliance) connected to that module. For these modules (SwitchLinc, ToggleLinc, LampLinc, etc.) you can specify the exact brightness you want for that module as well as a duration for how quickly the module should go to that brightness: Some modules will have different options. For example, when a KeypadLinc is responding to a scene command, it can turn one of the secondary button LEDs on instead of controlling the main load, or the thermostat module can respond to a scene command by changing both the thermostat operation mode and the current `cool` and `heat` setpoint temperatures. 1. Optionally turn on the `Persistent` checkbox to force this link to automatically be restored whenever either the responder module or controller module is synced. If the modules are ever reset, replaced, or have this link modified, then Indigo will automatically rewrite the original link on the next sync operation. 1. Repeat steps 1 through 5 for every new responder module that you want to add to the selected controller. If the module you selected is a responder (LampLinc, Thermostat Adapter, KeypadLinc, SwitchLinc, etc.), then you can add or edit controllers: 1. If you want to add a controller of the selected responder module, then press the `New Link to Controller` button. If you want to change the settings (brightness, ramp rate duration, etc.) used by the selected module in a controller's scene, then select the controller module's link in the table with the `Link to Controller Device` column title. 1. Choose the controller module's device name from the On controller popup control (ex: RemoteLinc). 1. Choose the button or group number that identifies the controller scene for which you want the responder to listen (ex: RemoteLinc button #3) from the `broadcast of button/group number` popup control. 1. When responding to a scene command, most modules will control the main load (light or appliance) connected to that module. For these modules (SwitchLinc, ToggleLinc, LampLinc, etc.) you can specify the exact brightness you want for that module as well as a duration for how quickly the module should go to that brightness using the % text box and the rate popup. Some modules will have different options. For example, when a KeypadLinc is responding to a scene command, it can turn one of the secondary button LEDs on instead of controlling the main load, or the thermostat module can respond to a scene command by changing both the thermostat operation mode via the mode popup and the current `cool` and `heat` setpoint temperatures via those text boxes. 1. Optionally turn on the `Persistent` checkbox to force this link to automatically be restored whenever either the responder module or controller module is synced. If the modules are ever reset, replaced, or have this link modified, then Indigo will automatically rewrite the original link on the next sync operation. 1. Repeat steps 1 through 5 for every new controller module you want to add for the selected responder. Lastly, to have Indigo write all of your link changes to the remote modules, press the `Sync Now` button to have Indigo immediately write all changed links to the remote modules. Or, press the `Close (Sync Later)` button to close the link editor window and write the changes to the modules at a later time. When you are ready to write the changes to the remote modules, select `Start Sync Device Links...` from the `Interfaces->Insteon/X10 Power Line` menu, and then press the `Sync Changes Only` button. #### Manually Creating Links between Modules In addition to using Indigo's remote link and scene management, you can also manually create the links physically at the devices themselves. Follow the instructions that came with the hardware for the exact steps, which usually involves creating the links by press-and-holding the set button or up paddles for 10 seconds on each module. If you manually create or delete a link, then you must tell Indigo to [re-sync those modules](#insteon-link-syncing). This enables Indigo to read in the link changes, and is required for Indigo to accurately show the state of the modules as they change. ### Managing Insteon PowerLinc Scenes Insteon scenes (also called groups) can be used to control multiple Insteon modules, such as light switch modules, lamp or appliance plug-in modules, and thermostat modules, all in unison. For example, you could create a *home theater lighting* scene that turns off all lighting in your media room, except for a few sconce side lights which are set to 20% brightness. You can create lighting scenes for any activity you desire: *dining*, *entertaining*, *sleeping*, *reading in bed*, *emergency*, etc. ![Manage Powerlinc Scenes Image](../../../images/manage_powerlinc_scenes.png) Indigo can remotely create scenes in the computer interface (PowerLinc) that control multiple remote modules. Indigo automatically creates the links to define the scene in both the PowerLinc and most remote modules. This means you do not have to press-and-hold the button on either the PowerLinc or most remote modules. Indigo will do it all remotely for you with a single press of the Sync Now button. Once Indigo writes the links defining the scene to the PowerLinc and remote modules, you can execute the scene from any [Trigger](../../concepts/triggers.md#triggers), [Schedule](../../concepts/schedules.md#schedules), or [Action Group](../../concepts/actions.md#action-groups) using the `Execute Insteon Scene` [action](../../concepts/actions.md#execute-insteon-scene). Indigo can also remotely create and edit scenes between remote modules (ex: from a KeypadLinc to a LampLinc). See [Managing Insteon Device Links](#managing-insteon-device-links) for more information on this capability. #### Defining the Scene First, specify which PowerLinc Group/Scene number you want to use: 1. Choose `Manage PowerLinc Links...` from the `Interfaces->Insteon/X10 Power Line` menu. 1. Select a `PowerLinc Group/Scene` number to use for the scene. Use 1 if this is your first scene. 1. Optionally enter a `PowerLinc Group/Scene` name in the edit field (ex: "reading in bed"). Next, create a new link for every responder module in the scene: 1. If you want to add a new responder module to the scene, then press the `New Link to Responder` button. If you want to change the settings (brightness, ramp rate duration, etc.) of an existing responder module in the scene, then select that module's link in the table in the top-half of the window. 1. Choose the responder module's device name from the `responder` popup control. 1. When responding to a scene command most modules will control the main load (light or appliance) connected to that module. For these modules (SwitchLinc, ToggleLinc, LampLinc, etc.) you can specify the exact brightness you want for that module as well as a duration for how quickly the module should go to that brightness by using the % text box and the duration popup. Some modules will have different options. For example, when a KeypadLinc is responding to a scene command it can turn one of the secondary button LEDs on instead of controlling the main load, or the thermostat module can respond to a scene command by changing both the thermostat operation mode via the mode popup and the current `cool` and `heat` setpoint temperatures using the text boxes. 1. Optionally turn on the `Persistent` checkbox to force this link to automatically be restored whenever the responder module is synced. If the responder module is ever reset, replaced, or has this link modified, then Indigo will automatically rewrite the original link into the device when it is next synced. 1. Repeat steps 1 through 4 for every new module you want to add to the scene. Lastly, have Indigo write all of your link changes to both the PowerLinc and the remote modules by pressing the `Sync Now` button to have Indigo immediately write all changed links to the PowerLinc and remote modules. Or, press the `Close (Sync Later)` button to close the link editor window and write the changes to the modules at a later time. When you are ready to write the changes to the PowerLinc and remote modules select `Start Sync Device Links...` from the `Interfaces->Insteon/X10 Power Line` menu, and then press the `Sync Changes Only` button. You can test the scene after the links are written by using the Send On and Send Off buttons. #### Executing the Scene The Indigo scene can now be executed from any Trigger, Schedule, or Action Group: 1. Follow the instructions to create a [Trigger](../../concepts/triggers.md#triggers), [Schedule](../../concepts/schedules.md#schedules), or [Action Group](../../concepts/actions.md#action-groups). 1. Select the `Actions` tab inside the edit window. 1. Select `Insteon Actions->[Execute Insteon Scene](../../concepts/actions.md#execute-insteon-scene)` from the action `Type` popup item. 1. Use the `Send` popup item to select which scene command to send: - `Group On` will command all the responder modules to their scene-specific brightness using their scene-specific ramp rate duration, if any. - `Group On to 100% (instant / ignore rate)` will set the brightness of dimmable modules to 100% immediately, ignoring any ramp rate duration. - `Group Off` will turn off all the responder modules using their scene-specific ramp rate duration, if any. - `Group Off (instant / ignore rate)` will turn off all the modules immediately, ignoring any ramp rate duration. 1. Select the scene number defined previously (see above) from the `Scene` popup item. Note: you can press the `Modify this Scene button...` to add a new responder module to the currently selected scene, or you can double-click a device in the scene list to edit its settings (brightness, ramp rate duration, etc.). ### Insteon Link Syncing Indigo's smart Insteon Link Syncing makes it easy to set up and keep all of your devices working with your PowerLinc. This link syncing process allows Indigo to control devices, and to update its internal device state (on / off / brightness) as the device is controlled locally (at the switch) or remotely by other devices. Indigo shows the state of all Insteon devices as they change, even if the change is because of a command from another device. For example, a LampLinc that is turned ON from a ControLinc will immediately show as ON within Indigo. Indigo automatically does link syncing when you first create the device. If, after the device is initially created, a remote device has any additional controller links added (ex: LampLinc is controlled by a KeypadLinc), then that device (LampLinc in this case) should have its links re-synced. This option is available from the Device dialog and will ensure Indigo has an accurate representation of the device's internal links, allowing it to correctly show state (on / off / brightness) changes as they occur. #### Re-Syncing a Single Insteon Device - Make sure the device is properly wired or plugged in. - Select Device List from the View menu. - Double-click the device you need to re-sync. - Press the Re-Sync Links... button. - Press the Start Sync button. - Wait for the Smart Link Syncing steps to complete and press the Close button. - The sync process can take a few minutes to complete. Lamp and appliance devices normally take less than a minute, but KeypadLincs and ControLincs can take a few minutes because of the additional link information needed for all the buttons. Battery powered devices will need to be awake for a sync to complete. If you see an error that says the device is asleep, you will need to have that device handy and hit the sync button. Sometimes just operating the device will be enough to wake it up, but that's device specific. For a door sensor, for instance, you may be able to wake it up enough for a sync just by tripping the sensor. #### Re-Syncing All Insteon Devices In addition to syncing individual devices, you can also batch sync all of your Insteon devices. Depending on the number of Insteon devices you have, this process can take a significant amount of time to complete. Because control of your devices will be limited during the synchronization process, it is recommended that you start the synchronization process at night or before you leave the house. - Make sure all devices are properly wired or plugged in. - Select Start Sync Device Links... from the Interface menu. - Press the Sync All Devices button. - Watch the Event Log window to see when the synchronization process is complete. - The batch synchronization process can be canceled at any time. - Select Stop Sync Device Links from the Interface menu. - Watch the Event Log window to see when the synchronization process is canceled. ## Resetting your PowerLinc Sometimes, customer support will ask you to reset your PowerLinc. Here's the process: 1. In Indigo 5 or above, select the `Interfaces->Insteon/X10 Power Line->Disable` menu item 1. Unplug your PowerLinc and wait about 15 seconds 1. Press and hold the black set button on the side 1. While holding the button, plug it back in and continue to hold the button for about 15 seconds 1. Release the button 1. In Indigo, select the `Interfaces->Insteon/X10 Power Line->Enable` menu item 1. Select the `Interfaces->Insteon/X10 Power Line->Configure...` menu item 1. Click the `Interface Options...` button 1. Click the `Sync Links` button The last step will take a while, so watch the Event Log window for progress. ## Replacing Your PowerLinc If your PowerLinc is ever replaced, then you must re-sync all of your devices. This will ensure that all devices have their internal links updated to reflect the new PowerLinc's Insteon address. If you are replacing a 2414U with a newer PowerLinc, you may also need to [install the drivers](#install-the-ftdi-vcp-drivers) for your new PowerLinc. Once you have the driver installed, just connect the PowerLinc to your Mac. If you're switching from a 2414 to one of the current ones, you'll need to select it in the [config dialog for Insteon](#configuring-indigo-to-use-your-interface). Otherwise, you'll need to select the new Serial Port in that same dialog. Once you click "Save" on that dialog, Indigo will prompt you to resync all links - do that. Once it's done, all links in all devices should be correct. Be sure to have any battery-powered devices close at hand - you'll need to press and hold the set button to wake them up. Note: that any links that you create, either using the UIs described above, or by manual linking must be marked as Persistent in the link dialogs in order for them to be recreated correctly. For links that you create manually between devices, when you have the manual links created successfully, you must come back to Indigo and sync the device(s) links. The manual links will show up in the dialogs - you must then mark them as `Persistent in order for them to be retained when doing a resync/replacement. ## Other Insteon Features in Indigo The `Interfaces->Insteon/X10 Power Line` submenu contains a collection of miscellaneous Insteon commands that will help you manage specific aspects of devices as well as do so low-level Insteon commands not directly supported in the UI: ![Insteon Menu Image](../../../images/insteon_menu.png) Note that these are also actions available in the [Insteon Brand Specific](../../concepts/actions.md#insteon) submenu. ### Execute Raw Insteon Command This action will allow you to send a raw Insteon command to any Insteon device. You can send standard messages (2 bytes) or extended messages (16 bytes). You can also have the results of the command inserted into a variable for later processing. #### Set Motion Sensor LED Brightness This action will set the brightness of the LED that flashes inside the motion sensor when motion is detected. While the brightness value is between 0 and 255, 0 does not mean the LED is completely off - it's just very dim. Note: only revision 2 Motion Sensors with jumper 5 set can be configured. #### Set Motion Sensor Timeout This action will set the timeout value between the time the motion sensor stops detecting motion and when it sends the OFF command. The timeout values work like this: 0 is equal to 30 seconds and 255 is equal to 2 hours. Values in between are proportional to those values. **Note**: only revision 2 Motion Sensors with jumper 5 set can be configured. **Note**: a value of 0 will be interpreted as 3 for Motion Sensor II models. #### Set Motion Sensor Day/Night Sensitivity This action will set the sensitivity for when the motion sensor detects changes from dawn to dusk and vice versa. The sensitivity values work like this: 0 will make the sensor register day all the time and 255 is equal to night all the time. Values in between are proportional to those values. Note: only revision 2 Motion Sensors with jumper 5 set can be configured. #### Set LED Brightness This action will set the brightness of the LEDs on certain devices. Newer KeypadLincs are supported as well as some SwitchLinc models. Unfortunately there isn't really a way to tell you which devices are supported so you'll just have to try it and see if it works. You can script this action from Python: ```python insteonId = "com.perceptiveautomation.indigoplugin.InsteonCommands" insteonPlugin = indigo.server.getPlugin(insteonId) if insteonPlugin.isEnabled(): actionProps = dict() actionProps["brightness"] = 1 # a value from 1-100 actionProps["device"] = 123456 # the ID of the KeypadLinc or SwitchLinc actionProps["brightenMethod"] = "kpl" # the device is a KeypadLinc - use "swl" if it's a SwitchLinc insteonPlugin.executeAction("setLedBrightness", props=actionProps) ``` #### Set KeypadLinc Auto-Off Button Group This action will allow you to specify what buttons will go off automatically when you press any other button. Useful in conjunction with Toggle Mode below for creating "radio groups". See the [Fanlinc And Keypadlinc](fanlinc_and_keypadlinc.md) article for usage examples. #### Set KeypadLinc Button Toggle Mode This action will allow you to specify whether a button toggles (alternates between ON and OFF when pressed) or whether it sends a single command anytime it's pressed (can send either ON or OFF). Useful in conjunction with Auto-Off groups above for creating "radio groups". See the [Fanlinc And Keypadlinc](fanlinc_and_keypadlinc.md) article for usage examples. #### Turn On/Off KeypadLinc Buttons This action allows you to turn on/off groups of buttons. Why not just have multiple actions using the built-in Turn ON/Turn OFF LED actions? Because each of those requires a lot of Insteon traffic - and if you need to set several buttons at once this action will do it in one (or two if you want to maintain some buttons) action(s). It's more efficient and easier to configure (one action versus potentially seven actions). Select the action you want to take for each button: `Turn On`, `Turn Off`, `Leave Alone`. The latter option will require that we query the KPL to find the states first so if you select that for any of the buttons the action may execute a bit slower than it would otherwise. Note: using this action, which is sending raw Insteon commands through the IndigoServer, will cause the KeypadLinc's button states in Indigo to become out of sync. This is because the server doesn't know that you're changing the button states given that it's just a raw command message that it's being asked to send to the PowerLinc. If you need to keep the states in sync then add another action to do a status request to the KeypadLinc (after a short delay to avoid collisions). #### Configure SynchroLinc This action will allow you to configure the Trigger Watts, Threshold Watts, and Delay Seconds in a SynchroLinc. Here are the details of those settings: - Trigger Watts (0 to 1800 watts in 0.5 watt steps): the wattage needed before the SynchroLinc broadcasts. - Threshold Watts (aka hysteresis, 0 to 127.5 watts in 0.5 watt steps): tolerance before on/off toggle is sent. - Delay Seconds (0.15 to 38.25 seconds): prevents message flooding if thresholdWatts is too low. #### Set I/O Linc Momentary Mode This action will allow you to set the momentary mode of an I/O Linc to A, B, C, or None (the built-in UI only sets A or None). #### Set Siren Alarm Sound This action will allow you to set the sound that the siren makes when it's turned on. The choices are chime which is a softer sound, and siren which is a very loud sound. #### Set Siren LED Mode This action will allow you to set how the LED on the siren behaves. The LED can always be on or off, or it can flicker based on Insteon traffic. #### Set OutletLinc Load Sense This action will allow you to turn the load sense feature of the dual outlet OutletLinc on and off (on either the top or bottom outlet). ### Troubleshooting If you can't get a device to sync, follow these steps: 1. Hold down the shift & option keys while selecting the **Interfaces->Insteon/X10 Power Line->Configure...** menu item 1. In the resulting dialog, check the box next to **LILO debug logging** (leave the dialog open) 1. Switch back to the home window and try the define and sync with the AL again - you'll see a lot of debugging information show up in the Event Log window. 1. Starting with the first part of the define and sync, copy/paste all the event log lines into an email to support@indigodomo.com. 1. Switch back to the Insteon Debugging window and uncheck the **LILO debug logging** checkbox (and close the dialog) --- Advanced PowerLinc Options (https://docs.indigodomo.com/2025.2/user/interfaces/insteon/advanced-powerlinc/) --- # Advanced PowerLinc Options !!! abstract "In this guide" Covers the Advanced PowerLinc Options dialog: LED brightness controls, signal timing settings, and link sync operations including full factory reset. These options are only needed for specific troubleshooting scenarios or when instructed by Indigo support. Indigo provides some advanced options for the various PowerLinc Insteon interfaces. Select `Configure...` from the `Interfaces->Insteon/X10 Power Line` menu, then (assuming you have a PowerLinc interface selected) click on the `Interface Options...` button. You'll see the `Advanced PowerLinc Options` dialog: ![Advanced Powerlinc Options Image](../../../images/advanced_powerlinc_options.png) The first two options should always be checked by default - you really only want to disable those options if the Indigo Support team recommends it. From this dialog, you can also perform some other link syncing operations: - Start/Stop Link Sync (same as using the menu items on the `Interfaces->Insteon/X10 Power Line` menu) - On your PowerLinc, you can clear all of its internal links, read all the links, and sync links. You normally won't use these options unless instructed by Indigo Support - You can completely reset your PowerLinc and resync the links all in one go - this is roughly synonymous to doing a factory reset, but not quite. Indigo support will often recommend doing a factory reset vs using this option because the factory reset performs some actions that we can't do through software. --- FanLinc and KeypadLinc (https://docs.indigodomo.com/2025.2/user/interfaces/insteon/fanlinc_and_keypadlinc/) --- # FanLinc and KeypadLinc The FanLinc is a great device - it's custom-made to control ceiling fans including the light kits attached. And, if you consider a 6-button KeypadLinc and how it might control a FanLinc, we think you'll see that they are a great combination: ![Fan Linc Image](../../../images/kpl_fanlinc_buttons.png) When you look at the above image, it looks very logical and you would intuit that when you press the ON button the light goes on and when you press the OFF button the light goes off. Press and hold ON and it brightens, etc. When you press the `FAN HIGH` button the fan would switch to HIGH, etc. Think for a minute what the behavior of the LEDs on those center 4 buttons would be and you'd probably come up with the following: press HIGH and it would light up and the other three would be out. Press `FAN MED` and that light would go on and the `FAN HIGH` button would go out. Logical, right? This functionality is called (in most UI terminology) a radio group. One and only one button **must** be lit at a time. This is not how KeypadLinc buttons work by default - they generally work independently of one another. You can, however, tell the KeypadLinc to create radio groups like that. It can be done manually at the KeypadLinc (read the supplied instructions that came with your KeypadLinc for details) but it's quite tedious. Because we figured a good number of you might be interested in doing this, we added some Insteon commands that will allow you to configure the KeypadLinc so that it has a radio group like this. In fact, there are two parts to setting up a radio group. First, you have to configure which buttons go OFF when another button is pressed. So, for instance, when you press the `FAN HIGH` button (button 3 in Insteon terminology), you want the `FAN MED`, `FAN LOW`, and `FAN OFF` buttons to go off if they're on. And so on for each of the other buttons. Each of these is a separate Insteon instruction to the KeypadLinc. But that doesn't get you all the way there - the buttons are still toggling between ON and OFF which you don't want - you always want one and only one button lit because one of the buttons represents OFF. So the next step you want to take is to set each of the buttons into Non-Toggle Mode - and have the button always send an ON command when pressed (so it lights up if it's not already lit). Ok - so we've established that there are two steps to creating a radio group: create the Auto-Off groups for each button then put each button into Non-Toggle mode and tell them to send ON every time. How do you actually do it? First, you need to create the KeypadLinc radio groups (only available in Pro versions). ## Creating Radio Groups in Indigo 6 Pro and above In Indigo 6, we added some convenient menu items on the `Interfaces->Insteon/X10 Power Line` submenu to perform some advanced Insteon configuration functions. First, you want to create the auto-off button groups which will cause the other buttons to go off when one of the buttons is pressed (goes on). Select the `Interfaces->Insteon/X10 Power Line->Set KeypadLinc Auto-Off Button Group...` menu item, and you'll see this dialog: ![Auto Off Button Group Menu Image](../../../images/auto_off_button_group_menu.png) Select the KeypadLinc you want to work with from the KeypadLinc popup. Then, for each button in the group, repeat these steps: 1. Select a button in the group (for instance, button 3) that, when pressed, should cause the other buttons to go out 2. Select the checkboxes next to the other buttons in the group (for instance, 4, 5, and 6) that should go out when the button selected above is pressed 3. Click `Execute` Repeat for buttons 4-6. This creates the auto off functionality. ### Setting Buttons Into Non-Toggle Mode Next, we want to configure the buttons to always go ON (and send the ON command) when pressed. This is called non-toggle mode because the buttons don't toggle on and off when you press them. Select the `Interfaces->Insteon/X10 Power Line->Set KeypadLinc Button Toggle Mode...` menu item, and you'll see this dialog: ![Button Toggle Mode Menu Image](../../../images/button_toggle_mode_menu.png) Select the `Non-Toggle` checkbox for each button in the group (3, 4, 5, and 6). Leave the `Button X sends ON` checkbox checked, since you want those buttons to always send ON commands. When you have them all selected, click `Execute`. That's it - your buttons are now configured in a radio group. Proceed to the [Creating the Links](#creating-the-links) section for the next steps. ## Creating Radio Groups in Indigo 5 Pro With Indigo 5.1.1 we released a plugin called Insteon Commands. This plugin allowed us to add more obscure and/or advanced Insteon commands to Indigo more quickly than adding it natively to the Mac client UI. What you'll do is create an Action Group that executes several plugin actions, each of which will send the appropriate commands to the KeypadLinc. Once you've created the Action Group, you will execute it and several seconds later your KeypadLinc will be configured correctly. Specifically, you'll need to use two of the actions in this plugin to create your radio group. First, an Action Group (called "Set up radio group" or something) and create a `Set KeypadLinc Auto-Off Button Group` for each button (3-6) that will turn OFF all the other buttons. For instance, here's the configuration for button 3: ![Auto Off Button Group Image](../../../images/autooffbuttongroup.png) Repeat the action for each of the other (4-6) buttons, turning off the rest. Next, create a `Set KeypadLinc Button Toggle Mode` action that sets buttons 3-6 to Non-Toggle mode (and leave the `Button # sends ON` checkboxes checked): ![Toggle Button Image](../../../images/togglebutton.png) That's it - execute the Action Group and the buttons should be set up such that when you press one it will go on and the others will go off. If not, revisit each of your actions and make sure that you have them configured correctly. ## Creating the Links { #creating-the-links } Good - you've taken the first step to get your 6 button KeypadLinc controlling your FanLinc. The next step is to link the KeypadLinc buttons to the FanLinc so that when you press them they control the fan and light respectively. Creating Insteon links is covered in the [Managing Insteon Devices](index.md) document - you want to link button 1 to the FanLinc lights and buttons 3-6 to high, medium, low, and off respectively. Congratulations! You now have the KeypadLinc directly controlling fan speeds via the middle 4 buttons and the fan light being controlled by the top and bottom buttons. Done! ## Almost Done Well, we're almost done. If you never intend to control the fan from Indigo, then yes, you're done. However, if you also want to control the fan from Indigo as well as from the KeypadLinc, you need to do just a bit more work. As you might have read in the multi-way groups and KeypadLinc Buttons articles, keeping KeypadLinc buttons in sync when controlling the lights they're linked to requires a little more work. This is because when you control a device from Indigo, we have to use direct commands. This will not cause the links you've created between devices to be activated (there isn't a way to simulate a button press on a KPL to cause that behavior). Because of that, the KeypadLinc buttons that are linked to other devices can get out of sync if the device is controlled from Indigo. One other thing that complicates this particular scenario is the fact that Indigo can't create multiple links to the same device in a single group. For instance, you can't create a PowerLinc scene that includes multiple buttons on the same KeypadLinc. This is a limitation in Indigo that's not likely to be overcome soon because of the nature of how those links are maintained and how Indigo treats devices and links. Because of these things, you'll need to control the FanLinc Fan through Action Groups (rather than directly) so that the KeypadLinc's buttons will stay in sync. Fear not, there's an action in the [Insteon Actions](index.md) section that will help with that as well. The simplest approach is to create 4 triggers - one each for when the Fan speed becomes `High`, `Medium`, `Low`, and `Off`. The action for those actions will be to set the KPL's button LEDs appropriately. Rather than setting each LED separately using the built-in mechanism for setting LEDs (which would require a lot of Insteon communication), you can use the `Turn On/Off KeypadLinc Buttons` action in the [Insteon Actions](index.md) section of the Action type popup. That will only send 1 command to the KPL to set the state of all buttons. So, for the trigger that fires when the Fan speed becomes `High`, in the action's config dialog you would select the KeypadLinc, then select `Turn Off` for buttons 1, 2, 4, 5, 6, 7, 8 and `Turn On` for button 3. Now, you might be asking yourself why we'd want to turn off 1, 2, 7, and 8. Actually, because this is a 6 button KeypadLinc, those actions will be ignored since 1 and 2 are the load ON button and 7 and 8 are the load OFF button. The only important ones to turn off are 4 (`Medium`), 5 (`Low`), and 6 (`Off`) since the fan is on `High`, and turn button 3 (`High`) on. Now, if you're operating the switch buttons manually, this will already be the case - but that's OK. If you're operating the fan from somewhere else - like a control page - this will keep the KeypadLinc's LEDs in sync. --- Insteon Links (Scenes) (https://docs.indigodomo.com/2025.2/user/interfaces/insteon/insteon_links/) --- # Insteon Links Insteon devices provide a simple way to link together so that one device knows about and can (optionally) control another. And, unlike X10, you can link multiple devices together in different ways to create groups or scenes. For instance, let's say you have a SwitchLinc called "Dining Room", a KeypadLinc (that has 6 buttons) called "Hallway", a LampLinc called "Office", and another SwitchLinc called "Media Room". You could link these devices together in the following ways (these are just a few examples): 1. Button 3 (the first small button) on "Hallway" could control "Office" such that pressing and holding (when off) would brighten that light, and doing so when it was on would dim that light. 2. By linking together "Dining Room", "Hallway", and "Media Room", you could have all of those lights come on, dim, brighten, and go off at the same time, same rate, etc. 3. You could have a button 4 on "Hallway" set each light to a different setting, creating a scene. All of these things can be done using just the link protocols that are part of the Insteon specification. Creating these links manually in the devices can be a little cumbersome - you have to walk around your house putting each device into link mode, for each link you want to establish, on each device. For the last example, assuming you wanted to set "Dining Room", "Office", and "Media Room", you would need to touch "Hallway", then the device, to create the link. That's 6 device touches for just that scene. That's where Indigo can help. Indigo will allow you to [create almost any Insteon links](index.md) that can be created manually, without having to walk around your house putting switches into link mode, setting options, etc. Indigo talks to the Insteon network via the PowerLinc 2414U interface. This interface is just like any other device: it can create links, etc. What's special about the PowerLinc is that it has a USB port that allows your Mac to communicate with it. So, your Mac can see what Insteon signals are being sent and can send those signals as well. You can accomplish many of your Home Automation scenarios without Insteon links - for instance, you could have Indigo listen for a button press on "Hallway", and then Indigo would perform the actions of setting each light to a different brightness. There is a lot of flexibility in this approach because it's easier to change what actions are performed and Indigo has a much wider variety of actions to perform - not only directly controlling HA devices, but using more logic to determine if an action should be performed. For instance, let's say that we want button 4 to operate as described above, but also if the time of day is between dusk and dawn, you want it to additionally turn on a new switch, called "Porch Light". Using only Insteon links, you couldn't accomplish this logic. However, if you were using Indigo, you could accomplish this. There are several ways, but the easiest would be to create 2 almost identical trigger actions: both would trigger when button 4 of "Hallway" was turned on. The difference would be that the condition for one would be "If dark" and the other would be "If daylight". The actions for both would be identical except you'd add another action to the "If dark" trigger that would also turn on "Porch Light". So, why would you choose one over the other? Here are some advantages and disadvantages to each approach: **Insteon Links** - Don't require Indigo to be running to work - Act instantaneously - that is, if dimmable lights are linked together, they can dim and brighten at the same rate as you hold down the button/switch - Links aren't necessarily easily edited or altered - it's a little more difficult to see what's linked to what **Indigo Triggers** - Allow for considerable flexibility - Indigo has many more action types - Require Indigo to be running - Because of the extra step of communication between Indigo and the PLC, actions aren't necessarily immediate - a delay of up to a couple of seconds between the time the trigger fires and the time that Indigo can start sending Insteon commands is possible. So, now that you have a general idea of each method, the good news is that you can use both at the same time! So, for instance, after you set up button 4 using Insteon links as described above, you could then add a trigger action in Indigo that would, if it's dark, turn on "Porch Light" as well. --- PowerLinc 2413S (https://docs.indigodomo.com/2025.2/user/interfaces/insteon/powerlinc-2413s/) --- # PowerLinc 2413s !!! abstract "In this guide" Setup instructions for users running the PowerLinc 2413S serial Insteon interface via a USB-to-serial adapter. Includes a factory reset procedure for units previously used with another home controller (ISY, etc.) and driver installation notes. Although Indigo officially supports the USB PowerLinc 2413U interface, users have reported success using the serial version (2413S) along with USB to serial adapters. Note that if the 2413S was previously used with another home controller (ISY, etc.) then you should first factory reset it: 1. Unplug the PowerLinc 2413S from the Mac. 1. Unplug the PowerLinc 2413S from the wall outlet. 1. Hold down the SET button on the device for 20 seconds. 1. While still holding the SET button plug it back into the wall and keep holding the SET button for 20 additional seconds, then release. 1. Install the USB to serial adapter driver, plug the adapter into the 2413S and your Mac, then select the correct Serial Port in the interface settings for the device. --- Connecting X10 Interfaces (https://docs.indigodomo.com/2025.2/user/interfaces/x10/) --- # Connecting X10 Interfaces !!! abstract "In this guide" How to connect an X10 RF interface and add X10 devices to Indigo using house/unit codes. Covers the device type selection dialog, the Module Definition dialog for unlisted devices, and how to control X10 devices from Indigo actions. If you are using a PowerLinc or CM15 then enable it as described in the [Managing Insteon Devices](../insteon/index.md#connecting-insteon-and-x10-power-line-interfaces) document. The process for connecting X10 **RF** Interfaces is basically the same as for power line interfaces - you just need to select the correct serial port. The W800USB will require installing the [FTDI drivers](../insteon/index.md#install-the-ftdi-vcp-drivers) mentioned above. You only need to use this interface if you're using a separate X10 RF interface like the W800 or MR26. ![X10 RF Configuration Dialog Image](../../../images/x10_rf_config_dialog.png) ## Adding and Managing X10 Devices First, make sure that Indigo has enabled communication with the interface. If Indigo is not online with the interface, then choose the `Interfaces->Insteon/X10 Power Line->Enable` menu item. See [connecting the Interface](../../getting-started/index.md#connecting-insteon-and-x10-interfaces) in the [Getting Started](../../getting-started/index.md) guide for more details. !!! tip "TIP" You may also need to bridge the two phases of your power line to get reliable communication. Search for "X10 signal bridge" in your favorite search engine for details. To add an X10 device, select `DEVICES` in the outline view (or one of it's sub-folders) and click the `New...` button. Select `X10` from the `Type:` popup and you'll see the following: ![X10 Device Detail Image](../../../images/x10_device_detail.png) Every X10 device in Indigo is defined by a collection of settings that detail the characteristics of that device. For example, there are several different X10 codes used to dim and brighten light switch devices. Depending on the type of device module used, it will respond to some X10 codes for dimming but not others. And some devices have additional features, like the ability to transmit back to Indigo when the device is turned on and off locally at the device itself. Normally, you will not need to worry about these device settings. By choosing the correct device type in the device dialog, Indigo automatically chooses the correct settings for that particular device module. However, you may have a device module not currently listed in the Type popup menu. To use devices that are not defined in the device Type popup, you can click the `Definition...` button you will be shown the `Module Definition` dialog: ![X10 Definitions Dialog Image](../../../images/x10_definitions_dialog.png) If you know the specific features of your X10 device you can manually set them here. If you have difficulties finding the settings for a device you have, then visit our [online support forum](https://forums.indigodomo.com/). It is possible that one of our users has already discovered the correct settings to use. --- X10 RF Options (https://docs.indigodomo.com/2025.2/user/interfaces/x10/rf-options/) --- # X10 RF Options !!! abstract "In this guide" Covers the RF Interface Options dialog for X10 RF interfaces, where you specify which house codes the interface monitors and optionally remap and retransmit received signals on the power line. Indigo provides some advanced options for the various X10 RF interfaces (including the CM15 which is both Power Line and RF). Select `Configure...` from the `Interfaces->X10 RF` menu, then click on the `Interface Options...` button. You'll see the `RF Interface Options` dialog: ![X10 RF Options Image](../../../images/x10_rf_options.png) This dialog will allow you to specify which X10 house codes the interface will respond to. You can also optionally have that house code remapped to another house code and retransmitted on the PowerLine. --- Managing Z-Wave Devices (https://docs.indigodomo.com/2025.2/user/interfaces/z-wave/) --- # Managing Your Z-Wave® Network !!! abstract "In this guide" How to connect and configure a Z-Wave interface, add and interview Z-Wave devices, perform secure inclusion for locks and sensors, and troubleshoot common interview failures. Also covers Z-Wave network repair, device removal, and managing device associations. Indigo provides several tools to manage your [Z-Wave](about.md) network. Not only can you create/control/delete devices but you can also [manage device associations and scenes](#manage-associations). See the [Connecting Z-Wave Interfaces](#connecting-z-wave-interfaces) section below for details on connecting and configuring your Z-Wave interface. Tip: if you're having issues with Indigo communicating with some of your Z-Wave devices, look at the position of your Z-Stick. If you have it plugged directly into your Mac then the chances are that the signal range is reduced somewhat. Also, if there are any other electronics close to it (external hard drives, etc.) then you may want to try to move the interface around a little. For the Z-Stick, try a USB hub with connectors on the top that allow you to position it vertically. A word of warning: some users have used short USB extension cables successfully but others have found that these sometimes cause communication errors with the Z-Stick. ## Connecting Z-Wave Interfaces First, check out our [supported interfaces list](http://www.indigodomo.com/devices/interfaces/) to see if the interface you want to use has been tested with Indigo. ### Install the Appropriate Drivers The [Aeotec Z-Stick Gen5/Gen5+](https://aeotec.com/z-wave-usb-stick/) uses a built-in driver included with Mac OS X (AppleUSBCDC Modem), so you don't need to install any drivers for it. Note that if you move the stick to a different port or if you upgrade to a new OS version you will need to reselect the serial port. The Aeotec [Z-Stick Series 2](https://smile.amazon.com/Aeon-Labs-DSA02203-ZWUS-Z-Wave-Z-Stick/dp/B003MWQ30E/) and [Z-Stick 7](https://aeotec.com/products/aeotec-z-stick-7/) interfaces require that you install the [Silicon Labs VCP Driver Kit](http://www.indigodomo.com/silabsurl) for Mac OS X. Make sure you have restarted after the installation process. If you reinstall your OS, then you will need to rerun the driver installer. Warning: Before upgrading macOS to the next major revision, please visit the [Announcements section of our forums](https://forums.indigodomo.com/viewforum.php?f=2) to see if there are potential issues with the OS update. The [SmartStick+](https://shop.homeseer.com/collections/z-wave-usb-sticks-network-controllers/products/homeseer-smartstick-g-usb-z-wave-stick) is a serial interface from HomeSeer that also uses the built-in driver included with Mac OS X (AppleUSBCDC Modem), so you don't need to install any drivers for it. Note that if you move the stick to a different port or if you upgrade to a new OS version you will need to reselect the serial port. The [GoControl QuickStick Combo](https://smile.amazon.com/QuickStick-Combo-HUSBZB-1-Nortek-Cert/dp/B0157GOEA8/) is an interface that includes both Z-Wave and Zigbee (though Zigbee isn't supported natively in Indigo). It also requires that you install the [Silicon Labs VCP Driver Kit](http://www.indigodomo.com/silabsurl) for Mac OS X, specifically v5 or later. It will then present 2 new serial ports: GoControl_zwave (which is the one you select) and GoControl_zigbee (for the Zigbee interface). As mentioned before, other Z-Wave Interfaces that support the Z-Wave Serial API may be compatible and those may require other drivers. Check the [Interface Hardware](https://www.indigodomo.com/devices/interfaces/) list to see what interfaces we've actually tested. ### Connecting the Z-Wave Interface Plug the Z-Stick into an available USB port connected to your Mac. We've done some testing with this interface connected to a USB hub with favorable results (unlike some other Insteon and X10 interfaces). However, if you experience any type of errors when trying to configure/use the interface you may try plugging the stick directly into a USB port on your Mac. Tip: We highly recommend that you position your Z-Stick carefully. If you have it plugged directly into your Mac then the chances are that the range is reduced and you may have problems communicating with devices that are physically far away. Also, we recommend getting a powered USB hub that allows the Z-Stick to stand straight up for the best signal. Avoid using USB extension cables as they have been known to cause issues. ### Configuring Indigo to use Your Z-Wave Interface ![Z-Wave Configuration Dialog Image](../../../images/zwave_configuration_dialog.png) Once you have your interface plugged into a USB port, you can enable and configure Z-Wave in Indigo: 1. Choose the `Interfaces->Z-Wave->Enable` menu item. This should cause the `Configure Z-Wave` dialog to open automatically (but only the first time you enable Z-Wave - you can select the `Interfaces->Z-Wave->Configure...` menu item to get back to the configuration dialog later): 1. The default `Connection Type` of `Local (physical)` is correct (assuming you've plugged your Z-Stick directly into your Mac). 1. On the `Serial Port` popup, you should select the serial port titled `SLAB_USBtoUART` (for Z-Stick Series 2) or one that *starts* with `usbmodem` (for Z-Stick Gen5), depending on which Z-Stick version you have. If you have two serial ports that start with `SLAB_USBtoUART` that means that you have multiple devices that use the Silicon Labs VCP driver - you'll just need to try each one to determine which is the correct port if you don't know beforehand. Unfortunately, that driver doesn't handle multiple devices using the Silicon Labs chip very well, and that number may change when you reboot. 1. Unless instructed by Perceptive Automation support, you should leave the `Show debug logging of interface communication` checkbox unchecked. 1. Save the `Configure Z-Wave` dialog. ### Enabling and Disabling Z-Wave Communication Choose the `Interfaces->Z-Wave->Disable` (or `Enable`) menu item to disable/enable the interface. We recommend that you finish skimming this document and the overview document so that you'll get a firm understanding of the basics of Indigo. However, if you want to jump ahead, you can go directly to the document that discusses how to create and manage your [Z-Wave network](index.md). ## Adding a Z-Wave Device Indigo supports a [variety of Z-Wave devices](http://www.indigodomo.com/devices/#zwave). Before proceeding, make sure that you have [connected and enabled your Z-Wave interface](../../getting-started/index.md#connecting-z-wave-interfaces). To add a Z-Wave device to Indigo, follow these steps: 1. Select `DEVICES` in the Main Window outline view or select one of the sub-folders. 1. Click the `New...` button. You'll see the `Create New Device` dialog. 1. Select `Z-Wave` from the Type popup menu. 1. Click the `Define and Sync...` button. 1. You'll see the `Synchronize Z-Wave Device` dialog. 1. Follow the directions on the dialog to complete setting up the device. ![Z-Wave New Device Dialog Image](../../../images/zwave_new_device_dialog.png) Note: all battery-powered Z-Wave devices will go to sleep to conserve battery power - when they are asleep, they will not respond to commands. Indigo needs to send the device some commands when it's adding the device, so you'll need to follow the manufacturer's instructions to wake the device up when you actually add the device to Indigo. IMPORTANT: When adding a device (particularly a battery-powered device), the device may enter sleep mode before we can finish all the sync activities needed to add the device. If this happens, you'll get an error before the sync is finished. In this case, you can often just repeatedly tap the include button/paddle, tamper button/mechanism, etc., to keep the device awake. Another option is to remove the batteries from the device and wait a few minutes. Then reinsert them (which will often put the device into an awake status for several minutes) then do the sync again. ### Using Encryption As mentioned in the dialog, we recommend using encryption only for devices where it is beneficial or required (like locks). Although encryption increases security, it can also degrade responsiveness of the hardware and increase the potential for network congestion and retries. The additional commands required for encryption can also decrease battery life. ### Changing Existing Hardware Inclusion to Use Encryption If you added a device to the network (Z-Wave Controller) without encryption enabled and then wish to enable encryption you must first remove it from the network. This is because the encryption key exchange can only occur with Indigo if the device has just been added to the network (within a few seconds), and before a device can be added to the network it must not be included in any networks. To do this you can use the `[Interfaces->Z-Wave->Start Controller Exclusion Mode](#start-controller-exclusion-mode)` menu item to put the Z-Wave Controller into exclusion mode, then follow the steps from the device's manual to have it exclude itself from the network. Once excluded (you can see the progress in the `[Event Log window](../../mac-client/event-log.md#event-log-window)`), you can then use the `New with Encryption Enabled` button inside the Synchronize Z-Wave Device dialog to re-add it to the network with encryption. ### Moving a Z-Wave Lock from a Different Controller to Indigo If you have a Z-Wave lock currently included with another controller, you must first exclude the lock from the network before adding it to Indigo with encryption enabled. To do this you can use the `[Interfaces->Z-Wave->Start Controller Exclusion Mode](#start-controller-exclusion-mode)` menu item to put the Z-Wave Controller into exclusion mode, then follow the steps from the device's manual to have it exclude itself from the network. Note you do not have to use the original controller to perform the exclusion -- you can use Indigo's exclusion process. Once excluded (you can see the progress in the `[Event Log window](../../mac-client/event-log.md#event-log-window)`), you can then use the `New with Encryption Enabled` button inside the Synchronize Z-Wave Device dialog to add it to Indigo's Z-Wave network with encryption. Follow the instructions from the lock's manual for the inclusion steps required. Note that either the Z-Wave Controller used by your Mac or a device that supports Z-Wave beaming will need to be close to the lock for the communication to succeed. ### Editing a Z-Wave Device's Properties { #editing-a-z-wave-device-s-properties } Once you've defined your device, you can edit its properties. If you've still got the device dialog open, you can skip the first three steps below. 1. Select `DEVICES` in the Main Window outline view or select one of the sub-folders. 1. Find the device in the device table and double-click it (or select it and click the `Edit...` button) 1. You'll see the standard `Edit Device` dialog 1. Click on the `Edit Device Settings...` button and you'll see the configuration dialog: ![Z-Wave Dialog Settings Image](../../../images/zwave_dialog_thermo_settings.png) The top part of that dialog shows some details about the device that will help support when diagnosing problems. Next, the polling properties for the device are shown if they are applicable to the device type. The first thing you need to determine is if the device needs to be polled to check for status changes. Most devices will require some kind of polling to stay in sync. There are a few devices that Indigo can determine status changes on automatically, and we'll turn off the checkbox for those when we define the device. There are many devices that don't send out this information though (primarily because of some patents held by Lutron) so we give the option to poll the device. Next, select the desired polling interval. This setting is quite important actually - the more frequently you poll a device the more congested your network will become. In fact, we can't guarantee an exact polling frequency below 5 minutes because we have to watch for the network to become idle before we can try polling. So, we suggest that the less important devices to keep in sync are polled at longer intervals and more critical devices get polled immediately. You can also poll devices that can't or don't get manually operated at a much lower frequency because Indigo will update the state as soon as the command to operate the device is acknowledged. The top item on the list, `Only When Activity Detected`, is a great optimization - some devices send out a message whenever they change - they don't actually send the necessary information for Indigo to automatically update state, but it is enough to bump the device to the top of the poll list so that the status update will occur much more quickly. And it keeps us from having to poll the device at regular intervals because we can just poll it when we see this specific message. How do you know to select this option? You can try the setting out: just select `Only When Activity Detected` and save the device. Then, go manually operate the device. If the status updates (within about 10 seconds) then you can leave it. If the status never updates then you'll need to pick one of the other intervals as described above. The fastest polling option is `As Often as Possible` - we'll poll the device as frequently as we can, given the conditions of the Z-wave network. Also shown in the settings dialog are any configuration parameters specific to that device (in this example the temperature units and display contrast). Indigo only shows settings if there is a custom device profile for a particular device. However, if no settings are shown you can still modify any configuration parameters by using the `[Interfaces->Z-Wave->Modify Configuration Parameter...](#modify-configuration-parameter)` menu item. If a `Submit Device Information` button is visible, then you can use it to help us learn more about the device. When pressed a page will open in your browser asking for some more information that will help us and other users more effectively use the device. Please take time to accurately fill out the form as much as possible. The more information we have the more likely we will be able to add a custom device profile to fully support the device. Maintaining our [Z-Wave Supported Device List](http://www.indigodomo.com/devices/#zwave) is a community effort since we can't possibly directly test every Z-Wave device available worldwide. We appreciate your help! ## Resyncing a Z-Wave Device Sometimes you may be instructed by support to resync your device - the process is quite simple: 1. Select `DEVICES` in the Main Window outline view or select one of the sub-folders. 1. Find the device in the device table and double click it (or select it and click the `Edit...` button). 1. Click the `Define and Sync...` button. 1. Click on the `Sync` button. 1. When the `Synchronize Z-Wave Device` dialog disappears, just close the `Edit Device` dialog. This will tell Indigo to query the device and update various information about it and may help with some communication issues. ## Replacing a Z-Wave Device If a Z-Wave device fails (and depending on the type), you may be able to just replace it with a new one and all Triggers, Conditions, Actions, and Control Pages will continue to work. To replace a device: 1. Select `DEVICES` in the Main Window outline view or select one of the sub-folders. 1. Find the device in the device table and double click it (or select it and click the `Edit...` button). 1. Click the `Define and Sync...` button. 1. Follow the instructions on the dialog to use your controller to include the device into your network if you haven't already. 1. Select the new device in the `Sync using node` popup. 1. Click on the "Sync" button. 1. When the `Synchronize Z-Wave Device` dialog disappears, just close the `Edit Device` dialog. Note that not all devices can be replaced. Specifically, if a [device has multiple personalities](../../concepts/devices.md#devices-with-multiple-personalities) (like, for instance, a multi-sensor that has a motion sensor, temp sensor, humidity sensor, etc.) and any of those dependent devices are used in Triggers, Conditions, Actions, or Control Pages, then you'll get an error message in the `[Event Log window](../../mac-client/event-log.md#event-log-window)` saying that you need to manually resolve those conflicts (or manually delete the device). Indigo helps with this process - see [Deletion Dependencies](../../concepts/deletion-dependencies.md) for details. IMPORTANT: When replacing or resyncing a device (particularly a battery-powered device), the device may enter sleep mode before we can finish all the sync activities needed to re-add the device. If this happens, you'll get an error before the sync is finished. In this case, you can often just repeatedly tap the include button/paddle, tamper button/mechanism, etc., to keep the device awake. Another option is to remove the batteries from the device and wait a few minutes. Then reinsert them (which will often put the device into an awake status for several minutes) then do the sync again. ## Excluding a Z-Wave Device From time to time, you may find the need to exclude a device from your Z-Wave network. For example: - you want to factory reset it (resetting doesn't necessarily require a device to be excluded from the network), - replace a device with a newer device, or - add a device to your setup that was previously included in another controller. There are some important points to remember about device exclusion: - Excluding a device from the controller releases the device from the controller and allows it to be used with another controller. - If your controller has failed and you want to add your device to a new controller, you can perform the exclusion with your new controller (essentially any controller can exclude a device--even if the device was included with a different controller). - Just because you've excluded a device from the network, doesn't mean that all the other devices that used to talk to it know it's gone. That's why it's always a good idea to optimize your network after making big changes (adding or removing many devices or changing the location of many devices or the controller). You can usually preserve your Indigo setup when excluding a device. The Indigo device object and all the things that refer to it are independent of the physical device itself. Simply exclude the first device, include the second one and then point your Indigo device definition to the new device. It will be shown in the `Sync using node` dropdown as an Available Node ID: ![Available Node ID](../../../images/available_node_id.png){ width=400 } This assumes that you're replacing a device with one that is of a similar type. To exclude a device: - Depending on your device, you may need to bring the device into close proximity with the controller. Alternatively, if your controller supports it, you can take your controller to the device. - Select the `Interfaces→Start Controller Exclusion Mode`. - Following your device's instructions, initiate an exclude action (different devices may require different steps). - If the exclusion has been done successfully, Indigo will automatically exit the exclusion and the device should no longer appear in the Z-Wave device list. ## Deleting a Z-Wave Device When deleting a Z-Wave device, it's always a good idea to exclude it from your network after you've deleted it in Indigo (if it's still functional). So, the process is this: 1. Select `DEVICES` in the Main Window outline view or select one of the sub-folders. 1. Find the device in the device table and select it. 1. Click the `Delete...` button (or press the Delete key on the keyboard). If the device has any [deletion dependencies](../../concepts/deletion-dependencies.md), Indigo will show the dependency dialog that will allow you to review and/or modify any of those dependencies. If you choose to continue the deletion process, the device will be deleted along with the dependencies. The last thing you'll want to do is to exclude the device from your network if it's still functional. To do this you can use the `[Interfaces->Z-Wave->Start Controller Exclusion Mode](#start-controller-exclusion-mode)` menu item to put the Z-Wave Controller into exclusion mode, then follow the steps from the device's manual to have it exclude itself from the network. ## Replacing a Z-Wave Controller You may need to replace your controller. This section describes the steps involved in moving your Z-Wave devices to a new controller. If your controller doesn't have a physical inclusion button you must follow this process. You may also use this process even if your controller does have a button (such as the Aeotec Z-Stick) as it's the more "universal" approach: 1. Remove your old controller from your Mac. 1. Plug your new controller into your Mac and [reconfigure the Indigo Z-Wave Interface](../../getting-started/index.md#connecting-z-wave-interfaces) to use the new serial port. 1. Select the `Interfaces->Start Controller Exclusion Mode`. Note you can exclude from any controller, not just the one it was included with. 1. Go to the Z-Wave device closest to your Mac that’s not battery powered (also skip any that require encryption) and perform its exclusion process. 1. Go back to your Mac, edit the device you just excluded, click the `Define and Sync` button and press the `New with Encryption Disabled` (only select Enabled if you're including a device which requires encryption to work correctly, such as a lock). 1. Go back to your device and perform its inclusion process (press its LINK button, etc.). 1. Return to your Mac (it should say `Inclusion into network successful` in green just under the `Add to network` buttons), confirm that the new node shows up in the popup at the bottom, and then press `Sync`. 1. Repeat steps 3-7 for each non-battery powered device from the closest to your Mac to the furthest. 1. Repeat steps 3-7 for each battery powered device or devices that require encryption. 1. Finally, select the `Interfaces->Z-Wave->Optimize Z-Wave Network...` menu item and click `Start Optimization` - you can do this at night just before going to bed when the network is likely to be the least busy. If your controller has a physical button, you may choose to follow this process because it requires fewer walks from your Mac to the devices being moved to the new controller: 1. Remove your old Z-Stick from your Mac. 1. Plug your new Z-Stick into your Mac and [reconfigure the Indigo Z-Wave Interface](../../getting-started/index.md#connecting-z-wave-interfaces) to use the new serial port. 1. Unplug your new Z-Stick from the Mac and take it to the Z-Wave device closest to your Mac that’s not battery powered (also skip any that require encryption) and exclude it. Note you can exclude from any controller, not just the one it was included with. 1. While at the device, include it into the new Z-Stick. 1. Plug the new Z-Stick back in to your Mac. 1. Open the config dialog for the device you included and click the `Define and Sync` button. 1. Select the new node number from the popup at the bottom and `Sync`. 1. Repeat steps 3-7 for each non-battery powered device from the closest to your Mac to the furthest. 1. Repeat steps 3-7 for each battery powered device or devices that require encryption. 1. Finally, select the `Interfaces->Z-Wave->Optimize Z-Wave Network...` menu item and click `Start Optimization` - you can do this at night just before going to bed when the network is likely to be the least busy. ## Z-Wave Menu Options The `Interfaces->Z-Wave` submenu contains several Z-Wave specific menu items to help manage your Z-Wave network and devices. Below are the current options. ![Z-Wave Menu Image](../../../images/zwave_menu.png) ### Manage Associations ![Z-Wave Dialog Manage Associations Image](../../../images/zwave_dialog_manage_associations.png) Indigo supports defining Z-Wave Associations (which are similar to Insteon links) between devices that support that functionality. Note that battery operated devices which are asleep will need to be woken up (per their instructions manual) for associations to be edited. ### Modify Configuration Parameter ![Z-Wave Dialog Modify Configuration Parameters Image](../../../images/zwave_dialog_modify_config_parms.png) Some Z-Wave devices provide configuration options through the use of configuration parameters. These are generally outlined in the documentation that comes with a device. Indigo often times support setting these parameters directly in the device config dialog, but because of the sheer number of Z-Wave devices we can't add every one. This menu item will allow you to set any config parameter that a device accepts. **Note** - this process can cause your device to not function correctly if incorrect parameters are entered so you'll want to make sure you are very careful to use only the params specified for the specific device. ### Send Raw Z-Wave Command ![Z-Wave Dialog Send Raw Command Image](../../../images/zwave_dialog_send_raw.png) This menu selection can be used to send arbitrary Z-Wave protocol-level commands to any Z-Wave device. This is generally only useful when Support instructs you to do so. Note battery operated devices allow for the option to queue the command to be sent the next time the device wakes. ### Start Controller Inclusion Mode This menu selection puts the Z-Wave Controller into inclusion mode. You can then follow the steps from the device's manual to have it include itself from the network. Open and watch the `[Event Log window](../../mac-client/event-log.md#event-log-window)` for progress as it is included. This menu item is functionality the same as using the `New with Encryption Disabled` button inside the `[Synchronize Z-Wave Device](#adding-a-z-wave-device)` dialog. ### Start Controller Inclusion Mode with Encryption This menu selection puts the Z-Wave Controller into inclusion mode with encryption enabled. You can then follow the steps from the device's manual to have it include itself from the network. Open and watch the `[Event Log window](../../mac-client/event-log.md#event-log-window)` for progress as it is included. This menu item is functionality the same as using the `New with Encryption Enabled` button inside the `[Synchronize Z-Wave Device](#adding-a-z-wave-device)` dialog. ### Start Controller Exclusion Mode This menu selection puts the Z-Wave Controller into exclusion mode. You can then follow the steps from the device's manual to have it exclude itself from the network. Open and watch the `[Event Log window](../../mac-client/event-log.md#event-log-window)` for progress as it is excluded. Note Indigo is able to exclude devices from any Z-Wave network. ### Stop Inclusion / Exclusion This menu selection exits both inclusion and exclusion mode. ### Optimize Z-Wave Network ![Z-Wave Dialog Optimize Network Image](../../../images/zwave_dialog_optimize_network.png) Indigo can optimize your Z-Wave network by having devices rediscover which devices they are close enough to communicate with. This information is then reported back to the Z-Wave Controller so network routing tables can be updated. ### Report Failed Modules to Event Log This menu item will write any devices that the Z-Wave interface believes may have failed to the Indigo Events Log. This list is managed by the Z-Wave interface (not by Indigo) and it's important to note that **the listed devices may not have actually failed** (the interface may list a device that it hasn't received a transmission from for a while, for example). It's recommended that you use this menu item from time to time to ensure that the interface has the correct information for your devices. ```text January 1, 1970 at 12:34:56 AM Z-Wave found failed module reported by controller "028 - Energy Meter Device" Z-Wave found failed module reported by controller "042 - Dehumidifier" ``` If you see a device listed that you believe should not be listed, it is often enough to simply browse to that device in the Indigo UI, open the Edit Device dialog, and perform a **Define and Sync** operation. Indigo will attempt to refresh the device's information and, if successful, tell the Z-Wave interface to remove the device from the failed devices list. After performing this operation, you can confirm the device has been removed from the list by rerunning the **Report Failed Modules to Event Log** step. If the interface doesn't think that any devices are in a failed state, running the report will result in ```text Z-Wave no failed modules found in controller ``` ### Remove Failed Device from Controller If a Z-Wave device has failed or is no longer available to your network, you can use this menu item to remove it from the Z-Wave interface. You may have to attempt to resync the device in order for it to be listed as failed. - If an Indigo device object still exists, you can open the Edit Device dialog and attempt to **Define and Sync** the device. If it has truly failed, the sync operation will fail, and the interface will add it to its internal failed list. - If an Indigo device object is no longer available, you can simply create a new device, attempt to sync the failed device and, when the operation has failed, and the interface will add it to its internal failed list. If you select this menu item and the Z-Wave interface doesn't have any failed devices in its list, Indigo will return this message: ```text Z-Wave no failed modules found in controller ``` ### Reset Z-Wave Interface You can completely reset your Z-Wave interface by selecting this menu item. **You should only do this if instructed by Support**. --- *Z-Wave® is a registered trademark of Sigma Designs, Inc. Indigo's support of Z-Wave hardware is neither endorsed nor certified by Sigma Designs.* --- Z-Wave Technology Overview (https://docs.indigodomo.com/2025.2/user/interfaces/z-wave/about/) --- # Z-Wave® Terminology and Technical Overview Z-Wave is a wireless (RF) home automation technology that's available worldwide. There are several characteristics of Z-Wave technology that we think are important for users to understand. In this article, we'll attempt to explain these features in terms that our users will find easily understood. This page is about the Z-Wave technology in general terms: if you're ready to get started using Z-Wave with Indigo, check out the [Managing Your Z-Wave Network](index.md) document. ## Overview Z-Wave is a wireless mesh network technology. That is, all signals are transmitted over RF (for those with power line-based systems, no more signal noise problems from stuff plugged into the wall). Z-Wave is a proprietary technology, owned by Silicon Labs, and licensed to a variety of vendors. The [Z-Wave Alliance](https://z-wavealliance.org) was formed by various vendors to help assure interoperability between devices. Z-Wave is a mesh network - where each node knows about the ones around it so that a message can be sent through various devices on the network until it reaches its destination. This increases network and message reliability. Z-Wave devices operate on different frequencies, so you'll find devices specific for North America (908MHz), Europe (868MHz) and Australia/New Zealand (921MHz). Many vendors supply devices for each so finding devices in your area shouldn't be a problem. ## Controllers If you are an existing Indigo user, specifically one who uses INSTEON, you may recognize the term "controller" to mean a device which can control other devices: KeypadLinc, RemoteLinc, SwitchLincs, etc. You can link a controller directly to a "responder" such that when a button is pressed on a keypad or switch, a command is sent directly from it to the linked "responder" device. Z-Wave has a similar mechanism ([Associations](#associations)) which we'll discuss in a bit. However, Z-Wave uses the word "controller" differently. ### Primary Controller The Z-Wave primary controller is responsible for assigning network id and node ids to devices (Z-Wave devices are referred to as a "node") and to create Secondary Controllers. This controller is the one that keeps the definitive list of nodes on the network. There must always be a primary controller in any Z-Wave network. For Indigo controlled Z-Wave networks, the primary controller will be the Z-Stick - it will create the network id (or home id) and will assign node id's to any device that's added to the network (see [Including/Excluding a Device](#including-excluding) below for more details on how to add devices to the network). ### Controller Types Controllers are generally one of two types. Portable controllers are handheld controllers, like remote controls, which can move around your house. Because these controllers can be at any place in the house, they must constantly be asking for nodes around it so they can maintain the routing information. Static controllers are controllers that don't move around so don't necessarily need to update routing information very often. The Z-Stick is primarily a static controller - however, since it can be moved around to include other devices it may need routing updates more frequently. Other types of static controllers are some scene controllers (synonymous to the INSTEON KeypadLinc), some switches, etc. ## Devices There are Z-Wave devices of all types you'd expect with any mature home automation technology: plug-in modules (aka wall-warts), switches, outlets, thermostats, motion sensors, etc. Z-Wave also supports locks from a variety of vendors. ### Including/Excluding a Device in a Z-Wave Network { #including-excluding } When you add a device to a network via the controller, it's called "including" the device (i.e. the inclusion process). Each device may have a different inclusion process. Likewise, you must exclude a device from the network if you are no longer using it. You can usually [include and exclude devices directly from Indigo](index.md). Older versions require you to take your controller to the device to include/exclude it using the button on the controller. Devices must be awake to be included into the Z-Wave controller (the procedure for each device that supports sleep is different so you'll need to refer to the docs for your device). Once included, Indigo may attempt to queue up messages to the device and wait for it to wake up. That behavior may or may not work based on the capability of the device, so if you need to communicate with a sleeping device (for instance to update any configuration parameters) you may need to manually wake it up. ## Associations Z-Wave associations are used when one module needs to command one or more other modules. For example, an association with a switch module could be created to control a remote lamp module when the switch is turned ON and OFF. Associations are also used between a module and the Z-Stick used by Indigo. In that case, the association is often used so that Indigo can update its UI when a module changes states, or so that user defined Triggers can be executed when a button is pressed. ## Routing and Network Healing Z-Wave uses a routed mesh network for extending the range between all modules. For reliable communication to occur between distant modules, an established route, or path, needs to be created. Indigo tells the Z-Stick to create these routes when a module is Defined or Synced. If you move modules to different locations it may be necessary to re-Sync them so that their new routes can be established. If you are unable to communicate with a distant module, then try re-Syncing modules that are nearby the distant module to help establish a new route. ## Glossary of Terms | Term | Definition | |---|---| | **Association** | A pre-defined grouping of Z-Wave devices that allows them to interact with each other. | | **Command Class** | A standard set of instructions that defines how a Z-Wave device can communicate and perform specific actions. | | **Controller** | A hardware device that acts as the primary hub and manages the Z-Wave network, including routing and security. | | **Encryption** | Devices that support encryption use a secure transmission protocol to relay traffic. Not all devices support encryption, and it is recommended that encryption should only be used in circumstances where security is important like door locks. | | **Exclusion** | Exclusion mode is a controller state that allows devices to be removed from a Z-Wave network. | | **Hop Limit** | In a Z-Wave network, the hop limit is four hops. This means that a signal can travel through a maximum of four intermediate Z-Wave devices (routers) to reach its destination. The maximum range with four hops is roughly 600 feet (or 200 meters). | | **Hub** | A hardware device that typically contains both communication circuits and a software application to manage a Z-Wave network. Indigo, along with a Z-Wave hardware controller, acts as the hub in a Z-Wave network. | | **Inclusion** | Inclusion mode is a controller state that allows devices to be added to a Z-Wave network. | | **Interface** | The software component used to communicate between Indigo and the Z-Wave Network. | | **Mesh Network** | A network where devices communicate with each other directly or through intermediate nodes, allowing for a wider range and better signal reliability. | | **Node** | A single device in a Z-Wave network. | | **NodeID** | A unique identifier for each Z-Wave node within the network. | | **Optimization** | Indigo supports a feature to optimize the Z-Wave network by iterating through all network devices to "refresh" their settings and routing tables. This is typically only required when significant changes are made to a network (for example, many devices are added/moved/removed from the network). | | **Parameter** | Z-Wave devices typically have parameters that dictate how the device behaves. For example, a dimmer may have a setting to control how quickly a light's intensity will change, or determine how frequently a battery-powered device sends data. | | **Primary Controller** | The main controller that manages the Z-Wave network. | | **Repeater** | A Z-Wave device whose purpose is simply to pass traffic along the network. Repeaters help extend the range of the network. | | **Secondary Controller** | A secondary controller can control Z-Wave devices, but it cannot add new devices to the network (only the primary controller can include or exclude devices). Secondary controllers are added or removed from the network by the primary controller. An example of a secondary controller would be a hand-held Z-Wave remote. | | **Raw Command** | Raw commands are how Indigo communicates with the Z-Wave network. Indigo supports sending custom raw commands via the Z-Wave interface. This is more of an advanced feature. | | **Sensor** | A device that detects and transmits information to the network, such as temperature or motion. | | **Z-Wave** | A wireless communications protocol that uses low-energy radio waves to connect smart devices in a mesh network. | ## Further Reading If you want even more detailed information about Z-Wave, here are some resources we suggest: - [Z-Wave on Wikipedia](https://en.wikipedia.org/wiki/Z-Wave) - [Z-Wave Alliance](https://z-wavealliance.org) --- --- Association Management (https://docs.indigodomo.com/2025.2/user/interfaces/z-wave/associations/) --- # Indigo Z-Wave® Association Management !!! abstract "In this guide" How to view and manage Z-Wave device associations using Indigo's Association Management dialog. Z-Wave associations allow devices to control each other directly (similar to Insteon links); this guide covers selecting a controlling device, choosing an association group, and adding or removing responding devices. Z-Wave has a couple of mechanisms that allow devices to control and respond to each other directly (similar to how [Insteon links](../insteon/index.md) work). Currently, Indigo supports the management of one of these mechanisms - Associations. Some Z-Wave devices can control associations - we'll call them association controllers. These devices may have multiple groups that they can control as well. All devices can respond to an association - we'll call these association responders. To edit associations, select `Interfaces->Z-Wave->Manage Associations...`. ![Z-Wave Associations Image](../../../images/zwave_associations.png) The `Controlling device` popup lists all Z-Wave devices in your system that support control associations. Once you select one of those (if you have any), then the `Controlling group` popup will show how many different association groups the device can control. The `Responding devices` list shows all Z-Wave devices that are being controlled by the selected device/group. Select any you want to delete and click the `Remove Selected Device` button to remove them from the association. Select a device from the `Device to add popup` and click `Add Responding Device` to add the device to the association. --- *Z-Wave® is a registered trademark of Sigma Designs, Inc. Indigo's support of Z-Wave hardware is neither endorsed nor certified by Sigma Designs.* --- Mac Client (https://docs.indigodomo.com/2025.2/user/mac-client/) --- # Indigo Mac Client !!! abstract "In this article" A tour of the Indigo Mac Client's Home Window: the Outline View, Item List, Item Detail panel, Status Bar, and key menus. Understanding this layout is the starting point for configuring all devices, triggers, schedules, and control pages. When you double-click on the `Indigo {{ version }}` icon in the `Applications` folder, you're actually starting the Indigo Mac Client, which will start up the Indigo Server process according to the options selected in the [Start Server](../getting-started/installation.md#starting-indigo-server) dialog. ## In This Section The Mac Client is where all configuration happens. This section tours it: - **[Home Window](home-window.md)** — the main window: the Outline View, Item List, Item Detail panel, and Status Bar. - **[Event Log Window](event-log.md)** — a live view of everything the Indigo Server is doing. - **[Menus](menus.md)** — a reference for every menu in the Mac Client. The Variable Window is covered in [Variables](../concepts/variables.md). --- *Z-Wave® is a registered trademark of Sigma Designs, Inc. Indigo's support of Z-Wave hardware is neither endorsed nor certified by Sigma Designs.* --- Event Log Window (https://docs.indigodomo.com/2025.2/user/mac-client/event-log/) --- # Event Log Window { #event-log-window } ![Event Log Window Image](../../images/event_log_window.png) The event log window shows you most everything the Indigo Server is doing at any given time. You see incoming and outgoing traffic, trigger and schedule executions, and other useful diagnostic information. - `Show Event Logs Folder` - this option opens the Logs folder in the Finder. Here you will find one file for each day's events. You can change how many day's worth of event log files are stored by selecting `Indigo {{ version }}->General Settings...` and then selecting the `General` tab. There, you can enter the number of days of event log files that you want to keep. Indigo Server will delete all files outside of that period of time. - `Clear Window` - this option clears the log entries from the Event Log window. --- Home Window (https://docs.indigodomo.com/2025.2/user/mac-client/home-window/) --- # Home Window The Home Window in the Mac client looks like this: ![Main Window Image](../../images/main_window.png) The Home Window has 4 areas: 1. Outline View 1. Item List 1. Item Detail 1. Status Bar The first 3 areas can be resized using the standard macOS split view handles (see the **red** arrows in the above image). ## Outline View The outline view shows all high-level objects (except [Variables](../concepts/variables.md#variables) which have their own window): [Overview](../concepts/devices.md#devices), [Overview](../concepts/triggers.md#triggers), [Overview](../concepts/schedules.md#schedules), [Overview](../concepts/actions.md#action-groups), and [Overview](../concepts/control-pages.md#control-pages). Selecting one of these in the outline view will switch the Item List to view all of those types of objects. The same goes for folders that are under the high-level headings except only the objects in those folders will show. ![Outline View Controls Image](../../images/outline_view_controls.png) To create a new folder, select the main object type in the list then click the plus (`+`) button lower-left corner of the outline view. A new folder will appear and you can just start typing its new name. You can select a folder then click on the gear icon in the lower-left (see the image above) or right-click the folder (bring up the contextual menu). The resulting menu will have the following options: - ` Rename Folder...` allows you to rename the folder. - `Delete Folder` will do just that. If the folder isn't empty, it will show you a sheet with 3 options: `Cancel` (do nothing), `Delete Items` (delete the items in the folder along with the folder), or `Move Items` (move the items to the parent first then delete the folder). - `Disable/Enable Remote Display` (for devices, action groups, and control pages) will show/hide it in remote clients (like [Indigo Touch for iOS and Web](http://www.indigodomo.com/touch.html), the [DomoPad 3rd party client for Android](https://forums.indigodomo.com/viewtopic.php?f=73&t=11536), etc). - `Copy ID (123456789)` will copy the unique folder ID for use in Python scripts. To move an object to a folder, just drag it from the list view onto the folder (or top-level object) - somewhat like moving mail messages from the inbox to a mail folder in the Mail application. ## Item List The Item List contains the items contained in the object that's selected in the Outline View - so if DEVICES is selected, then all devices in your system are shown. If you have one of the device folders selected, only the devices in that folder are shown. The buttons above the table view perform actions on items in the list - the buttons will work on whatever list is displayed in the Item List. So, if you have DEVICES (or a device folder) selected in the Outline View, the item list will show devices and the buttons will operate on that list - `New...` device, `Edit...` the selected device, `Duplicate` the selected device, or `Delete` the selected device(s). The search bar will filter the list. The image above shows all possible columns in the device view. However, if you right-click on the table header: ![Device Column Selection Menu Image](../../images/device_column_selection_menu.png) you can customize what columns are shown by checking/unchecking them in the list. Several of the columns have a checkbox in them - you can check/uncheck the feature for the object by clicking on the checkbox. In a few rare cases the checkbox will be disabled (grayed out). Every Item List is customizable in this regard. Also, if you right-click on a device: ![Device Contextual Menu Image](../../images/device_contextual_menu.png) This menu will: 1. Take you to the How-To wiki for that device type - if it's a plugin device, it will take you to the help page supplied by the plugin developer. 1. Toggle remote display - this tells Indigo whether to show the device in remote clients. 1. Toggle communication with the device - this effectively enables/disables the device. If a device is disabled, it will show up gray in the list. 1. Open a window that shows all other objects that are dependent on this device. See [Deletion Dependencies](../concepts/deletion-dependencies.md) for more information. 1. Copy the unique ID of the device to the clipboard - to assist in writing Python scripts that use the device 1. Copy a Python script string that will return an instance of the selected device. It will look like this: `indigo.devices[91776575] # "Living Room Switch"` 1. Print all the information about a device to the Event Log window. It will look something like this: ```text address : 3B.04.7A batteryLevel : None blueLevel : None brightness : 0 buttonConfiguredCount : 0 buttonGroupCount : 1 configured : True defaultBrightness : 100 description : valve deviceTypeId : displayStateId : brightnessLevel displayStateImageSel : DimmerOff displayStateValRaw : 0 displayStateValUi : 0 enabled : True energyAccumBaseTime : None energyAccumTimeDelta : None energyAccumTotal : None energyCurLevel : None errorState : folderId : 810233868 globalProps : MetaProps : (dict) com.indigodomo.indigoplugin.alexa : (dict) publish-device : true (bool) sub-type : Valve (string) voice-name : valve (string) greenLevel : None id : 1508839119 lastChanged : 2021-09-25 08:24:22 lastSuccessfulComm : 2021-09-25 08:24:22 ledStates : [] model : LampLinc (dual-band) name : Insteon Dimmer onBrightensToDefaultToggle : True onBrightensToLast : False onState : False ownerProps : emptyDict : (dict) pluginId : pluginProps : emptyDict : (dict) protocol : Insteon redLevel : None remoteDisplay : True sharedProps : com.indigodomo.indigoserver : (dict) states : States : (dict) brightnessLevel : 0 (integer) onOffState : off (on/off bool) subModel : Plug-In subType : Plug-In supportsAllLightsOnOff : True supportsAllOff : True supportsColor : False supportsRGB : False supportsRGBandWhiteSimultaneously : False supportsStatusRequest : True supportsTwoWhiteLevels : False supportsTwoWhiteLevelsSimultaneously : False supportsWhite : False supportsWhiteTemperature : False version : 67 whiteLevel : None whiteLevel2 : None whiteTemperature : None ``` Triggers and Schedules have a contextual menu when you right-click on them that will allow you to: 1. Enable/disable the event. 1. Hide executions in the event log - they trigger will continue to execute but nothing will be reported in the Event Log window. 1. Open a window that shows all other objects that are dependent on this trigger/schedule. See [Deletion Dependencies](../concepts/deletion-dependencies.md) for more information. 1. Copy the unique ID of the trigger or schedule to the clipboard - to assist in writing Python scripts 1. Copy a Python script string that will return an instance of the selected device. It will look something like this: `indigo.triggers[565290390] # "Motion Sensor Dawn Triggered"` Action Groups have a contextual menu when you right-click on them that will allow you to: 1. Toggle remote display - this tells Indigo whether to show the device in remote clients. 1. Open a window that shows all other objects that are dependent on this action group. See [Deletion Dependencies](../concepts/deletion-dependencies.md) for more information. 1. Copy the unique ID of the device to the clipboard - to assist in writing Python scripts that use the device 1. Copy a Python script string that will return an instance of the selected device. It will look something like this: `indigo.actionGroups[1703232002] # "Toggle Variable"` And finally, Control Pages also have a contextual menu: 1. Toggle remote display - this tells Indigo whether to show the device in remote clients. 1. Show in Browser - open the control page in the default browser. 1. Copy the unique ID of the device to the clipboard - to assist in writing Python scripts that use the device 1. Copy a Python script string that will return an instance of the selected device. It will look something like this: `indigo.controlPages[249813574] # "iPad Landscape"` ## Item Detail The item detail area shows controls and extra state information depending on what type of object is selected. ### Devices Device controls are separated into a variety of control tiles that are laid out in a grid in the Item Detail area. We also refer to this as the control area. If you have no device selected, the control area is blank. If you have a single device selected, you may see a variety of different control tiles. The first tile that will always be showing is the Device Details tile: ![Device Details Tile Image](../../images/device_details_tile.png) ![Device Details Tile Image](../../images/device_details_tile2.png) The information in the tile will be specific to the device type. For instance, battery powered devices (that report their battery status) will show the battery level. For sensor devices, the sensor reading will show (temp, humidity, etc. for instance). All devices, regardless of type, will show `Last Update` - the last date/time that the device was changed in any way. Plugin devices, regardless of whether they implement one of the built-in device types below or if they are completely custom devices, will also have a `Custom States` tile that will show all the device's custom states. This tile is always below the first row of tiles so you may need to scroll or resize the Item Detail area to see it: ![Custom States Tile Image](../../images/custom_states_tile.png) If you right-click a custom state, you will see the following contextual menu with several options: ![Device State Context Menu Image](../../images/device_state_context_menu.png) 1. Copy Python Reference will copy the full reference to that state that you can use in a Python script. It will look something like this: `indigo.devices[1604256801].states["album"] # State "album" of "MightMini iTunes"` 1. Copy State Value will copy the string value of the state to the clipboard so you can paste it somewhere else. 1. Copy State ID will copy the ID of the state for use in a Python script. 1. Copy Substitution String will copy the appropriately marked up string that you can use in a variety of dialogs to substitute the value of the state at runtime. It will look something like: `%%d:1604256801:album%%` Other device types will show specific control tiles based on their type as discussed below. #### Lights/Appliances For lights (dimmers) and appliance modules (relays or On/Off devices), you see `On/Off/Brightness Controls` tile: ![On Off Dimmer Tile Image](../../images/on_off_dimmer_tile.png) If the device doesn't support dimming, those controls will be hidden. You'll also notice that in the Device Details tile, `On State` and `Brightness` (if it's a dimmer) will show the current values. For lights that allow users to set the color, you'll see the `Color Controls` tile: ![Color Controls Tile Image](../../images/color_controls_tile.png) The top row of controls allow you to turn the light On, Off, and the last text field will allow you to type in the brightness (0-100). The second row of controls allows you to set the color and if the light supports it, the white value (also 0-100). If the light doesn't support setting white separately, the slider and text field will not show. The last row will allow you to se the temperature of the white (again, if the light supports it). If the light doesn't allow setting temperature, the `Send Status Request` button will show instead. #### Sensors (Motion Sensors, Energy Meters like the iMeter, etc.) ![Sensor Controls Tile Image](../../images/sensor_controls_tile.png) Sensor devices have their own control tile as well. Some of those devices can be explicitly turned On and Off (thus the buttons) and can respond to status requests. The buttons will be enabled/disabled based on the device's capabilities. One thing to note about some battery powered devices: the **Status Request''''** button may be enabled because some of those devices have the option to be plugged in (for instance, the Aeotec MultiSensor) and will respond to status requests when plugged in but not when running on battery power. Also, some energy monitoring devices, such as the iMeter, keep running totals of energy usage that can be reset by the user. For those, a `Reset` button will be placed next to the Total Usage in the `Device Details` tile. #### Speed Controls ![Speed Controls Image](../../images/speed_controls.png) Speed control devices are devices that can control the speed of some kind of motor. For instance, the Insteon FanLinc. And, in fact, the controls are currently tailored to the FanLinc's High, Medium, Low, and Off settings. When we run across more of these types of devices we'll customize the controls appropriately. #### Sprinklers ![Sprinkler Controls Image](../../images/sprinkler_controls.png) The sprinkler tile allows you to turn on a specific zone, go to the next or previous zone, update the valve status. And, if a schedule is currently running, pause/resume it, begin running the previous schedule, and stop all activity. #### Thermostats ![Thermostat Controls Image](../../images/thermostat_controls.png) The thermostat tile shows the current temperature, humidity, mode, cool/heat setpoints, and fan mode. You can also adjust all of those as well as refresh the values and turn everything off. Note that on rev2 thermostat adaptors the circles next to cool and heat setpoints will be lit if their respective HVAC system is actually running (A/C or heater). #### Generic Output Devices ![I/O Controls Image](../../images/io_controls.png) Output Controls tile shows the state of all the output states available for the device and you can update them immediately using the `Send Status Request` button. The button grid shows all available outputs and their status. Use the `Turn Off All Outputs` button to turn off all binary outputs. ### Triggers and Schedules ![Trigger Schedule Controls Image](../../images/trigger_schedule_controls.png) The detail area for triggers and schedules is identical - it shows the triggering event (either a state change or some time/date description), whether the condition is always, rules, or a script, and a summary of actions. Depending on your interface, a popup may allow you to specify how the object is processed: - `Enabled and Upload` - this means that it will be processed both while Indigo Server is running and it will be uploaded to your standalone controller (if it supports uploading). - `Enable` - this means that it will be processed while Indigo Server is running, and mirrors the Enabled column in the list if it's visible. - `Upload` - this means that it will only be uploaded and executed by your standalone controller (if it supports uploading) - while Indigo Server is controlling the logic it will not be enabled - `Disabled` - this means that it's completely disabled - it also mirrors the Enabled column in the list if it's available. The `Execute Conditional Actions` button will execute all the actions associated with the object, but only after any conditions specified in the Conditions tab are evaluated (so you can test the conditions). The `Execute Actions Only` button will do exactly that - execute all the actions associated with the object without evaluating any conditions specified in the Conditions tab. ### Action Groups ![Action Group Controls Image](../../images/action_group_controls.png) The action group detail shows a list of all the actions in the group. The `Execute Actions Now` button will do exactly that - execute all the actions associated with the action group. ### Control Pages The only thing in the detail area for a control page is the `Show in Browser` button, which will open the selected page in the default browser. ## Status Bar The status bar along the bottom of the window has several elements. First, there's the communication queue indicator. It's the longish rectangle next to the Indigo icon. This indicator will show green bars going from right to left, and purple bars going from left to right. Green bars indicate outgoing communication purple bars show incoming communications. If you see green bars begin to build up, you can tell that something is keeping the outgoing command queue from processing correctly. Next to that bar you'll see the names of the interfaces you have configured. If they are in green, then they are functioning properly. If they are in red with a line through them, Indigo can't communicate with them. If they are gray, they are offline for some reason. The next four sets are pretty self-explanatory: the next sunrise time, the next sunset time, the current time (for the Indigo Server, so it may be different than the client time if you're running the client on a different machine), and the next time a schedule is going to execute. --- Menus (https://docs.indigodomo.com/2025.2/user/mac-client/menus/) --- # Menus In this section, we'll go through each of the menus in Indigo. ## Indigo {{ version }} Menu ![Indigo Menu Image](../../images/indigo_menu_2023_1.png) - `About Indigo {{ version }}` - this will show the About window which will show what client and server versions you're running and will show your registration code - `Start Local Server...` - this will open the [Start Server dialog](../getting-started/installation.md#starting-indigo-server) - `Stop Server` - this will stop the Indigo Server - `Connect to Remote Server...` - this option will allow you to connect to a different Indigo server or a server running on another Mac - `Close Connection` - this will close the connection to the server - `General Settings...` - this opens the [preferences dialog](../getting-started/installation.md#general-configuration-settings) - `Advanced Web Server Settings...` - this opens the [Indigo Web Server Settings dialog](../remote-access/web-server.md#advanced-web-server-settings) - `License Details...` - this opens the Subscription Status window that will show you the status of your License and it's associated Up-to-Date Subscription: ![Subscription Status Image](../../images/subscription_status.png) - `Check for Updates...` - this will have the server your connected to contact us to see if there is an update available ## File Menu ![File Menu Image](../../images/file_menu.png) - `New Database...` - this will close the current database and create a new one - you'll be prompted for a name and save location - `Select Database...` - this will allow you to switch to a different database - `Close Window` - this will close the frontmost window - `New Device...` - this will switch the Home Window to the devices view and open the [new device dialog](../concepts/devices.md#devices) - `New Trigger...` - this will switch the Home Window to the triggers view and open the [new trigger dialog](../concepts/triggers.md#triggers) - `New Schedule...` - this will switch the Home Window to the schedules view and open the [new schedule dialog](../concepts/schedules.md#schedules) - `New Action Group...` - this will switch the Home Window to the action groups view and open the [new action group dialog](../concepts/actions.md#action-groups) ## Edit Menu The Edit menu has the standard options on it that you would expect any Mac app to have. Note that the Delete and Duplicate options are available for most items in the user interface. ## View Menu The View menu allows you to switch between views in the Home Window just as if you had clicked the major item types in the outline view: Devices, Triggers, Schedules, Action Groups and Control Pages. ## Interfaces Menu The [Interfaces Menu](../getting-started/interfaces.md#managing-the-built-in-interfaces) is discussed in the [Getting Started Guide](../getting-started/index.md). ## Plugins Menu The [Plugins Menu](../concepts/plugins.md#plugin-menus-in-indigo) is discussed in the [Getting Started Guide](../getting-started/index.md). ## Window Menu ![Window Menu Image](../../images/window_menu_2023_1.png) - `Home Window` - this will show/bring to front the Home Window - `Event Log` - this will show/bring to front the Event Log Window - `Variable List` - this will show/bring to the front the Variable List Window The bottom of the menu will display the list of open windows, where the name of the active database will replace "Home Window". ## Help Menu ![Help Menu Image](../../images/help_menu_2023_1.png) - `Help for XXXX` - this will change slightly based on what window is selected and what's selected in that window - but it will open the default browser to the page in the help documentation for that particular item - `Email Log...` - this option emails some of your event log data to the specified email address. Particularly useful for technical support - `Show AppleScript Usage in Event Log` - this option prints a list of AppleScripts that are embedded in actions that you have specified. This will help to identify and replace those scripts. - `Show Event Logs Folder` - this option will switch to the Finder and open the folder that contains the Event Log log files. - `Show Web Assets Folder` - this option switches to the Finder and open the folder that contains custom images, text files, etc., that you might want to give Indigo (and others) access to through the Indigo Web Server. - `Show Indigo Server Install Folder` - this option opens a Finder window that displays the folder of the currently running Indigo installation. This will only run from the server machine (you'll receive a warning otherwise). - `Indigo Documentation` - this option will open your browser to the landing page for all Indigo {{ version }} documentation - `Getting Started Guide` - this option opens your browser to the Getting Started Guide, the place where everyone new to Indigo should start - `Managing a Z-Wave Network` - this option opens a browser window to the [Managing Your Z-Wave Network](../interfaces/z-wave/index.md) document in our Documentation wiki - `Managing an Insteon Network` - this option opens a browser window to the [Managing Your Insteon Network](../interfaces/insteon/index.md) document in our Documentation wiki - `Online Support Forum` - this option opens your browser to our [support forum](https://forums.indigodomo.com) - it's very active and is the primary place where you should seek help - we answer questions there very quickly and in fact we have many helpful users that can help as well - `Compatible Devices` - this option opens a browser window on our new [Compatible Devices list](https://www.indigodomo.com/devices/) that shows which devices and interfaces have been tested with Indigo and includes devices supported by 3rd party plugins - `Plugin Store` - this option opens your browser to our [Plugin Store](https://www.indigodomo.com/pluginstore/) where you can browse and download hundreds of 3rd party plugins that add functionality to Indigo --- License Transfers (https://docs.indigodomo.com/2025.2/user/maintenance/license-transfer/) --- # Transferring Your Indigo License to Another User !!! abstract "License Transfers" This guide describes the necessary steps for transferring your Indigo license to another user. In some cases, you may wish to transfer your license to another user--for example, if you sell your home along with the Indigo system. We are happy to assist you in this process, and there are only a few steps you need to take. It is important that the steps be taken **in this order**: 1. Have the new owner create a new Indigo Account and ask them to let you know the username/email on that account (don't need their password). 1. In your Indigo Account, cancel your subscription by clicking on the **Manage Subscription** link next to your license. Note that we can't transfer subscriptions, only licenses. Any time remaining on the subscription will be honored for the new user, but they need to renew, and catch-up the subscription when it expires. 1. Send us an email with the username/email for the new user's account (along with which license you want to transfer if you have multiple - use the activation name to identify it) and we will transfer the license. 1. When we're done with that, the new owner will need to deactivate/reactivate the license by selecting the **Indigo 20XX.Y** -> **License Details...** menu item and first clicking on the **Deactivate License** button then--in the resulting dialog--log in using the username/password they created in the step 1 above. --- Moving to Another Mac (https://docs.indigodomo.com/2025.2/user/maintenance/moving/) --- # Moving Indigo to Another Computer !!! abstract "In this guide" This guide describes how to move your Indigo installation to another computer, another computer with a different processor architecture, or restoring from a backup. ## Moving Indigo to another Mac or Restoring From a Backup Note this process can also be followed to recover from a backup, though if your system crashed then you may need to contact us directly to perform step #1 for you. 1. Deactivate Indigo on your Mac by selecting the `Indigo {{ version }}->License Details...` menu item and clicking the `Deactivate License` button. Do this while the Indigo Server is still running (do not shutdown the Indigo Server first) 1. Copy this folder over to the new Mac: `/Library/Application Support/Perceptive Automation/Indigo {{ version }}/` (if restoring from a backup, you'll need to locate this folder in whatever backup system you use) 1. Install Indigo {{ version }} on the new Mac ([download](https://www.indigodomo.com/downloads.html) and run the installer - you must be logged in to your Indigo Account to see all the installers available to you) !!! warning "NOTE" this is not the Library folder that's in your home directory - it's the one at the top level of your hard drive. The easiest way to get there is to select the `Go->Go To Folder...` menu item in the Finder and paste in this: `/Library/Application Support/` It'll open the correct folder and you'll see the "Perceptive Automation" folder - that's the one you want to copy over. That should get all of your customizations and allow the installer to update anything as necessary. You may also need to install the drivers for the technology you use - see the appropriate section below for details. If you're moving Indigo to a Mac with a different architecture, read on... ## Moving an Indigo Installation to another Architecture When you move your Indigo installation from one macOS architecture to another (for example from an Intel-based Mac to an M-series Mac) -- especially if you use Migration Assistant or Time Machine -- you will likely need to take additional steps because many Python libraries are compiled to the specific hardware they're installed on. One signal that this has become an issue is an error message like: ```text '/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/httptools/parser/parser.cpython-310-darwin.so' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64')) ``` If you see errors like that (*incompatible architecture*), can't get the login or license dialog to work, or you have other errors that keep Indigo from starting up, you may need to take a few extra steps: 1. manually remove the `/Library/Frameworks/Python.framework/` directory, and 1. run the Indigo installer again to reinstall Python. Taking these additional steps should ensure that your Python installation is compiled for your new Mac and that all file permissions are properly set. --- Uninstalling Indigo (https://docs.indigodomo.com/2025.2/user/maintenance/uninstalling/) --- # Uninstalling Indigo !!! abstract "In this guide" This guide shows the steps to follow when uninstalling Indigo from your Mac, including the server application, the client application and the folder structure (including details that apply to a specific version of Indigo. ## Uninstalling the Server To uninstall the Indigo Server and all of its data and configuration files, first deactivate Indigo on your Mac by selecting the `Indigo {{ version }}->License Details...` menu item and clicking the `Deactivate License` button. You'll get the following warning dialog: ![Deactivate Sheet Image](../../images/deactivate_sheet.png) Then make sure that you have the server and client completely shut down (select `Stop Server` from the `Indigo {{ version }}` menu if the server is still running before you quit the Indigo Mac Client). Then, delete the following files: - `/Library/Application Support/Perceptive Automation/Indigo {{ version }}/` - `~(your user folder)/Library/LaunchAgents/com.perceptiveautomation.IndigoServer2.plist` - *Optional* : If you don't use Python 3.11 for anything (other than Indigo {{ version }}), you can delete this folder: `/Library/Frameworks/Python.framework/Versions/3.11` and this file: `/Library/Frameworks/Python.framework/Versions/Current` Note: this will leave any other version of Python alone including any previous versions that older Indigo installs may have installed (if you plan on reverting to an older version). To completely remove all Python installations that any version of Indigo may have installed, just delete the entire Python framework: `/Library/Frameworks/Python.framework` ## Uninstalling the Mac Client To also uninstall the Indigo Mac Client delete the following files: - `/Applications/Indigo {{ version }}.app` - `~(your user folder)/Library/Preferences/com.perceptiveautomation.indigo-2025-1.plist` - `~(your user folder)/Library/Preferences/Indigo {{ version }} Client Prefs.indiPref` - `~(your user folder)/Library/Preferences/Indigo {{ version }} Client Settings.indiPref` Note your Mac has two different Library folders. One is in your the home directory of the account Indigo runs under and the other is at the root level of the drive. In Mac OS X Lion and higher the user's home Library folder is hidden in the Finder so you'll have to use the `Go to Folder...` menu item in the Finder and type in the path (`~/Library/Preferences`) which will open a Finder window to the Preferences folder. If you remove the Mac client application before shutting down the server, then delete the rest of the files above and reboot. ## Uninstalling Older Indigo Versions To uninstall older versions but leave {{ version }} installed correctly, delete these: - Indigo 2025.1: - `/Library/Application Support/Perceptive Automation/Indigo 2025.1/` - `/Applications/Indigo 2025.1.app` - Indigo 2024.2: - `/Library/Application Support/Perceptive Automation/Indigo 2024.2/` - `/Applications/Indigo 2024.2.app` - Indigo 2024.1: - `/Library/Application Support/Perceptive Automation/Indigo 2024.1/` - `/Applications/Indigo 2024.1.app` - Indigo 2023.2: - `/Library/Application Support/Perceptive Automation/Indigo 2023.2/` - `/Applications/Indigo 2023.2.app` - Indigo 2023.1: - `/Library/Application Support/Perceptive Automation/Indigo 2023.1/` - `/Applications/Indigo 2023.1.app` - Indigo 2022.2: - `/Library/Application Support/Perceptive Automation/Indigo 2022.2/` - `/Applications/Indigo 2022.2.app` - Indigo 2022.1: - `/Library/Application Support/Perceptive Automation/Indigo 2022.1/` - `/Applications/Indigo 2022.1.app` - Indigo 2021.2: - `/Library/Application Support/Perceptive Automation/Indigo 2021.2/` - `/Applications/Indigo 2021.2.app` - Indigo 2021.1: - `/Library/Application Support/Perceptive Automation/Indigo 2021.1/` - `/Applications/Indigo 2021.1.app` - Indigo 7.5: - `/Library/Application Support/Perceptive Automation/Indigo 7.5/` - `/Applications/Indigo 7.5.app` - Indigo 7.4: - `/Library/Application Support/Perceptive Automation/Indigo 7.4/` - `/Applications/Indigo 7.4.app` - Indigo 7.3: - `/Library/Application Support/Perceptive Automation/Indigo 7.3/` - `/Applications/Indigo 7.3.app` - Indigo 7.2: - `/Library/Application Support/Perceptive Automation/Indigo 7.2/` - `/Applications/Indigo 7.2.app` - Indigo 7.0 and 7.1: - `/Library/Application Support/Perceptive Automation/Indigo 7/` - `/Applications/Indigo 7.app` - Indigo 6 and prior (substitute the previous version # for 6): - `/Library/Application Support/Perceptive Automation/Indigo 6/` - `/Applications/Indigo 6.app` Note that these (specifically the first item in each) serve as a backup in case you want to revert, so you might want to consider zipping up the folder (not the client app) and saving it off before deleting if you think you might want to revert for some reason. --- Upgrading Indigo (https://docs.indigodomo.com/2025.2/user/maintenance/upgrading/) --- # Upgrading Indigo !!! abstract "In this guide" This guide provides detailed instructions on how to upgrade your Indigo software, including version-specific changes. ## Upgrading from a Previous Version The upgrade process is pretty straight-forward, but below you'll find the specifics for each older version. ## Upgrading from Indigo 2021.1 or later ### Server Folder Change For this release, we've changed the server install path to: /Library/Application Support/Perceptive Automation/Indigo {{ version }}/ and the Indigo Mac Client app is now named: /Applications/Indigo {{ version }}.app ### Web Assets Folder Change The installer moves customer-installed images and other files from these folders: /Library/Application Support/Perceptive Automation/Indigo 202x.y/Web Assets/ (depending on version of Indigo you were using) to: /Library/Application Support/Perceptive Automation/Indigo {{ version }}/Web Assets/ ### Shared Python Modules Folder Change Lastly, the [shared Python modules folder](../../scripting/tutorial.md#shared-classes-and-methods-in-python-files-python-modules) available during script execution is located here: /Library/Application Support/Perceptive Automation/Python3-includes If you have any existing Python modules/libraries in the old `Python2-includes` folder you should copy them to the new folder and **make sure** they are [Python 3 compatible](#python-script-changes-and-plugin-compatibility). ### Troubleshooting Not Compatible Error If you experience the error `This version of the Indigo client application is not compatible with the Indigo Server` when trying to launch Indigo then that means the Indigo client version being launched doesn't match the version of Indigo Server currently being run. This can occur when an older version of Indigo Server is still running or an older version of Indigo Client was launched by mistake. To fix this follow these steps: 1. Shut down the Indigo Server by selecting the `Indigo 202x.y->Stop Server` menu item in the Mac client. This will shutdown the Indigo Server regardless of which version is running. 1. Quit the Mac client via the `Indigo 202x.y->Quit Indigo` menu item. 1. Open the `/Applications` folder on your Mac and launch the version of Indigo you wish to use. This will have it launch the correct version of the Indigo Server when it starts. Once you are sure you are ready to run the latest version of Indigo can delete (backup first!) the older Indigo Server install paths in `/Library/Application Support/Perceptive Automation/` and client applications in `/Applications`. ## Upgrading from Legacy Versions of Indigo !!! abstract "In this guide" Step-by-step instructions for upgrading from Indigo 6.x or 7.x to Indigo {{ version }}, including installer path changes, plugin migration, and database conversion steps. Review this before upgrading — particularly if you have scripts that may need updating for Python 3 compatibility. ### Upgrading from Indigo 7.5 For this release, we've changed the server install path to: /Library/Application Support/Perceptive Automation/Indigo {{ version }}/ and the Indigo Mac Client app is now named: /Applications/Indigo {{ version }}.app In addition to the automatic upgrade logic described below (for upgrades from Indigo 6 and earlier), the installer moves previously installed control page images and web server plugins to the new install location. Note the folder that contains Web server images, legacy Web plugins, etc. has been renamed from: /Library/Application Support/Perceptive Automation/Indigo 7.5/IndigoWebServer/ to: /Library/Application Support/Perceptive Automation/Indigo {{ version }}/Web Assets/ If you experience the error `This version of the Indigo client application is not compatible with the Indigo Server` when trying to launch Indigo then that means the Indigo client version being launched doesn't match the version of Indigo Server currently being run. This can occur when an older version of Indigo Server is still running or an older version of Indigo Client was launched by mistake. To fix this follow these steps: 1. Shut down the Indigo Server by selecting the `Indigo 7.5->Stop Server` menu item in the Mac client. This will shut down the Indigo Server regardless of which version is running. 1. Quit the Mac client via the `Indigo 7.5->Quit Indigo` menu item. 1. Open the `/Applications` folder on your Mac and launch the version of Indigo you wish to use. This will have it launch the correct version of the Indigo Server when it starts. Once you are sure you are ready to run the latest version of Indigo can delete (backup first!) the older Indigo Server install paths in `/Library/Application Support/Perceptive Automation/` and client applications in `/Applications`. ### Upgrading from Indigo 7.4 For this release, we've changed the server install path to: /Library/Application Support/Perceptive Automation/Indigo {{ version }}/ and the Indigo Mac Client app is now named: /Applications/Indigo {{ version }}.app In addition to the automatic upgrade logic described below (for upgrades from Indigo 6 and earlier), the installer moves previously installed control page images and web server plugins to the new install location. Note the folder that contains Web server images, legacy Web plugins, etc. has been renamed from: /Library/Application Support/Perceptive Automation/Indigo 7.4/IndigoWebServer/ to: /Library/Application Support/Perceptive Automation/Indigo {{ version }}/Web Assets/ If you experience the error `This version of the Indigo client application is not compatible with the Indigo Server` when trying to launch Indigo then that means the Indigo client version being launched doesn't match the version of Indigo Server currently being run. This can occur when an older version of Indigo Server is still running or an older version of Indigo Client was launched by mistake. To fix this follow these steps: 1. Shut down the Indigo Server by selecting the `Indigo 7.4->Stop Server` menu item in the Mac client. This will shut down the Indigo Server regardless of which version is running. 1. Quit the Mac client via the `Indigo 7.4->Quit Indigo` menu item. 1. Open the `/Applications` folder on your Mac and launch the version of Indigo you wish to use. This will have it launch the correct version of the Indigo Server when it starts. Once you are sure you are ready to run the latest version of Indigo can delete (backup first!) the older Indigo Server install paths in `/Library/Application Support/Perceptive Automation/` and client applications in `/Applications`. ### Upgrading from Indigo 7.3 For this release, we've changed the server install path to: /Library/Application Support/Perceptive Automation/Indigo {{ version }}/ and the Indigo Mac Client app is now named: /Applications/Indigo {{ version }}.app In addition to the automatic upgrade logic described below (for upgrades from Indigo 6 and earlier), the installer moves previously installed control page images and web server plugins to the new install location. Note the folder that contains Web server images, legacy Web plugins, etc. has been renamed from: /Library/Application Support/Perceptive Automation/Indigo 7.3/IndigoWebServer/ to: /Library/Application Support/Perceptive Automation/Indigo {{ version }}/Web Assets/ If you experience the error `This version of the Indigo client application is not compatible with the Indigo Server` when trying to launch Indigo then that means the Indigo client version being launched doesn't match the version of Indigo Server currently being run. This can occur when an older version of Indigo Server is still running or an older version of Indigo Client was launched by mistake. To fix this follow these steps: 1. Shut down the Indigo Server by selecting the `Indigo 7.3->Stop Server` menu item in the Mac client. This will shut down the Indigo Server regardless of which version is running. 1. Quit the Mac client via the `Indigo 7.3->Quit Indigo` menu item. 1. Open the `/Applications` folder on your Mac and launch the version of Indigo you wish to use. This will have it launch the correct version of the Indigo Server when it starts. Once you are sure you are ready to run the latest version of Indigo can delete (backup first!) the older Indigo Server install paths in `/Library/Application Support/Perceptive Automation/` and client applications in `/Applications`. ### Upgrading from Indigo 7.2 For this release, we've changed the server install path to: /Library/Application Support/Perceptive Automation/Indigo {{ version }}/ and the Indigo Mac Client app is now named: /Applications/Indigo {{ version }}.app In addition to the automatic upgrade logic described below (for upgrades from Indigo 6 and earlier), the installer moves previously installed control page images and web server plugins to the new install location. Note the folder that contains Web server images, legacy Web plugins, etc. has been renamed from: /Library/Application Support/Perceptive Automation/Indigo 7.2/IndigoWebServer/ to: /Library/Application Support/Perceptive Automation/Indigo {{ version }}/Web Assets/ If you experience the error `This version of the Indigo client application is not compatible with the Indigo Server` when trying to launch Indigo then that means the Indigo client version being launched doesn't match the version of Indigo Server currently being run. This can occur when an older version of Indigo Server is still running or an older version of Indigo Client was launched by mistake. To fix this follow these steps: 1. Shut down the Indigo Server by selecting the `Indigo 7.2->Stop Server` menu item in the Mac client. This will shut down the Indigo Server regardless of which version is running. 1. Quit the Mac client via the `Indigo 7.2->Quit Indigo` menu item. 1. Open the `/Applications` folder on your Mac and launch the version of Indigo you wish to use. This will have it launch the correct version of the Indigo Server when it starts. Once you are sure you are ready to run the latest version of Indigo can delete (backup first!) the older Indigo Server install paths in `/Library/Application Support/Perceptive Automation/` and client applications in `/Applications`. ### Upgrading from Indigo 7, or 7.1 For this release, we've changed the server install path to: /Library/Application Support/Perceptive Automation/Indigo {{ version }}/ and the Indigo Mac Client app is now named: /Applications/Indigo {{ version }}.app In addition to the automatic upgrade logic described below (for upgrades from Indigo 6 and earlier), the installer is now smarter and moves previously installed control page images and web server plugins to the new install location. Note the folder that contains Web server images, legacy Web plugins, etc. has been renamed from: /Library/Application Support/Perceptive Automation/Indigo 7/IndigoWebServer/ to: /Library/Application Support/Perceptive Automation/Indigo {{ version }}/Web Assets/ If you experience the error `This version of the Indigo client application is not compatible with the Indigo Server` when trying to launch Indigo then that means the Indigo client version being launched doesn't match the version of Indigo Server currently being run. This can occur when an older version of Indigo Server is still running or an older version of Indigo Client was launched by mistake. To fix this follow these steps: 1. Shut down the Indigo Server by selecting the `Indigo 7->Stop Server` menu item in the Mac client. This will shut down the Indigo Server regardless of which version is running. 1. Quit the Mac client via the `Indigo 7->Quit Indigo` menu item. 1. Open the `/Applications` folder on your Mac and launch the version of Indigo you wish to use. This will have it launch the correct version of the Indigo Server when it starts. Once you are sure you are ready to run the latest version of Indigo can delete (backup first!) the older Indigo Server install paths in `/Library/Application Support/Perceptive Automation/` and client applications in `/Applications`. ### Upgrading from Indigo 6, 5, 4, 3, or 2 First, run the Indigo {{ version }} installer. Then look through this list for any final actions. - We have moved to Python v3.x, so if you installed any shared scripts ([as outlined here](../../scripting/tutorial.md#shared-classes-and-methods-in-python-files-python-modules)) then you'll need to move them from: `/Library/Python/X.X/site-packages/` to `/Library/Python/3.11/site-packages/` - Indigo will automatically copy your old preference file into the new location during the installation. - Indigo will also copy over any enabled and disabled Plugins from Indigo 6 or 5. If a newer version of a plugin is included in Indigo, then it will automatically be installed and used on launch. - Your old Indigo database file will automatically be converted to the new Indigo file format on first launch. A copy of your database will automatically be made before it is converted, but any changes made in Indigo to your database will not be available in older versions of Indigo. - If you are **upgrading from Indigo 6**, you'll need to manually copy over any custom scripts, web server plugins, and/or custom control page images that you may have added. They can be found in the following directories: - **Scripts**: `/Library/Application Support/Perceptive Automation/Indigo 6/Scripts/` - **Control Page Images**: `/Library/Application Support/Perceptive Automation/Indigo 6/IndigoWebServer/images/` - **Web Server Plugins**: `/Library/Application Support/Perceptive Automation/Indigo 6/IndigoWebServer/plugins/` - If you are **upgrading from Indigo 5**, you'll need to manually copy over any custom scripts, web server plugins, and/or custom control page images that you may have added. They can be found in the following directories: - **Scripts**: `/Library/Application Support/Perceptive Automation/Indigo 5/Scripts/` - **Control Page Images**: `/Library/Application Support/Perceptive Automation/Indigo 5/IndigoWebServer/images/` - **Web Server Plugins**: `/Library/Application Support/Perceptive Automation/Indigo 5/IndigoWebServer/plugins/` - If you are **upgrading from Indigo 4**, you'll need to manually copy over any custom scripts, web server plugins, and/or custom control page images that you may have added. They can be found in the following directories: - **Scripts**: `/Library/Application Support/Perceptive Automation/Indigo 4/Scripts/` - **Control Page Images**: `/Library/Application Support/Perceptive Automation/Indigo 4/IndigoWebServer/images/` - **Web Server Plugins**: `/Library/Application Support/Perceptive Automation/Indigo 4/IndigoWebServer/plugins/` - If you are **upgrading from Indigo 3 or Indigo 2**, you'll need to manually copy over any custom scripts and/or custom control page images that you may have added. They can be found in the following directories: - **Scripts**: `/Library/Application Support/Perceptive Automation/Indigo 2/Scripts/` - **Control Page Images**: `/Library/Application Support/Perceptive Automation/Indigo 2/IndigoWebServer/images/` !!! warning "NOTE" Do not replace the new Indigo versions of any file with your Indigo 6, 5, 4, 3, or 2 files. They have been modified to run optimally under the new version of Indigo. ### Upgrading from Indigo 1.x - Indigo will automatically copy your old preference file into the new location on first launch. - Your old Indigo database file will automatically be converted to the new Indigo file format on first launch. You will be prompted to save the new copy of the Indigo database file. The new Indigo file format is not compatible with Indigo 1.x. You should not replace or delete your older Indigo 1.x database file. Any changes made in Indigo to your database settings will not be available in older versions of Indigo. - You will need to manually copy any of your custom script files. You only need to move the files from the Indigo 1.x location (`[~your user home folder]/Documents/Indigo User Data/Scripts/`) to the new folder specified above if you modified or added new script or script attachment files. !!! warning "NOTE" Do not replace the new Indigo versions of any file with your Indigo 1.x files. They have been modified to run optimally under the new version of Indigo. ## AppleScript after Upgrading Indigo no longer supports AppleScripts that target the Indigo Server process. Check out the [AppleScript Integration Strategies](https://www.indigodomo.com/indigo/applescript.html) article for options on converting your AppleScripts. When Indigo first opens an Indigo 7.3 (or earlier) database, it will go through the database and identify items that will need changing and items you should look at to ensure that it will continue to function. The Event Log will contain the necessary information about where you can find those items. You can also select the `Help->Show AppleScript Usage in Event Log` menu item and Indigo will show you the list of those items in the Event Log window again. Embedded AppleScripts and AppleScript conditionals will need to be evaluated to best determine how to handle them. In those edit boxes in the UI, we've commented out the previous AppleScript for reference if you wish to convert them. Both of those script edit boxes now will only accept Python scripts. If you have [Execute Script](../concepts/actions.md#execute-script) actions that point to AppleScripts, those will continue to be run, though if the script contains a **tell "IndigoServer"** or **using terms from "IndigoServer"** they will fail. ## Python Script Changes and Plugin Compatibility All Indigo embedded and external Python scripts run in Python 3. If any of them aren't working, they may have been written for an older version of Python. Take a look at our [updating to Python 3 tips](https://github.com/IndigoDomotics/IndigoSDK/blob/main/Updating%20to%20API%20version%203.0%20(Python%203).md) document. You can also post your scripts on the [Help Converting to Python 3 forum](https://forums.indigodomo.com/viewforum.php?f=364) to get help with any conversion issues. --- Indigo Reflectors (https://docs.indigodomo.com/2025.2/user/remote-access/reflector/) --- # The Indigo Reflector Service !!! abstract "In this guide" How to activate and use the Indigo Reflector Service for secure remote access to your Indigo server — no router port forwarding, static IP, or DynDNS account required. Covers reflector activation, the personalized remote URL, and how Indigo Touch automatically switches between local Bonjour and remote reflector access. ## About Reflectors [Indigo Reflectors](http://www.indigodomo.com/account/reflectors/) are a service that gives you secure (HTTPS) remote access from anywhere to your Indigo Server **with no network configuration needed.** This is particularly useful for those of us that have dynamic (vs. static) IP addresses at home, or that have routers or networks that are difficult to configure for remote access to your Mac running Indigo. ![Reflector Flow Image](../../images/reflector_flow_image.png) Reflectors work by routing communication from [Indigo Touch for iOS](https://www.indigodomo.com/touch.html), [Domotics Pad for Android](https://play.google.com/store/apps/details?id=com.duncanware.domoPad), [Indigo Touch for Web browsers](https://www.indigodomo.com/touch.html), and from external services like [Amazon Alexa](../../plugins/alexa/index.md), through our hosted systems to your Indigo Server. We do this through a secure tunnel between our hosted systems and your Indigo Server (even we don't see the unencrypted traffic). Your purchase of Indigo includes an [Indigo Up-to-Date subscription](http://www.indigodomo.com/blog/2016/11/09/indigo-date/), which itself includes reflector access. If you have an active Up-to-Date subscription, you can have a reflector! Reflectors provide: - Personalized easy-to-remember URL when using [Indigo Touch](http://www.indigodomo.com/touch.html) for the Web - [Indigo Touch](http://www.indigodomo.com/touch.html) for iOS (iPhone, iPad, etc.) and for the Web can access your Indigo Server from anywhere in the world - [Indigo Touch](http://www.indigodomo.com/touch.html) for iOS enabled – automatic switching between local Bonjour access and remote reflector access - 3rd party services like Amazon Alexa can get secure, authenticated access to your Indigo Server to open up a broad range of integration possibilities. - Complete remote access solution (much more than just a dynamic IP address mapping service) - Fully encrypted (256-bit) communication - Hassle free setup: - No firewall configuration needed - No router port forwarding needed - No static IP address or DynDNS account needed - No non-standard port numbers to remember - No reverse proxies to implement SSL/HTTPS ## Activating your Reflector Activating a reflector is fast and easy. First, make sure you are running Indigo version 7.0.0 or greater(both client and server -- check the About Box). Next: 1. Choose the `Indigo->Start Local Server...` menu item. 1. Turn on the `Allow remote access` checkbox and enter a Username and good Password. You will *definitely* want password authentication on since you are about to enable access to the Indigo web server from the Internet. 1. Turn on the `Enable secure internet access via Indigo Reflector` checkbox. 1. Press the `Activate Reflector` button. A browser page will open to the http://www.indigodomo.com/account/codes/ page listing your registration codes. - If you already have an Indigo Account then log in (if necessary). You'll then be redirected to the registration codes page. - If you don't have an Indigo Account then create one by clicking on the `Sign up` link below the login form. When you create an account, we'll send you an activation email that contains a link to activate your account. When you click that link, it will switch you back to your browser and open the reflectors page. If for some reason it doesn't, switch back to Indigo and click the `Activate Reflector` button again and it will. Once on the registration codes list page, you'll see a message to click the **Create Reflector** link next to your Indigo registration code. Click on that link and you'll switch to the Reflectors page with a form field to enter your reflector name. When you submit that we will create the reflector, activate it, and take you back to the codes page which will show your reflector activated. Lastly, switch back to the Indigo `Start Local Server...` dialog. It should now show that the reflector status is activated and will show a link to your new reflector URL. Press the `Start Server` button and that is it – Indigo will restart and automatically connect to your reflector! To access from the web, simply go to `https://YOUR-REFLECTOR-NAME.indigodomo.net/`. You can now access Indigo web pages from anywhere using your personalized URL. No firewall settings changes, router port forwarding, etc. is needed. And even if your IP address changes, Indigo will automatically make sure that a new connection is re-established within just a few minutes. Your reflector will be active as long as you maintain an active Indigo Up-to-Date subscription. ## Reset Your Reflector's Activation { #reset-your-reflector-s-activation } If you are switching to a different reflector that you've asked us to create for you, or you've been instructed by support to reset your current reflector's activation, then follow these steps: 1. Shut down the Indigo Server (select `Indigo {{ version }}->Stop Server`) but don't quit the Indigo Client 1. Switch to your browser and [log out of your Indigo Account](http://www.indigodomo.com/account/logout/) 1. Go to the [reflector list](http://www.indigodomo.com/account/reflectors/) in your Indigo Account (you'll need to log back in) and click the `Reset` link beside your reflector's status (it should say *Activated* before your press *Reset*) 1. Switch back to the Indigo Client and click on the `Start Local Server` button You should now see an `Activate Reflector` button towards the bottom of the dialog. Click that, log in to your reflector account, and select the appropriate reflector. If the reflector you want to use doesn't show in the list of inactive reflectors, [contact us](http://www.indigodomo.com/#contact) with the name of the reflector you're trying to activate and what steps you've performed. ### Manual Reset If the procedure above doesn't work, and *​only* ​if instructed by support, follow these steps to manually reset your Indigo Client reflector settings: 1. Shut down the Indigo Server (select `Indigo {{ version }}->Stop Server`) but don't quit the Indigo Client 1. Switch to your browser and [log out of your Indigo Account](http://www.indigodomo.com/account/logout/) 1. Go to the [reflector list](http://www.indigodomo.com/account/reflectors/) in your Indigo Account (you'll need to log back in) and click the `Reset` link beside your reflector's status (it should say *Activated* before your press *Reset*). If the reflector has already been deactivated that is fine – just skip this step. 1. In the Finder, select `Go->Go to Folder…` 1. In the resulting dialog, copy and paste the following: `/Library/Application Support/Perceptive Automation/Indigo {{ version }}/Preferences/` 1. In the resulting Finder window, delete the folder named `PrismReflector` 1. Switch back to the Indigo Client and click on the `Start Local Server` button ## Indigo Touch and Your Reflector [Indigo Touch](http://www.indigodomo.com/touch.html) for iOS is transparently integrated with the Indigo Reflector service. When you use your iOS device (iPhone, iPad, etc.) to connect to Indigo while in your house (and on your local Wi-Fi network), Indigo Touch will automatically retrieve and remember your reflector address. You can press the settings (gear) icon on the top toolbar then find the `Reflector` item near the bottom to verify that it is working correctly. Once Indigo automatically detects your reflector address, it will seamlessly change between using the local Bonjour detected address and the remote reflector address. Just launch Indigo Touch and it works, no matter where you are! ## Bandwidth Limits Because using a reflector has to bounce all requested files to and from our server, monthly bandwidth usage cannot be unlimited. Reflectors that exceed an average bandwidth of approximately 200 MB per day may have temporary limitations or throttles imposed, but a vast majority of the time bandwidth is not an issue. There are a couple of things that can lead to excessive bandwidth usage. So please consider: - If you have a graphical Control Page in Indigo that uses the `Refreshing Image` control type then use a longer refresh duration (30 minutes or longer) for the image, especially if you plan to frequently access the page remotely. - Not leaving Indigo Touch or an Indigo web browser page running continuously while not viewing it. By closing the browser window or leaving Indigo Touch (home button), the requests through the reflector will stop which significantly reduces daily bandwidth usage. Note these usage suggestions only apply to connections when a reflector is being used. If you are directly connected to your Indigo Server on your home network or not using a reflector then the bandwidth isn't going through our servers and you can have Control Page images refreshed as frequently as you would like. ## Troubleshooting In this section you'll find information on troubleshooting any reflector errors/issues you may be experiencing. If you see any of the following errors in the Event Log window, do as described to resolve the issue. - failed to create reflector connection: reflector not active - try the steps above to [reset your reflector's activation](#reset-your-reflector-s-activation) - reflector connection test failed: local server unreachable - check [this forum post](https://forums.indigodomo.com/viewtopic.php?f=131&t=27344) for the likely causes. - Unable to authenticate with IndigoDomo.com (server might be down temporarily for maintenance) - you may be able to resolve the issue by: 1. shutting down the Indigo Server, 1. deleting the registration file located at *`/Library/Application Support/Perceptive Automation/Indigo {{ version }}/Preferences/Indigo Registration.indiPref`*, 1. restarting the Indigo Server, and 1. when prompted, enter your Indigo Account information. Reflectors may also be affected by a bad entry in your *`/etc/hosts`* file. [Check this forum post for details](https://forums.indigodomo.com/viewtopic.php?t=27344). ### Advanced Troubleshooting If nothing else works or if directed by Indigo support, [create a new topic in the reflectors forum](https://forums.indigodomo.com/viewforum.php?f=10) detailing what you've tried and do the following: 1. Launch the Terminal application (inside *`/Applications/Utilities/`*) 2. Copy/paste each line below *individually* and hit the return key after each one: id cd /Library/Application\ Support/Perceptive\ Automation cd Indigo\ {{ version }}/IndigoServer.app/Contents/Resources/PlugIns/ ps -axww | grep "Indigo" 3. Select the entire results of the Terminal window (CMD-A) and copy/paste into Code tags (the icon that looks like this: *``*). 4. Copy/paste each line below *individually* into the Terminal window and hit the return key after each one: ./reflector_library_stub.py -m geturl -d ./reflector_library_stub.py -m tunnel -d 5. Wait 3 minutes. It can take a while for the network errors we are trying to catch to be reported. Then enter again: ps -axww | grep "Indigo" 6. Select the entire contents of the Terminal window (CMD-A) again and copy/paste results into another section with Code tags. --- SSL Certificates Examples (https://docs.indigodomo.com/2025.2/user/remote-access/ssl-certificates/) --- # Indigo Web Server Certificates !!! abstract "In this guide" How to configure browsers and HTTP clients (such as Node-RED) to accept the Indigo Web Server's self-signed SSL certificate for local HTTPS connections. Covers locating the certificate files in the Web Assets folder and configuring TLS settings in third-party tools. ## Node Red Example The following example shows how to configure a Node Red flow that uses the Indigo Web Server (IWS) API to operate a lamp device. The example is only meant to show how to use a self-signed certificate and public key to enable the flow to connect to the IWS using `*https*` instead of `*http*` when using the IWS on a local network via `*10.0.1.123*`, `*127.0.0.1*`, or `*localhost*` (using the API via the Indigo Reflector Service uses a different CA-approved security certificate and the flow is configured the same as it would be using any API via `*https*`). ![Noe Red Image](../../images/node_red_1_flow.png){ width=600 } Using the flow via `*https*` requires a few settings. Enter the preferred URL to point at the local IWS address `*10.0.1.123*`, `*127.0.0.1*`, or `*localhost*` with the URL: ```text https://:8176/v2/api/command/ ``` for example, ```text https://10.0.1.123:8176/v2/api/command/ ``` ![Node Red 6 HTTP Request Node Image](../../images/node_red_6_http_request_node.png){ width=400 } select TLS, and use bearer authentication along with a valid token key (from your Indigo Account Authorizations). Then, use key and certificates from local files and point them to the certificate and public key files located in ```text /Library/Application Support/Perceptive Automation/Indigo {{ version }}/Web Assets/cert/ ``` ![Node Red 7 HTTP Request Node Image](../../images/node_red_7_http_request_node_tls_config.png){ width=400 } Several other screenshots complete the example: | ![Node Red Inject Node Turn On Image](../../images/node_red_2_inject_node_turn_on.png){ width=400 } | | --- | --- | | ![Node Red Inject Node Turn On JSON Image](../../images/node_red_3_inject_node_turn_on_json.png){ width=400 } | | ![Node Red Inject Node Turn Off Image](../../images/node_red_4_inject_node_turn_off.png){ width=400 } | | ![Node Red Inject Node Turn Off JSON Image](../../images/node_red_5_inject_node_turn_off_json.png){ width=400 } | | ![Node Red HTTP Debug Node Image](../../images/node_red_8_http_debug_node.png){ width=400 } | --- Indigo Touch for Web (https://docs.indigodomo.com/2025.2/user/remote-access/touch-for-web/) --- # Indigo Touch For Web !!! abstract "In this guide" How to load and use Indigo Touch for Web (ITW), the browser-based control client built into Indigo. Covers local and reflector-based access URLs, the device/action/variable/pages tabs, and what functionality is available compared to the full Mac client. Indigo includes an [integrated web server](web-server.md) that allows you to serve your own custom content, and it also includes a web-based alternative to Indigo Touch for iOS called **Indigo Touch for Web** or ITW. ITW is a Single Page Application (SPA) that runs entirely in a browser. ITW is not meant to be a replacement for the Mac Client -- you can't configure devices or add variables for example -- however, the most common Indigo functions are available. These include turning on/off devices, controlling thermostats, changing variable values and so on. Available features are listed below, and we have plans to include more features in the future! ![Indigo Touch Web Image](../../images/itw_tile_view_light.png){ width=800 } ITW works by establishing a bidirectional connection to the Indigo [Websocket API](../../api/index.md). ITW works on both desktop and mobile (mobile availability depends on configuration and whether you're connected locally via Wi-Fi or remotely via cellular). ## Loading ITW There are several ways to load ITW depending on your environment. The three most common being: 1. the Indigo Reflector Service using your custom reflector address such as *`https://my_reflector.indigodomo.net`* via a secure *`https://`* connection (the reflector service requires an Indigo up-to-date subscription). 1. a local loop-back address such as *`http://localhost:8176`* or *`http://127.0.0.1:8176`* via an insecure *`http:`* connection. These connections are only valid when used on the same machine running the Indigo server (and obviously, won't work on mobile). 1. a direct IP address such as *`http://10.0.1.123:8176`* or *`http://192.168.0.123:8176`* via an insecure *`http:`* connection. Note that the custom port address (8176 above) may differ depending on the settings you use when launching the Indigo Server. When you use a bare address like the examples above, the server will redirect the request and load the ITW homepage *`index.html`* so -- depending on your browser -- the address field will show *`localhost:8176/index.html`* or *`http://localhost:8176/index.html`* or something similar. ## Interface Controls When ITW loads, you'll be presented with the main view which has five tabs to choose from -- Devices, Actions, Variables, Pages and Logs (Indigo schedules are not currently shown in ITW). ### Tabs #### Devices - The Devices tab shows the devices in the Indigo database. - Only devices with *`Remote Display`* checked will be displayed. - Many device controls are available in ITW, including: - On/off - Lock/unlock - Dimming - Thermostat Devices also have a special popup menu which is shown when you click the ellipsis icon *`...`* at the top (or on the right side) of the device tile. This displays a menu with up to four options (depending on the type of device): - Copy ID - This option will cause the device's ID to be copied to the clipboard. Due to the security of modern browsers, this feature will only work via *`https`* connections via the Indigo Reflector Service and via *`http`* when connecting using a local loopback address like *`localhost`* or *`127.0.0.1`*. - Refresh from server - ITW objects update automatically when data are changed in Indigo, but sometimes you might want to force the data to refresh for a specific device. Selecting this option will cause the device data to be refreshed. - Send status request - only available for devices that support the Send Status Request feature in the Indigo Client. - Show device JSON - this option will cause a pop-up window to display the entire JSON payload for the device. #### Actions - The Actions tab shows the action groups in the Indigo database. - Only actions with *`Remote Display`* checked will be displayed. - Clicking a displayed action button will cause the action to be executed. #### Variables - The Variables tab shows all the variables in the Indigo database. - Only variables with *`Remote Display`* checked will be displayed. - You can change the value of any variable (with the sole exception of *`isDaylight`* which is read only). Clicking on a variable value will cause a dialog box to open where you can edit the value. !!! note **It's possible for the value to change on the Indigo server while you are editing it. In instances where this happens, the last write will take precedence. In other words, if you click save in ITW after the value has changed on the server, the ITW value will become the new value.** - For select boolean values, a toggle value button will be displayed in the editing dialog--similar to Indigo Touch. Variables also have a special popup menu which is shown when you click the ellipsis icon *`...`* on the right side of the variable tile. This displays a menu with two options: - Copy ID - This option will copy the variable's ID to the clipboard. - Copy value - This option will copy the variable's value to the clipboard. Due to the security of modern browsers, these features will only work via https connections or via http when connecting using a local loopback address like localhost or 127.0.0.1. #### Triggers - The Triggers tab shows all the triggers in the Indigo database. - Triggers don't have a *`Remote Display`* checkbox, so no triggers are hidden from view. - Disabled triggers are shown with a red border. - Clicking a displayed trigger button will cause the associated events to be executed. #### Schedules - The Schedules tab shows all the schedules in the Indigo database. - Schedules don't have a *`Remote Display`* checkbox, so no schedules are hidden from view. - Disabled schedules are shown with a red border. - Clicking a displayed schedule button will cause the associated events to be executed. #### Pages - The Pages tab shows control pages in the Indigo database. - Only control pages with *`Remote Display`* checked will be displayed. - Clicking a displayed control page button will cause the page to be loaded in a separate window or tab. #### Logs - The Logs tab begins with the 25 most recent event log entries. - New log messages will be displayed while the Logs tab is active. - Debug, caution and warning messages will be colored appropriately. - Long and multiline log messages will be truncated, and will appear with an ellipsis (...). Clicking on a truncated message will expand it. Click again to collapse it. ## Search Tools ### In Folder This dropdown list will list the folders appropriate to the tab selected. For example, the Actions tab will only display folders in your actions list in Indigo. Selecting a folder will filter the object list as it does in the Indigo client. ### Name Contains This text field allows you to filter the object list based on the object's name field. You can search by partial text -- for example, entering *`ext`* will show both "**Ext**erior" as well as "T**ext**". ## Themes ITW currently supports two themes -- light mode and dark mode. You can toggle between the two using the button at the top right of the screen. Your choice is saved locally via your browser's local storage, so the next time you visit, ITW will display using your theme preference. If you clear your browser's cache (including local storage), your preference choice will be erased. This setting is also browser specific so if you rotate among different browsers or have multiple users, each can have its own unique preference saved. | Light Mode | Dark Mode | | | | --- | --- | --- | --- | | ![Indigo Touch Web Image Light](../../images/itw_tile_view_light.png){ width=800 } | ![Indigo Touch Web Image Dark](../../images/itw_tile_view_dark.png){ width=800 } | ## Views Indigo Touch Web supports two views--Tile view and List view. You can toggle between the two views using the view button located in the upper right corner. | Tile View | List View | | | | --- | --- | --- | --- | | ![Indigo Touch Web Tile View](../../images/itw_tile_view_light.png){ width=800 } | ![Indigo Touch Web List View](../../images/itw_list_view_light.png){ width=800 } | Items in List view are collapsed by default. Clicking on the ">" next to an item will expand it to display things like additional controls and details. Click on the element's ">" again to collapse it. List view is available for all ITW tabs (except log entries). ## Loading A Specific Tab By default, ITW will show the Devices tab when it first loads. If you would like to begin with a different tab, you can add a URL query argument to the end of the target URL (**query arguments must be lowercase, and you must include *`index.html`* as a part of the URL for this feature to work**). Examples: | Tab | Target URL | Default | | --- | --- | --- | | Devices | *`http://localhost:8176/index.html?tab=devices`* | X | | Actions | *`http://127.0.0.1:8176/index.html?tab=actions`* | | | Variables | *`https://my_reflector.indigodomo.net/index.html?tab=variables`* | | | Pages | *`http://192.168.0.123:8176/index.html?tab=pages`* | | | Logs | *`http://10.0.1.123:8176/index.html?tab=logs`* | | ## Troubleshooting If you don't see the ITW web interface displayed in your browser, there are several things you can check. - be sure you're using the right URL security protocol: - *`https://`* when accessing via the Indigo Reflector Service, and - *`http://`* via the other available means. - be sure you've entered the correct address for your Indigo server and the port number you specified when you started the server (the default is 8176). - if the proper tab wasn't selected when you loaded the app, be sure your query arguments are lowercase... *`?tab=logs`* is valid, *`?tab=Logs`* is not. --- Indigo Web Server (https://docs.indigodomo.com/2025.2/user/remote-access/web-server/) --- # Indigo Web Server !!! abstract "In this guide" How to configure and use the Indigo Web Server (IWS) to serve custom content locally or via the Indigo Reflector. Covers authentication methods, port settings, the Web Assets folder structure, plugin resource folders, and integration examples using Node-RED and JavaScript frameworks. The Indigo Server contains a fully-functioning web server that you can use to serve content locally or via the Indigo Reflector Service (the Reflector requires an active Indigo Up To Date subscription). You can use this functionality to make content available in a variety of ways. For example, Indigo users have created complex websites using JavaScript, [Vue](https://vuejs.org), and [Svelte](https://svelte.dev). See below for a fully-functional [example](#example). ## Security Certificates Displaying web content in a secure format using HTTPS requires a security certificate for the site your browser is connected to. These certificates are typically reviewed and signed by a third party authority to ensure they're legitimate. Indigo's certificate is “self-signed”, which means that it hasn't been reviewed by a third party. We must use a self-signed certificate because it isn't possible to have an authority-signed certificate for a local server name (localhost or 127.0.0.1) that doesn't have its own domain name. Appropriately, your browser will warn you that a security certificate is self-signed and require you to intervene in order to display the requested content. You can choose to respond to this warning each time it appears, or you can tell your browser to trust the certificate – which will typically silence these warnings. We have a [separate page](ssl-certificates.md) that describes various ways to handle self-signed certificates. ## Advanced Web Server Settings Select the `Indigo {{ version }}->Advanced Web Server Settings...` menu item and you'll get this dialog: ![Web Server Settings Dialog](../../images/web_server_settings_2025_1.png){ width=700 } The menu has three options: 1. Debug Logging: Enable this setting to show extra Web Server debugging information. 1. Hide Security Logging: Enable this setting to hide HTTP API connection attempts. Warning: enabling this will reduce the logging of incoming HTTP API requests. We recommend keeping these messages as they can help identify security issues. 1. Cache Controls: If you are having issues with cached API Keys (not Local Secrets), use the button to clear the key cache. This will force Indigo to connect to your Indigo Account to validate the next transaction which contains an API Key. ## Web Assets Folder Structure Indigo {{ version }} contains special folders that are accessible to the Indigo Web Server which are located in: ```text /Library/Application Support/Perceptive Automation/Indigo {{ version }}/Web Assets/ ``` There are four stock folders located under this parent folder: ```text /Library/Application Support/Perceptive Automation/Indigo {{ version }}/Web Assets/images /Library/Application Support/Perceptive Automation/Indigo {{ version }}/Web Assets/plugins /Library/Application Support/Perceptive Automation/Indigo {{ version }}/Web Assets/public /Library/Application Support/Perceptive Automation/Indigo {{ version }}/Web Assets/static ``` As a convenience, you can access these folders under the Indigo Help menu: `Help` --> `Show Web Assets Folder` All assets you want to make available to the Indigo Web Server should be stored in these folders. ### images folder This folder is used to publish files and make them available in the Indigo UI for use in control pages (and for other purposes). More information on using this folder can be found in the [Custom Images on Control Pages](../concepts/control-pages.md#custom-images-on-control-pages) section. ### plugins folder With Indigo 2022.2, web server plugins are no longer supported. ### public folder This directory can be used to publish files from the Indigo Web Server that will be made available to anyone ****without any authentication****. If someone knows the URL of your reflector, they will have access to any files that are in this directory, so use it wisely. For example: ```text https://MYREFLECTOR.indigodomo.net/public/about.txt http://10.0.1.2:8176/public/about.txt http://localhost:8176/public/about.txt ``` Basic MIME types will be determined from file extensions. Subdirectories are also allowed so you can create hierarchy: ```text https://MYREFLECTOR.indigodomo.net/public/somedirectory/somefile.html https://MYREFLECTOR.indigodomo.net/public/images/somepic.jpg ``` Any files and/or directories in the /public/ folder will need to be moved over when you upgrade to a new major version (the installer WILL NOT move them automatically). The public folder was added with Indigo version 7.1. ### static folder This directory can be used to publish files from the Indigo Web Server that will be made available to anyone ****with authentication****. If someone knows the URL of your reflector, they will have access to any files that are in this directory once they have entered the appropriate authentication credentials for your reflector. For example: ```text https://MYREFLECTOR.indigodomo.net/static/about.txt http://10.0.1.2:8176/static/about.txt http://localhost:8176/static/about.txt ``` Basic MIME types will be determined from file extensions. Subdirectories are also allowed so you can create hierarchy: ```text https://MYREFLECTOR.indigodomo.net/static/somedirectory/somefile.html https://MYREFLECTOR.indigodomo.net/static/images/somepic.jpg ``` ### Authentication #### API Keys Information on Indigo Authentication options using API Keys can be found on the [Integration APIs](../../api/index.md) page under the authentication section. #### Local Secrets In addition to authentication via API Keys and the Indigo Reflector (recommended), users can create their own local secrets (another type of key) that don't require an internet connection to validate. Users can create a JSON list of their own "secrets" that will be loaded whenever the web server is restarted. The secrets file is located in the install folder's *`/Preferences`* directory and is named //`secrets.json`//: *`/Library/Application Support/Perceptive Automation/Indigo {{ version }}/Preferences/secrets.json`* Those secrets can be used in both authorization headers and in the api-key query argument on URLs (exactly the same as API Keys). Example: ```json [ "here-is-a-key", "this*is*another*key" ] ``` Additionally, if the user wants to completely disable API Keys, they may add the string "do-not-use-api-keys" to their list of secrets. ```json [ "here-is-a-key", "this*is*another*key", "do-not-use-api-keys" ] ``` The effect here is that only the first two will successfully authenticate API calls and nothing else. If the user only specifies this: ```json [ "do-not-use-api-keys" ] ``` The effect would be the same as unchecking the "Enable OAuth and API Key authentication" checkbox in the Start Local Server dialog, but with different logging when an authentication try fails. While valid, this configuration is not recommended; instead, users should use the OAuth setting when the server is first started (to avoid confusion). You use the local secret(s) the same way you would use an API Key; for example, *`http:*localhost:8176/v2/api/indigo.devices/123456789?api-key=my-local-secret`// **Important!** - When making changes to the *`secrets.json`* file, you must restart the server for the changes to take effect (the server loads the secrets file at startup). ### Custom Web Page Example { #example } Using the Indigo Web Server and Web Assets folders is extremely easy. This example is for reference purposes and is not meant to be a primer on constructing Web content. There are many good tutorials online that you can refer to if needed. First, create your content. For example, websites often contain a `index.html` page. A simple `index.html` page looks like this: ```xml Indigo Web Server
Hello world.
``` Using a plain text editor, save this code as `index.html` within the `.../Web Assets/static` folder (to serve it with authentication) or within the `.../Web Assets/public` folder (to serve it without authentication). You can refer to assets located in other folders by referencing them with the `Web Assets` folder as the root. That's it! The Indigo Web Server will serve your web page via the appropriate method. ## Troubleshooting The Indigo Web Server is a complex piece of the Indigo ecosystem, but it rarely has issues. There are some things that have happened in the past, and we thought we'd outline some debugging steps here. ### Websocket Failures In rare circumstances, problems may arise that cause an inability to connect to Indigo's [Websocket API](../../api/websocket.md). This will often manifest as [Indigo Touch for Web](touch-for-web.md) hanging at the Loading page. Here are some troubleshooting tips that should get you back in business. #### Enable Diagnostic Tools **Webserver Debug Options** In the Indigo client UI, go to Indigo > Advanced Web Server Settings... and enable **Debug Logging**. In rare circumstances you may need to enable **Cache Debug Logging** as well. This logging may provide some useful details. **Browser Debugging** Enable console debugging in your browser. The steps vary depending on the browser you're using, but in Safari go to Settings > Advanced > Show features for web developers. You should now see a Developer tab in settings and on the main Safari menu. Head to Develop and select Show JavaScript Console. Now trying to load Indigo Touch for Web and see if there are any errors shown in the console log (Indigo Touch for Web uses JavaScript to render the various pages). **Script Debugging** If you're having trouble getting a Python, JavaScript or other code to connect, it's best to add a lot of logging and error trapping to your code which will help to isolate any issues. You can always turn off or remove extra logging when you're done. #### Make Sure You Have the Right Websockets Version Installed Each new version of Indigo is tested against a specific version of the websockets library and having a different websockets version can potentially cause problems. If you can't connect to the server using [Indigo Touch For Web](touch-for-web.md), the websockets library may be causing some trouble. To check which version of the websockets library you have installed, open a terminal window and run the following command: ```text pip3 show websockets ``` To see which version **should** be installed, head over to [Python Packages and Indigo](../../scripting/guides/python-packages.md) and follow the instructions there. Also, note that you may have multiple version of Python installed, so you'll want to make sure you're using the right pip by specifying the full version (pip3.11, pip3.13, etc). #### Make Sure You're Using the Right Security Layer The security settings you chose when you started the Indigo Server affect how you connect to the web server. | Security | Protocols | | --- | --- | | HTTPS enabled | must use links that start with `https://` and `wws://` | | HTTPS not enabled | must use links that start with `http://` and `ws://` | #### Test With a Simple Script or Link Another diagnostic tool you can try is to attempt to connect to the server using simple, known methods to try to isolate the issue. There are sample websocket scripts near the top of the [Integration APIs](../../api/index.md) page. These scripts will attempt to make a simple connection to the websocket API. #### Review Your Network Configuration Some networking configurations can impact your ability to attach to the Indigo Server using the API endpoints. You should ensure that you're not blocking IPs or ports that you're trying to connect with. If you're running your traffic through a VPN, blocking or filtering traffic through applications like Little Snitch or PiHole, make sure these programs aren't blocking the traffic. Make sure that your Indigo Server and client are both on the same network (including subnet). Also check to ensure that nothing else is trying to use the port you're using with Indigo. ### Other Potential Issues There are other issues that may arise with the Indigo Web Server. If you are having issues outside the above, then you can use the next sections to help diagnose what might be causing them. #### Check Your Hosts File Sometimes, hosts file settings can cause conflicts. Review your hosts file (do not make changes unless you know what you're doing) to see if anything looks like it might be rerouting your connection. The hosts file is typically located in `/etc/hosts`. It should only contain lines similar to this: 127.0.0.1 localhost 255.255.255.255 broadcasthost ::1 localhost If you have other declarations, especially for `localhost`, then that might be interfering with normal Web Server operation. We recommend commenting out any lines that are not the above lines to debug any issues you may have. #### Check Your Server's Network Connection and Settings Assuming your server machine is able to connect to your network, check to ensure that the server machine isn't connecting with both wired and wireless connections. This can sometimes cause issues with network traffic. It's generally best to stick to one connection method. #### Try Local and Reflector Connections If you can't connect locally, you still may be able to connect with the Indigo Reflector service. The reverse is also true. If you can connect via one method but not the other (both should work under normal circumstances) that may help you isolate the issue. #### Disable Firewalls and Virus Protection Sometimes various firewall products (including macOS built-in firewall) can block different types of network connections. Virus Protection software can be especially bad about interfering with network traffic. While troubleshooting it's recommended to disable all such software on your Mac. --- Insteon/X10 Signal Troubleshooting (https://docs.indigodomo.com/2025.2/user/troubleshooting/powerline-signal-troubleshooting/) --- # Insteon and X10 Troubleshooting Basics Insteon and X10 are both power line protocols that allow remote control and monitoring of modules through your existing home wiring. Insteon is a newer, more robust standard that provides improved performance and signal reliability. There are dozens of different Insteon and X10 [control modules](https://www.indigodomo.com/devices/) supported by Indigo which can be used to control lighting, appliances, hot tubs, sprinklers, thermostats (HVAC), and much more. These control modules listen for Insteon or X10 commands on your existing power lines, decode this information, and then control the device (light, appliance, thermostat, etc.). Indigo both listens and transmits to these control modules using a [home control computer interface](https://www.indigodomo.com/devices/interfaces). Insteon is also dual-band, which means that it sends signals both on the power line and wirelessly. Most Insteon devices that are not battery powered and have shipped since 2014 or so are dual-band. That is, they send and receive signals from both the power line and from RF. It's worth noting however that these dual-band devices often prioritize power line signals over RF. That means that if there is signal noise on the power line, the device may still fail to operate reliably even though it's dual-band. ## Signal Troubleshooting In some cases Insteon or X10 control modules fail to receive or properly decode the information sent over the power line. This typically happens because there is excessive noise on the power line caused by some other appliance, such as a power supply, surge protector strips, portable electronics charger, etc., or because the signal is not able to travel over that particular leg, or phase, of your household wiring. The most common symptom of a signal problem is when some Insteon or X10 modules are controllable, but others fail to respond to commands sent by Indigo. In extreme cases, the signal can fail to get to all modules. To troubleshoot modules that will not turn on/off from Indigo: ### Bridge Your Home's 110V Power Legs Every house has two 110V power legs that are electrically connected only at the street or alley transformer. Most Insteon devices that shipped over the past several years that aren't battery powered are dual-band, which means that they repeat over both the power line and RF. If you have older devices, then properly installing two [Insteon Range Extenders](https://www.smarthome.com/insteon-2992-222-range-extender.html) will also bridge all Insteon signals across both power legs. Dual-band devices (which includes the Range Extenders) are required to have a complete and reliable Insteon home network. Be sure and follow the instructions that come with the Range Extenders to ensure that they are plugged into outlets on opposite power legs. X10 customers will need to find an alternate device to bridge the power legs for X10 commands. **Important**: *The Insteon Range Extenders, AccessPoint RFs and SignaLinc RFs ONLY bridge Insteon signals.* ### Change the Insteon / X10 Signal Path Sometimes just changing the signal path from the transmitting computer interface (PowerLinc, CM11, etc.) to the destination module can help. Try the following, attempting to turn the module on/off after each step: - Plug the computer interface into a different outlet. If needed, use a short extension cord to reach another outlet. - If the destination module is a plug-in type (LampLinc, ApplianceLinc, etc.), then temporarily plug it directly into the PowerLinc's passthrough outlet. If it works there, then this proves it is a signal problem. - Insteon users can move their AccessPoint RF pair (or SignaLinc RF pair) to other outlets in the house. Per their instructions, be sure the new outlets are on opposite power legs. - Insteon users can also plug one of the AccessPoint RFs directly into the PowerLinc's passthrough outlet. Per the AccessPoint instructions, the other AccessPoint must still be installed on the opposite power leg. This can help if there are severe signal problems on the same circuit as the PowerLinc. ### Temporarily Remove or Filter "Signal Suckers" Some electronics can significantly attenuate, or diminish, the strength of Insteon and X10 signals. Uninterruptible Power Supplies (UPSs) and high-end surge protector strips are notorious for attenuating both Insteon and X10 signals. Try temporarily unplugging all UPSs (just let them run off of battery for a couple of minutes) to see if that allows control of the module. If it does, then you can isolate the UPS so that it doesn't cause problems by plugging it into an [Insteon Noise Filter](https://www.smarthome.com/filterlinc-10-amp-plug-in-noise-filter.html). In addition to UPSs, try temporarily unplugging other electronics and surge protector strips that are on the same circuit as the PowerLinc. Potential problem devices include: - UPSs and surge protector power strips - televisions and video game systems - laptop and mobile phone chargers - fax machines - MIDI musical instruments If unplugging any of them helps, then you can isolate the problem device with an [Insteon Noise Filter](https://www.smarthome.com/filterlinc-10-amp-plug-in-noise-filter.html). **Important**: Although UPSs and other electronics on the same circuit as the PowerLinc are most often the culprit, it is possible for UPSs on other circuits or other parts of the house to cause signal problems. Try unplugging all UPSs if you don't see an improvement after removing devices on the same circuit as the PowerLinc. ### Temporarily Remove or Filter Noisy Electronics Some electronics can also introduce noise on the power lines that can cause problems for Insteon and X10 signal reliability. Try temporarily unplugging the following, especially if they are on the same power circuit as the home control computer interface (PowerLinc, CM11, etc.) or the module you are trying to control: - portable electronic chargers (laptop, mobile phone, tooth brushes, razors, etc.) - CFL, LED, halogen and HID lighting - fans, treadmills, and other appliances with motors If unplugging any of them helps, then you can isolate the problem device with an [Insteon Noise Filter](https://www.smarthome.com/filterlinc-10-amp-plug-in-noise-filter.html). ### Identify Failing X10 (and Insteon) Devices If you see traffic from X10 device addresses for which you don't have a corresponding device, it is possible that you have another X10 or Insteon device that's starting to fail and is erroneously broadcasting out garbage that looks to the interface like valid X10 traffic. The interface may become so busy trying to decipher these garbage transactions that it will not reliably send commands. You'll need to find the offending device and disable it. With plug-in modules that's simple, just unplug them all and start adding them back one at a time. It's a bit harder with devices that are in-wall. Some of these devices have the ability to be "air-gapped" which will completely remove power to them. Most SwitchLinc devices, for instance, can be air-gapped by pulling the LED pipe below the switch out until it clicks and stays pulled out. Other devices that don't have this ability will be harder to deal with. You can cut off circuit by circuit at your power panel until the problem ceases. That will give you a better clue where the problem device is located. ### Increase the Insteon / X10 Signal Strength Although we recommend making sure your power legs are bridged correctly (see above) and isolating problem electronics first (see above), if you still have unreliable operation with some modules then you can try increasing the signal strength with a signal repeater or booster. All Insteon modules are automatically repeaters (of Insteon commands only), so adding additional modules (LampLinc, ApplianceLinc, SwitchLinc, etc.) or additional AccessPoint RFs can help. X10 commands are only repeated by an active couple repeater, like the dryer SignaLinc Coupler Repeater. Another option besides repeating the command is to use a signal booster, like the BoosterLinc. Note the BoosterLinc only boosts X10 signals. ## Signal Isolation Steps When diagnosing issues, the most thorough practice is to isolate which circuit appears to have the most problems. As described above, changing the signal path may help you identify specific circuits that have issues. A more definitive approach is to check circuits individually. Start by turning off all circuits in your house except the one with your Mac and PowerLinc. Make sure that all devices on that circuit work correctly. Then, turn the circuits back on one by one and check all the devices on all active circuits. When you start experiencing failures (no acknowledgements with Insteon), then it's likely that the most recently turned on circuit has something that's causing the noise. You can then start unplugging/disabling devices (air-gap any in-wall switches — for Insteon switches this means pulling the LED pipe below the switch out until it clicks, thus completely removing the power) and other things plugged in until communication seems to improve. Repeat this process as necessary until you can isolate the problem areas and devices. ## Getting More Help Still having a problem? Perform the following troubleshooting steps and send the results to us in an email, or — to draw on the expertise of the entire Indigo community — post the results on our active [online forum](https://forums.indigodomo.com/). 1. Choose the **Window → Event Log** menu item. 2. Press the **Clear Window** button at the top of the **Event Log** window. 3. Choose the **Interfaces → Insteon/X10 Power Line → Enable** menu item if it isn't already enabled. 4. From the Main Window select the Devices list and the Device you want to control, then press the **Turn On** and **Turn Off** buttons at the bottom of the window several times. 5. **Question**: Did the module turn on or off? 6. If the Device module is a plug-in type (LampLinc, ApplianceLinc, etc.), then plug it and its lamp/appliance directly into the PowerLinc's passthrough outlet. **Otherwise**, if the Device module is not a plug-in type, then plug the PowerLinc (or CM11) into a different outlet. If needed, use a short extension cord to reach another outlet. 7. From the Main Window select the Devices list and the Device you want to control, then press the **Turn On** and **Turn Off** buttons at the bottom of the window several times. 8. **Question**: Did the module turn on or off? 9. Select the Event Log window, then select all the logged text and copy/paste it into a [forum post](https://forums.indigodomo.com/) or email, along with a description of what occurred while following these steps. --- Python Version Conflicts (https://docs.indigodomo.com/2025.2/user/troubleshooting/python-conflicts/) --- # Python Version Conflicts !!! abstract "In this guide" This page explains why Python version and module conflicts occur when multiple Python installations coexist on your Mac, and how to ensure scripts and packages use the correct Indigo-managed Python interpreter. Over the years, we've seen multiple reports of issues concerning Python modules, Python versions etc. We'd like to clear this up so that when people run across issues you can understand what's going on. ## Indigo Python Installation First and foremost - Indigo installs Python as part of the install process. We use the installers from python.org, which install here on your Indigo Server Mac: ```text /Library/Frameworks/Python.framework/ ``` Inside that directory are the versions that either we have installed (2.7, 3.10, 3.11 etc.) or which might have been installed by you if you downloaded a Python installer from python.org. Which ones are present is based on the Indigo release you're using. Since Indigo 2022.1, Indigo has been using Python 3. ### Starting up the Python interpreter installed by Indigo If you need to use the Python interpreter installed by Indigo directly from a command line (or shebang), it's always safest to be explicit in the path to the executable. There are tradeoffs though, so here are the various ways from most specific to least: 1. `/Library/Frameworks/Python.framework/Versions/3.11/bin/python3` - this is the most explicit way to start up the 3.11 interpreter installed by Indigo. 1. `/usr/local/bin/python3.11` - this is a symlink to the above option, and has the same downside. In addition, though, since it's a symlink in a more public directory, it could get updated by something else entirely. 1. `/Library/Frameworks/Python.framework/Versions/Current/bin/python3` - the downside to this option is that if you (for some reason) install an older version, the installer might update that link to point to the older version. 1. `/usr/local/bin/python3` - this option is created/maintained by the Python installer as well, so it has the same benefits/issues of the last option, but also since it's a symlink it could get munged by some other installer (homebrew/macports). ### Installing packages using the right pip3 In the same way discussed above (using the right interpreter), you will find the same options for installing Python packages with **pip**. 1. `/Library/Frameworks/Python.framework/Versions/3.11/bin/pip3` - this is the most explicit way to start up the 3.11 interpreter installed by Indigo. 1. `/usr/local/bin/pip3.11` - this is a symlink to the above option, and has the same downside. In addition, though, since it's a symlink in a more public directory, it could get updated by something else entirely. 1. `/Library/Frameworks/Python.framework/Versions/Current/bin/pip3` - the downside to this is that if you (for some reason) install an older version, the installer might update that link to point to the older version. 1. `/usr/local/bin/pip3` - this option is created/maintained by the Python installer as well, so it has the same benefits/issues of the last option, but also since it's a symlink it could get munged by some other installer (homebrew/macports). ## Other Python Installs So far, we've only discussed Python installations that are performed from the macOS installers available on python.org (which we use from our installer). Problems can arise when you (or some installer you run) installs a different 3rd party version of Python. The three most common sources of a 3rd party Python install are: homebrew, macports, and Xcode. Each will leave the existing Indigo-installed Pythons alone (so Indigo will continue to work), but they will insert themselves in unexpected ways, like by altering the **PATH** in your terminal sessions to point to other locations, unexpectedly replacing symlinks, or my installing into locations that are in your PATH variable before `/usr/local/bin` (making it very dangerous to just open a terminal window and typing in **python** or **pip3**). ### Xcode Xcode installs Python 3.9 (as of Xcode 14.2) inside the Xcode application bundle, and inserts these executables **/usr/bin/python3** and **/usr/bin/pip3**. Often times, **/usr/bin** is very early in the PATH so if you don't specify full paths you'll end up getting the wrong python or pip. This is especially troublesome in that if you don't have Xcode installed, and you type just **pip3** in a terminal, it will prompt you to install Xcode and Python 3.9. Buyer beware! ### homebrew & macports We believe homebrew and macports install their Python installs under a directory they create called opt (short for optional): ```text /opt/local/Library/Frameworks/Python.framework/ ``` so it's nice and separate, but it will also likely add items to your **PATH** which will point to these locations for **python3** and **pip3**, so again if you don't specify a full path you might not be getting the right one. ## Troubleshooting Each of the different installers mentioned above will do several things: They will insert paths into the various `.login`, `.profile`, `.bashrc` files such that their version of Python (and its executables) are found first when typing any of the Python commands from the shell without a full path. They set the **PYTHONPATH** (which is a list of file system paths that the Python interpreter looks in to find modules not part of the standard install like those installed by **pip3**) so that they look in their own site-packages directories Let's start with the second one first: they will add their own `site-packages` directories (i.e. `/opt/local/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages`) as well as the one from our Python install (`/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages`) to **PYTHONPATH**. This will, on the surface, make it seem that they're working OK because they're finding things that may have been installed prior to their installation. The problem is that when you install further modules using **pip3** (**the one they installed for you** since you didn't specify a full path), those modules will get installed into their site-packages directory rather than the one from our Python install (`/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages`). So anything new you install won't be accessible from the Python that Indigo installs and uses. But when you test from a shell script, using the **python** (*the one they installed for you* since you didn't specify a full path) command, it works. This is because that command isn't using our Python. This duality makes it very confusing to figure out what's going on because it appears to work from a shell but not from Indigo. It is possible that you can manage having multiple Python installs - you just have to remember which paths to executables you need to use to get modules installed in the correct place. We **highly** recommend that you do not install 3rd party Pythons. This will make sure that your Indigo experience is as painless as possible.