Table of Contents

Indigo Web Server Design Notes 2022.2

In Indigo v2022.2, we’re completely redesigning the web server plugin (commonly referred to as IWS). The major components of the new web server are:

  • Sanic - the new web server replacing CherryPy.
  • Janus - a Python queue package that works across both synchronous (threaded) code and async code (used in Sanic), needed to send CRUD (Create, Retrieve, Update, Delete) messages from the Plugin methods into Sanic.
  • Jinja2 - the new templating engine used to create control pages from the control page XML.
  • Svelte - the new Indigo Touch Web UI is based on this JavaScript library.
  • SvelteUI - a component library for Svelte that we’re using to do most of the heavy lifting in for the UI.

We’re introducing two new APIs:

  1. Websocket API – (which the new Indigo Touch Web UI will use), and
  2. HTTP API – will share as much of the messaging construction with the websocket interface as is practical.

Both of these APIs are authenticated with HTTP Digest and API Keys (either as a query string or an Authorization header which is preferable) depending on how the user configures it in the Start Local Server dialog. We’re deprecating HTTP Basic authentication. This version of the plugin will support all the necessary endpoints for Indigo Touch as outlined below.

We also intend to deprecate the REST API (though we may reverse course if users demand it).

Python vs JavaScript

In these APIs, we’re using JSON (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. A simple example, we may call this an object or a dict:

{
  "key1": "value 1",
  "key2": 2
}

We expect that 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”. It’s also important to note that throughout this article, there are examples of JSON objects that contain comments marked by //. These are for illustration only. The JSON specification does not support comments, and these comments will not be present in messages sent by the server and should be removed from client-side messages.

{
  // These comments can not be in the actual JSON payload and should be removed.
  "key1": "value 1",
  "key2": 2
}

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:

PythonJSON Equivalent
True true
False false
float Number
int Number
None null
dict Object
list Array
tuple Array

Versioning

Starting with Indigo 2022.2, we’re implementing an API versioning scheme. For the original URLs, we’ll implement these for Indigo Touch older than 3.0 (or maybe 2023.1 or whatever):

  • /servercommand
  • /serverrequest
  • /images/
  • /resolveimagepath
  • /refreshingimage

Moving forward, all APIs will be versioned under the following scheme:

  • /v2/ - this is the top level version number and will change as necessary

API Command Summary

Websocket API V2

  • /v2/ws/ - path to all websocket endpoints
  • /v2/ws/devices-feed - the device websocket endpoint
  • /v2/ws/variable-feed - the variable websocket endpoint
  • /v2/ws/action-feed - the action group websocket endpoint
  • /v2/ws/page-feed - the control websocket endpoint
  • /v2/ws/log-feed - the event log websocket endpoint

HTTP API V2

  • /v2/api/ - path to the new API endpoints (REST replacement)
  • /v2/api/command - path to the endpoint when sending Indigo a command (device control, variable update, action execution). Use a POST with the message payloads described below, use an API key to authenticate - preferable as an Authorization header but also can be specified as api_key query arg.
  • /v2/api/devices - path to the endpoint to get a list of devices (TBD may allow specifying some kind of smaller message rather than the full device dict)
  • /v2/api/devices/12345 - path to the endpoint to get a specific device object (the device dict described below)
  • /v2/api/variables - path to the endpoint to get a list of variables
  • /v2/api/variables/12345 - path to the endpoint to specific variable object (dict to be described below)
  • /v2/api/actiongroups - path to the endpoint to get a list of action groups
  • /v2/api/actiongroups/12345 - path to the endpoint to get a specific action group (dict to be described below)
  • /v2/api/controlpages - path to the endpoint to get a list of control pages
  • /v2/api/controlpages/12345 - path to the endpoint to get a specific control page (dict to be described below)

Control Page HTML rendering

  • /v2/controlpage/IDOFCONTROLPAGE - the actual HTML control page rendering endpoint

One exception is /index.html (or no path at all) - this will always launch the web UI. For this version, it’s a Single Page Application (SPA) built using Svelte, SvelteUI, and the new websocket API.

Websocket API v2

This is the API for websockets in the web server in Indigo v2022.2. Websockets are bidirectional TCP connections, which clients can read from and write to – much like you can a socket or serial connection. This initial API is meant to support the current Indigo Touch (iOS and Web) functionality that was previously provided via the old client API (delivered over IWS polls). It’s expected that any other client apps (like DomoticsPad) that want to present a control interface should be able to use this API to create full-fledged clients. It’s important to note that this version will not present functionality enough to build a full-fledged configuration client like the Indigo Mac client. Authentication will be accomplished via OAuth, API Key, or HTTP Digest auth.

There are 5 websocket feeds that work over local IP or via the reflector:

  • device-feed ws://localhost:8176/v2/ws/device-feed
  • action-feed ws://yourreflector.indigodomo.net/v2/ws/action-feed
  • variable-feed ws://yourreflector.indigodomo.net/v2/ws/variable-feed
  • page-feed ws://yourreflector.indigodomo.net/v2/ws/page-feed
  • log-feed ws://yourreflector.indigodomo.net/v2/ws/log-feed

Each feed (except log-feed) will send the following server CRUD messages:

  • add (when a new Indigo object is added)
  • refresh (this message will return the entire Indigo object collection or a single Indigo object if one was requested)
  • update (when an Indigo object changes)
  • delete (when the Indigo object is deleted)

When a websocket connection is made, the client will receive two refresh messages: one with the entire collection of Indigo objects and the other with the entire collection of Indigo folders corresponding to the feed (device folders in the device-feed, etc). From that point on, the client will receive the add / refresh / update / delete / messages.

At any time, the client can request a refresh, and Indigo will return the entire object hierarchy again (or the specific object) in a refresh message. If an objectId is specified in the refresh message, only the specified Indigo object will be retrieved rather than the full list.

The log-feed is different in that it will only send add messages with the appropriate log event object defined below.

JavaScript Example

For JavaScript developers, the following functions are examples of how to use the typeVal to determine rendering colors for various aspects of the log line display:

/**
 * This function will return the color of the border that we draw around event
 * log entries based on the typeVal of the message (see the EventTypes
 * enumeration for details).
 *
 * @param le - the log event object
 * */
function getBorderColor(le) {
  let eType = le.typeVal;
  if (eType === EventTypes.Error || eType === EventTypes.Error_Client) {
    return 'red';
  } else if (eType === EventTypes.Warning || eType === EventTypes.Warning_Client) {
    return 'orange';
  } else if (
    eType === EventTypes.Debug_Client ||
    eType === EventTypes.Debug_Plugin ||
    eType === EventTypes.Debug_Server)
  {
    return 'green';
  } else {
    return theme.colors["gray500"].value;
  }
}
 
/**
 * This function will determine the color of the message or the color of the type
 * string in log view.
 *
 * @param le - the log event object
 * @param part - return the color for this part of the message (primarily the message
 *             or the typeStr)
 */
function getTextColor(le, part="message") {
  let eType = le.typeVal;
  if (eType === EventTypes.Error || eType === EventTypes.Error_Client) {
    return 'red';
  } else if (eType === EventTypes.Warning || eType === EventTypes.Warning_Client) {
    return 'orange';
  } else if (
    eType === EventTypes.Debug_Client ||
    eType === EventTypes.Debug_Plugin ||
    eType === EventTypes.Debug_Server)
  {
    return 'green';
  } else {
    if (part === "message") {
      return theme.colors.secondary;
    } else {
     return theme.colors["gray600"].value;
    }
  }
}

Messages from clients to the server are specific to the feed type and are described below.

HTTP API v2

Messages sent to the HTTP API are formatted in exactly the same way as those used with the websockets API.

Python API Command Example

For Python developers, here is a sample script that sends a device toggle request to the HTTP API:

import requests
 
API_KEY = "API_TOKEN_HERE"
COMMAND_URL = "http://127.0.0.1:8176/v2/api/command"
 
headers = {"Authorization": f"Bearer {API_KEY}"}
 
toggle_message_target_id = 1198679701  # THE INDIGO ID OF THE TARGET DEVICE
toggle_message = {
    "id": "someid",  # RESERVED FOR FUTURE USE
    "message": "indigo.device.toggle",
    "objectId": toggle_message_target_id,  # THE INDIGO ID OF THE TARGET DEVICE
    "parameters": {
        "delay": 5,
        "duration": 5
    }
}
 
print(f"Sending toggle message to device {toggle_message_target_id}")
reply = requests.post(url=COMMAND_URL, headers=headers, json=toggle_message)
print(f"Reply received: {reply.status_code}")
print(f"Reply JSON: {reply.json})

API Object Types

Folder Objects

First, every object type (except logs) may have folders. This is an example of a folder object. It is the same for any folder in any feed (child or children is the generic name for the Indigo objects contained in the folder – device, variable, etc.) 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.

{
  "id": 617272302,
  "class": "indigo.Folder",
    "name": "Airfoil",
    "remoteDisplay": true
}

Folder messages work in any of the websockets (except the log-feed websocket). The following are messages that you’ll receive from the server.

Example add folder message

{
  "message": "add",
  "objectType": "indigo.Device.Folder", // or indigo.Variable.Folder, etc
  "objectDict": {}  // A folder object defined above
}

Example update folder message

{
  "message": "refresh", // we use refresh here because the folder object is small
  "objectType": "indigo.Device.Folder", // or indigo.Variable.Folder, etc
  "objectDict": {}  // A folder object defined above
}

Example delete folder message

{
  "message": "delete",
  "objectType": "indigo.Device.Folder", // or indigo.Variable.Folder, etc
  "objectId": 1234567
}

Example refresh folder server message

{
  "message": "refresh",
  "id": "someid", // Reserved for future use.
  "objectType": "indigo.Device.Folder",
  "objectDict": {}  // A folder object defined above
}

Example refresh all folders server message

{
  "message": "refresh",
  "id": "someid", // Reserved for future use.
  "objectType": "indigo.Device.Folder", // or indigo.Variable.Folder, etc
  "list": []  // A list of folder objects defined above
}

Device Feed

We’re using the dict(device) functionality added a few releases back and then converting that dict to JSON for all device messages sent to clients. This contains everything about the device.

ws://localhost:8176/v2/ws/device-feed
wss://yourreflector.indigodomo.net/v2/ws/device-feed
http://localhost:8176/v2/api/devices
http://yourreflector.indigodomo.net/v2/api/devices/12345

Server messages

You’ll receive full device JSON objects which will represent an Indigo device. Each device will be slightly different based on the device class and the definition (if a custom device). See the Indigo Object Model docs for device details.

Example device object (dictionary in Python)

{
  "address": "3B.04.7A",
  "batteryLevel": null,
  "blueLevel": null,
  "brightness": 0,
  "buttonConfiguredCount": 0,
  "buttonGroupCount": 1,
  "class": "indigo.DimmerDevice", // added class key in 2022.2
  "configured": true,
  "defaultBrightness": 100,
  "description": "valve",
  "deviceTypeId": "",
  "displayStateId": "brightnessLevel",
  "displayStateImageSel": "DimmerOff",
  "displayStateValRaw": 0,
  "displayStateValUi": "0",
  "enabled": true,
  "energyAccumBaseTime": null,
  "energyAccumTimeDelta": null,
  "energyAccumTotal": null,
  "energyCurLevel": null,
  "errorState": "",
  "folderId": 0,
  "globalProps": {
    "com.indigodomo.indigoserver": {}
  },
  "greenLevel": null,
  "id": 1508839119,
  "lastChanged": "2021-09-25T08:24:22",
  "lastSuccessfulComm": "2021-09-25T08:24:22",
  "ledStates": [],
  "model": "LampLinc (dual-band)",
  "name": "Insteon Dimmer",
  "onBrightensToDefaultToggle": true,
  "onBrightensToLast": false,
  "onState": false,
  "ownerProps": {},
  "pluginId": "",
  "pluginProps": {},
  "protocol": "indigo.kProtocol.Insteon",  // changed to the full enum in 2022.2
  "redLevel": null,
  "remoteDisplay": true,
  "sharedProps": {},
  "states": {
    "brightnessLevel": 0,
    "onOffState": false
  },
  "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": null,
  "whiteLevel2": null,
  "whiteTemperature": null
  }

Here are some examples of the server messages that clients will receive on the device feed.

Example add device message

{
  "message": "add",
  "objectType": "indigo.Device",
  "objectDict": {} // Device object as outlined above
}

Example update device message

{
  "message": "patch", // we use a patch rather than send the entire updated device
  "objectType": "indigo.Device",
  "patch": {}  // A patch object - see the Object Patches below for details
}

Device patch objects are created via the dictdiffer python module, 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.

Example delete device message

{
  "message": "delete",
  "objectType": "indigo.Device",
  "objectId": 1234567
}

Example refresh a single device message

{
  "id": "someid", // Reserved for future use.
  "message": "refresh",
  "objectType": "indigo.Device",
  "objectDict": {} // Device object as outlined above
}

Example refresh all devices message

{
  "id": "someid", // Reserved for future use.
  "message": "refresh",
  "objectType": "indigo.Device",
  "list": [] // a list of Device objects as outlined above
}

Client messages

Messages sent from clients to the server. This will most often be commands to control a device, but there are a few other commands (refresh).

Device refresh messages

These messages are sent to the server to refresh a single device, the entire device list, a single device folder, or the entire device folder list.

refresh device object(s) client message

{
  "id": "someid", // Reserved for future use.
  "message": "refresh",
  // Specify the object type
  "objectType": "indigo.Device",
  // Specifying an objectId is optional. If it's included the server will send a refresh 
  // message for the specified object. If no id is specified, the whole object list will be 
  // returned
  "objectId": 123456
}

refresh device folder(s) client message

{
  "id": "someid", // Reserved for future use.
  "message": "refresh",
  "objectType": "indigo.Device.Folder",
  // Specifying a objectId is optional. If it's included the server will send a refresh 
  // message for the specified folder. If no folder is specified, the whole folder
  // list will be returned
  "objectId": 123456
}

Device command messages

indigo.device

These commands can be used on any Indigo device type - it can be considered the “base” class for messages

status request

{
  // Note, 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 received.
  "id": "someid", // Reserved for future use.
  "message": "indigo.device.statusRequest",
  "objectId": 123456  // the device id
}

toggle

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.device.toggle",
  "objectId": 123456,  // the device id
  "parameters": {
    "delay": 5,
    "duration": 10
  }
}

turn off

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.device.turnOff",
  "objectId": 123456,  // the device id
  "parameters": {
    "delay": 5,
    "duration": 10
  }
}

turn on

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.device.turnOn",
  "objectId": 123456,  // the device id
  "parameters": {
    "delay": 5,
    "duration": 10
    }
}

lock

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.device.lock",
  "objectId": 123456,  // the device id
  "parameters": {
    "delay": 5,
    "duration": 10
  }
}

unlock

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.device.unlock",
  "objectId": 123456,  // the device id
  "parameters": {
    "delay": 5,
    "duration": 10
  }
}

indigo.dimmer

brighten

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.dimmer.brighten",
  "objectId": 123456,  // the device id
  "parameters": {
    "by": 5,
    "delay": 10
  }
}

dim

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.dimmer.dim",
  "objectId": 123456,  // the device id
  "parameters": {
    "by": 5,
    "delay": 10
  }
}

set brightness

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.dimmer.dim",
  "objectId": 123456,  // the device id
  "parameters": {
    "value": 50,
    "delay": 10
  }
}

set brightness

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.dimmer.dim",
  "objectId": 123456,  // the device id
  "parameters": {
    "value": 50,
    "delay": 10
  }
}

indigo.iodevice

set binary output

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.iodevice.setBinaryOutput",
  "objectId": 123456,  // the device id
  "parameters": {
    "index": 5,
    "value": true
  }
}

indigo.sensor

set on state

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.sensor.setOnState",
  "objectId": 123456,  // the device id
  "parameters": {
    "value": true
  }
}

indigo.speedcontrol

decrease speed index

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.speedcontrol.decreaseSpeedIndex",
  "objectId": 123456,  // the device id
  "parameters": {
    "by": 2,
    "delay": 5
  }
}

increase speed index

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.speedcontrol.increaseSpeedIndex",
  "objectId": 123456,  // the device id
  "parameters": {
    "by": 2,
    "delay": 5
  }
}

set speed index

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.speedcontrol.setSpeedIndex",
  "objectId": 123456,  // the device id
  "parameters": {
    "value": 2,
    "delay": 5
  }
}

set speed level

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.speedcontrol.setSpeedLevel",
  "objectId": 123456,  // the device id
  "parameters": {
    "value": 50,
    "delay": 5
  }
}

indigo.sprinkler

next zone

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.sprinkler.nextZone",
  "objectId": 123456  // the device id
}

previous zone

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.sprinkler.previousZone",
  "objectId": 123456  // the device id
}

pause schedule

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.sprinkler.pause",
  "objectId": 123456  // the device id
}

resume schedule

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.sprinkler.resume",
  "objectId": 123456  // the device id
}

run schedule

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.sprinkler.run",
  "objectId": 123456,  // the device id
  "parameters": {
    "schedule": [10,15,8, 0, 0, 0, 0, 0]
  }
}

stop schedule

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.sprinkler.stop",
  "objectId": 123456  // the device id
}

set active zone

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.sprinkler.setActiveZone",
  "objectId": 123456,  // the device id
  "parameters": {
    "index": 2
  }
}

indigo.thermostat

decrease heat setpoint

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.thermostat.decreaseHeatSetpoint",
  "objectId": 123456,  // the device id
  "parameters": {
    "value": 2
  }
}

increase heat setpoint

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.thermostat.increaseHeatSetpoint",
  "objectId": 123456,  // the device id
  "parameters": {
    "value": 2
  }
}

**set heat setpoint

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.thermostat.setHeatSetpoint",
  "objectId": 123456,  // the device id
  "parameters": {
    "value": 76
  }
}

decrease cool setpoint

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.thermostat.decreaseCoolSetpoint",
  "objectId": 123456,  // the device id
  "parameters": {
    "delta": 2
  }
}

increase cool setpoint

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.thermostat.increaseCoolSetpoint",
  "objectId": 123456,  // the device id
  "parameters": {
    "delta": 2
  }
}

set cool setpoint

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.thermostat.setCoolSetpoint",
  "objectId": 123456,  // the device id
  "parameters": {
    "value": 76
  }
}

set hvac mode

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.thermostat.setHvacMode",
  "objectId": 123456,  // the device id
  "parameters": {
    "value": "indigo.kHvacMode.HeatCool"
  }
}

set fan mode

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.thermostat.setFanMode",
  "objectId": 123456,  // the device id
  "parameters": {
    "value": "indigo.kFanMode.AlwaysOn"
  }
}

Variable Feed

We implemented the dict(variable) functionality that parallels what we already had in place for devices.

ws://localhost:8176/v2/ws/variable-feed
wss://yourreflector.indigodomo.net/v2/ws/variable-feed
http://localhost:8176/v2/api/variables
http://yourreflector.indigodomo.net/v2/api/variables/12345

Server messages

You’ll receive full variable JSON objects which will represent an Indigo variable.

Example variable object (dictionary in Python)

{
  "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.

Example add variable message

{
  "message": "add",
  "objectType": "indigo.Variable",
  "objectDict": {} // Variable object as outlined above
}

Example update variable message

{
  "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.

Example delete variable message

{
  "message": "delete",
  "objectType": "indigo.Variable",
  "objectId": 1234567
}

Example refresh a single variable message

{
  "id": "someid", // Reserved for future use.
  "message": "refresh",
  "objectType": "indigo.Variable",
  "objectDict": {} // Variable object as outlined above
}

Example refresh all variables message

{
  "id": "someid", // Reserved for future use.
  "message": "refresh",
  "objectType": "indigo.Variable",
  "list": [] // a list of Variable objects as outlined above
}

Client messages

Messages sent from clients to the server. This will most often be commands to update a variable, but there are a few other commands (refresh).

Refresh messages

These messages are sent to the server to refresh a single varable, the entire variable list, a single variable folder, or the entire variable folder list.

refresh variable object(s) client message

{
  "id": "someid", // Reserved for future use.
  "message": "refresh",
  // Specify the object type
  "objectType": "indigo.Variable",
  // Specifying an objectId is optional. If it's included the server will send a refresh 
  // message for the specified object. If no id is specified, the whole object list will be 
  // returned
  "objectId": 123456
}

refresh variable folder(s) client message

{
  "id": "someid", // Reserved for future use.
  "message": "refresh",
  "objectType": "indigo.Variable.Folder",
  // Specifying a objectId is optional. If it's included the server will send a refresh 
  // message for the specified folder. If no folder is specified, the whole folder
  // list will be returned
  "objectId": 123456
}

Variable command messages

This is the only command message you can send to the variable feed.

updateValue

{
  // Note, values passed in the parameter dictionary must be strings. You can
  // pass in an empty string ("") to clear the variable value.
  "id": "someid", // Reserved for future use.
  "message": "indigo.variable.updateValue",
  "objectId": 123456  // the variable id to update
  "parameters": {
    "value": "Some string value"
  }
}

Action Group Feed

We implemented the dict(action_group) functionality that parallels what we already had in place for devices.

ws://localhost:8176/v2/ws/action-feed
wss://yourreflector.indigodomo.net/v2/ws/action-feed
http://localhost:8176/v2/api/actiongroups
http://yourreflector.indigodomo.net/api/ws/actiongroups/12345

Server messages

You’ll receive full variable JSON objects which will represent an Indigo action group.

Example action group object (dictionary in Python)

{
  "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.

Example add action group message

{
  "message": "add",
  "objectType": "indigo.ActionGroup",
  "objectDict": {} // ActionGroup object as outlined above
}

Example update action group message

{
  "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
}

Variable patch objects are created via the dictdiffer python module, 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.

Example delete action group message

{
  "message": "delete",
  "objectType": "indigo.ActionGroup",
  "objectId": 1234567
}

Example refresh a single action group message

{
  "id": "someid", // Reserved for future use.
  "message": "refresh",
  "objectType": "indigo.ActionGroup",
  "objectDict": {} // ActionGroup object as outlined above
}

Example refresh all action groups message

{
  "id": "someid", // Reserved for future use.
  "message": "refresh",
  "objectType": "indigo.ActionGroup",
  "list": [] // a list of ActionGroup objects as outlined above
}

Client messages

Messages sent from clients to the server. This will most often be commands to execute an action group, but there are a few other commands (refresh).

Refresh messages

These messages are sent to the server to refresh a single action group, the entire action group list, a single action group folder, or the entire action group folder list.

refresh action group object(s) client message

{
  "id": "someid", // Reserved for future use.
  "message": "refresh",
  // Specify the object type
  "objectType": "indigo.ActionGroup",
  // Specifying an objectId is optional. If it's included the server will send a refresh 
  // message for the specified object. If no id is specified, the whole object list will be 
  // returned
  "objectId": 123456
}

refresh action group folder(s) client message

{
  "id": "someid", // Reserved for future use.
  "message": "refresh",
  "objectType": "indigo.ActionGroup.Folder",
  // Specifying a objectId is optional. If it's included the server will send a refresh 
  // message for the specified folder. If no folder is specified, the whole folder
  // list will be returned
  "objectId": 123456
}

Action Group command messages

There is only one command message you can send to the action feed.

execute

{
  "id": "someid", // Reserved for future use.
  "message": "indigo.ActionGroup.execute",
  "objectId": 123456  // the action group id to execute
}

Control Page Feed

We implemented the dict(control_page) functionality that parallels what we already had in place for devices.

ws://localhost:8176/v2/ws/page-feed
wss://yourreflector.indigodomo.net/v2/ws/page-feed
http://localhost:8176/v2/api/controlpages
http://yourreflector.indigodomo.net/v2/api/controlpages/12345

Server messages

You’ll receive full variable JSON objects which will represent an Indigo control page.

Example variable object (dictionary in Python)

{
  "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 variable feed.

Example add page message

{
  "message": "add",
  "objectType": "indigo.ControlPage",
  "objectDict": {} // ControlPage object as outlined above
}

Example update page message

{
  "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, 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.

Example delete page message

{
  "message": "delete",
  "objectType": "indigo.ControlPage",
  "objectId": 1234567
}

Example refresh a single page message

{
  "id": "someid", // Reserved for future use.
  "message": "refresh",
  "objectType": "indigo.ControlPage",
  "objectDict": {} // ControlPage object as outlined above
}

Example refresh all pages message

{
  "id": "someid", // Reserved for future use.
  "message": "refresh",
  "objectType": "indigo.ControlPage",
  "list": [] // a list of ControlPage objects as outlined above
}

Client messages

Messages sent from clients to the server. This consists only of refresh requests since there are no other actions that can be taken on control pages.

Refresh messages

These messages are sent to the server to refresh a single page, the entire page list, a single page folder, or the entire page folder list.

refresh page object(s) client message

{
  "id": "someid", // Reserved for future use.
  "message": "refresh",
  // Specify the object type
  "objectType": "indigo.ControlPage",
  // Specifying an objectId is optional. If it's included the server will send a refresh 
  // message for the specified object. If no id is specified, the whole object list will be 
  // returned
  "objectId": 123456
}

refresh variable folder(s) client message

{
  "id": "someid", // Reserved for future use.
  "message": "refresh",
  "objectType": "indigo.ControlPage.Folder",
  // Specifying a objectId is optional. If it's included the server will send a refresh 
  // message for the specified folder. If no folder is specified, the whole folder
  // list will be returned
  "objectId": 123456
}

Log Feed

We convert the event Indigo dictionary into a python dictionary in the event_log_line_received plugin method.

ws://localhost:8176/v2/ws/log-feed
wss://yourreflector.indigodomo.net/v2/ws/log-feed

Server messages

You’ll receive full variable JSON objects which will represent an Indigo log entry.

Example log object (dictionary in Python)

{
  "message": "add",
  "objectDict": {
  "message": "Stopping plugin \"Web Server 2022.2.0\" (pid 1020)",
  "timeStamp": "2022-12-01T12:03:27.759000",
  "typeStr": "Application",
  "typeVal": 0
  },
  "objectType": "indigo.LogEvent"
}

When you first open a websocket connection to the log feed, you’ll immediately receive the last 25 event log entries (individually) and any further event log entries as long as the connection is open. So you only need one handler for messages coming from the log feed since they will always be objects as deviced above (the message key will always be add). 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.

Note that typeVal will be one of the following values:

export enum EventTypes {
  Application = 0,
  Error,
  Error_Client,
  Warning,
  Warning_Client,
  Debug_Server,
  Debug_Client,
  Debug_Plugin,
  Custom,
  LastBaseEnumItem
}

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.

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
{
  "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 dictictionary 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
{
  "id": "a-random-id-for-this-message", 
  "message": "indigo.variable.updateValue",
  "objectId": 123456
  "parameters": {
    "value": 1234.56
  }
}

You will receive the following error response:

Resulting Error Message
{
  "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 that 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,
  2. objectId,
  3. parameters, and
  4. value

based on the indigo.variable.updateValue message format (we do no validation on the id value passed in, we just 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.

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
{
  "request_body": "this is not valid JSON",
  "error": "invalid JSON"
}

We will just return the entire request body since it isn’t valid JSON and we don’t know what else to do with it.

sandbox/api.txt · Last modified: 2023/09/01 20:58 (external edit)
 

© Perceptive Automation, LLC. · Privacy