Jilia Response Format

The Jilia API exposes servers and devices as URL endpoints, or resources. The result of an HTTP request made against those endpoints is at its basic form, JSON. However, the specific form of that JSON is defined by the Siren hypermedia specification.

Hypermedia is a response format that attempts to completely encapsulate not only the "data" of the response, but also the metadata required to continue interacting with that resource. For Jilia, this means a server response will include not only its associated devices, but also the URL one can call to view the details of a device. When a device is requested, it too will provide both information about its current state as well as endpoints for sending commands to that device.

The goal is to remove the requirement that your application code posses domain specific knowledge just to provide an interface to the user. Instead, the the application is coded against the specification (Siren in this case).

Examples

To better illustrate the concepts above, let's look at some example Jilia responses. If you already have a registered application and activated hub, the Quick Start page provides an interactive walkthrough of authentication and device discovery. For more information on the authentication process, see the documentation page.

In all examples below, the text {token} should be replaced with your authorization token.

Servers

The servers available to your application are retrieved using a simple HTTP GET request to the Jilia root URL. (The -i flag tells cURL to display the response headers as part of the output.)

curl -i -XGET https://api.jilia.io/v1/  -H "Authorization: Bearer {token}"

The above request results in something simliar to the output below.

HTTP/1.1 200 OK
Content-Type: application/vnd.siren+json
Content-Length: 279
Access-Control-Allow-Origin: *
Date: Mon, 05 Oct 2015 21:58:54 GMT
Access-Control-Allow-Headers: origin, x-requested-with, accept, content-type, authorization
Access-Control-Max-Age: 3628800
Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS
Connection: keep-alive{
  "class": [
    "root"
  ],
  "links": [
    {
      "rel": [
        "self"
      ],
      "href": "https://api.jilia.io/v1/"
    },
    {
      "title": "HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6",
      "rel": [
        "http://rels.zettajs.io/peer"
      ],
      "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6"
    },
    {
      "title": "HUB-7e8c1a36-e9a2-4ad2-9130-8f7d9ff1f076",
      "rel": [
        "http://rels.zettajs.io/peer"
      ],
      "href": "https://api.jilia.io/v1/servers/HUB-7e8c1a36-e9a2-4ad2-9130-8f7d9ff1f076"
    }
  ],
  "actions": [
    {
      "name": "query-devices",
      "method": "GET",
      "href": "https://api.jilia.io/v1/",
      "type": "application/x-www-form-urlencoded",
      "fields": [
        {
          "name": "server",
          "type": "text"
        },
        {
          "name": "ql",
          "type": "text"
        }
      ]
    }
  ]
}

Of particular importance are the two items in the "links" array that do not have a "rel" value of "self". The two entries, specifically their "href" values, point to the two available servers.

{
  "title": "HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6",
  "rel": [
    "http://rels.zettajs.io/peer"
  ],
  "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6"
},
{
  "title": "HUB-7e8c1a36-e9a2-4ad2-9130-8f7d9ff1f076",
  "rel": [
    "http://rels.zettajs.io/peer"
  ],
  "href": "https://api.jilia.io/v1/servers/HUB-7e8c1a36-e9a2-4ad2-9130-8f7d9ff1f076"
}

Without any specific domain knowledge, application code can parse for those relationships and their URL resources, and provide the user with an interface for querying those entities for more information. Below is a simplified example (you would need to include the require authorization header) of HTML markup showing the parsed server resources.

<h1>Available Servers</h1>
<ul>
  <li>
    <a href="https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6">HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6</a>
  </li>
  <li>
    <a href="https://api.jilia.io/v1/servers/HUB-7e8c1a36-e9a2-4ad2-9130-8f7d9ff1f076">HUB-7e8c1a36-e9a2-4ad2-9130-8f7d9ff1f076</a>
  </li>
</ul>

Devices

Let's pick one of the servers listed and query it for more information.

curl -XGET https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6 -H "Authorization: Bearer {token}"

The request for more server information returned an object with an "entities" collection.

{
  "class": [
    "server"
  ],
  "properties": {
    "name": "HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6"
  },
  "entities": [
    {
      "class": [
        "device",
        "thermostat"
      ],
      "rel": [
        "http://rels.zettajs.io/device"
      ],
      "properties": {
        "id": "89f5424e-7de2-4303-86bf-75bff9874fff",
        "name": "Thermostat1",
        "current-temperature": 75.54,
        "heating-setpoint": 76,
        "cooling-setpoint": 76,
        "system-mode": "cool",
        "running-mode": "off",
        "drift": 1.5,
        "type": "thermostat",
        "state": "ready"
      },
      "links": [
        {
          "rel": [
            "self",
            "edit"
          ],
          "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/devices/89f5424e-7de2-4303-86bf-75bff9874fff"
        },
        {
          "rel": [
            "http://rels.zettajs.io/type",
            "describedby"
          ],
          "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/meta/thermostat"
        },
        {
          "title": "HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6",
          "rel": [
            "up",
            "http://rels.zettajs.io/server"
          ],
          "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6"
        }
      ]
    },
    {
      "class": [
        "device",
        "contact"
      ],
      "rel": [
        "http://rels.zettajs.io/device"
      ],
      "properties": {
        "id": "43d6c7ec-22bd-4f17-8f2b-34d898d2455e",
        "name": "Door1",
        "contact": "closed",
        "type": "contact"
      },
      "links": [
        {
          "rel": [
            "self",
            "edit"
          ],
          "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/devices/43d6c7ec-22bd-4f17-8f2b-34d898d2455e"
        },
        {
          "rel": [
            "http://rels.zettajs.io/type",
            "describedby"
          ],
          "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/meta/contact"
        },
        {
          "title": "HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6",
          "rel": [
            "up",
            "http://rels.zettajs.io/server"
          ],
          "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6"
        }
      ]
    },
    {
      "class": [
        "device",
        "on-off"
      ],
      "rel": [
        "http://rels.zettajs.io/device"
      ],
      "properties": {
        "id": "07cb007c-704b-4629-b763-e9cd5de6bdaf",
        "name": "Relay1",
        "on-off": "off",
        "type": "on-off",
        "state": "ready"
      },
      "links": [
        {
          "rel": [
            "self",
            "edit"
          ],
          "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/devices/07cb007c-704b-4629-b763-e9cd5de6bdaf"
        },
        {
          "rel": [
            "http://rels.zettajs.io/type",
            "describedby"
          ],
          "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/meta/on-off"
        },
        {
          "title": "HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6",
          "rel": [
            "up",
            "http://rels.zettajs.io/server"
          ],
          "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6"
        }
      ]
    }
  ],
  "actions": [
    {
      "name": "query-devices",
      "method": "GET",
      "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6",
      "type": "application/x-www-form-urlencoded",
      "fields": [
        {
          "name": "ql",
          "type": "text"
        }
      ]
    }
  ],
  "links": [
    {
      "rel": [
        "self"
      ],
      "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6"
    },
    {
      "rel": [
        "http://rels.zettajs.io/metadata"
      ],
      "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/meta"
    },
    {
      "rel": [
        "monitor"
      ],
      "href": "wss://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/events?topic=logs"
    }
  ]
}

Each object in the "entity" collection is an associated device of the targeted server. The current state of the device is exposed by its "properties". For example, the first device listed is a thermostat, which exposes values such as its current temperature, mode, and if it's running.

"properties": {
  "id": "89f5424e-7de2-4303-86bf-75bff9874fff",
  "name": "Thermostat1",
  "current-temperature": 75.54,
  "heating-setpoint": 76,
  "cooling-setpoint": 76,
  "system-mode": "cool",
  "running-mode": "off",
  "drift": 1.5,
  "type": "thermostat",
  "state": "ready"
},

Also included in each entity object is a collection of "links". To view more information about a selected device, we can grab the "href" value for its "self" relationship.

{
  "rel": [
    "self",
    "edit"
  ],
  "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/devices/89f5424e-7de2-4303-86bf-75bff9874fff"
},

And use the URL to request information about the device.

curl -XGET https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/devices/89f5424e-7de2-4303-86bf-75bff9874fff  -H "Authorization: Bearer {token}"
{
  "class": [
    "device",
    "thermostat"
  ],
  "properties": {
    "id": "89f5424e-7de2-4303-86bf-75bff9874fff",
    "name": "Thermostat1",
    "current-temperature": 75.54,
    "heating-setpoint": 76,
    "cooling-setpoint": 76,
    "system-mode": "cool",
    "running-mode": "off",
    "drift": 1.5,
    "type": "thermostat",
    "state": "ready"
  },
  "actions": [
    {
      "class": [
        "transition"
      ],
      "name": "heating-setpoint",
      "method": "POST",
      "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/devices/89f5424e-7de2-4303-86bf-75bff9874fff",
      "fields": [
        {
          "name": "temperature",
          "type": "number"
        },
        {
          "name": "action",
          "type": "hidden",
          "value": "heating-setpoint"
        }
      ]
    },
    {
      "class": [
        "transition"
      ],
      "name": "cooling-setpoint",
      "method": "POST",
      "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/devices/89f5424e-7de2-4303-86bf-75bff9874fff",
      "fields": [
        {
          "name": "temperature",
          "type": "number"
        },
        {
          "name": "action",
          "type": "hidden",
          "value": "cooling-setpoint"
        }
      ]
    },
    {
      "class": [
        "transition"
      ],
      "name": "system-mode",
      "method": "POST",
      "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/devices/89f5424e-7de2-4303-86bf-75bff9874fff",
      "fields": [
        {
          "name": "mode",
          "type": "radio",
          "value": [
            {
              "value": "off"
            },
            {
              "value": "cool"
            },
            {
              "value": "heat"
            }
          ]
        },
        {
          "name": "action",
          "type": "hidden",
          "value": "system-mode"
        }
      ]
    },
    {
      "class": [
        "transition"
      ],
      "name": "drift",
      "method": "POST",
      "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/devices/89f5424e-7de2-4303-86bf-75bff9874fff",
      "fields": [
        {
          "name": "drift",
          "type": "number"
        },
        {
          "name": "action",
          "type": "hidden",
          "value": "drift"
        }
      ]
    }
  ],
  "links": [
    {
      "rel": [
        "self",
        "edit"
      ],
      "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/devices/89f5424e-7de2-4303-86bf-75bff9874fff"
    },
    {
      "title": "HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6",
      "rel": [
        "up",
        "http://rels.zettajs.io/server"
      ],
      "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6"
    },
    {
      "rel": [
        "http://rels.zettajs.io/type",
        "describedby"
      ],
      "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/meta/thermostat"
    },
    {
      "title": "state",
      "rel": [
        "monitor",
        "http://rels.zettajs.io/object-stream"
      ],
      "href": "wss://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/events?topic=thermostat%2F89f5424e-7de2-4303-86bf-75bff9874fff%2Fstate"
    },
    {
      "title": "current-temperature",
      "rel": [
        "monitor",
        "http://rels.zettajs.io/object-stream"
      ],
      "href": "wss://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/events?topic=thermostat%2F89f5424e-7de2-4303-86bf-75bff9874fff%2Fcurrent-temperature"
    },
    {
      "title": "heating-setpoint",
      "rel": [
        "monitor",
        "http://rels.zettajs.io/object-stream"
      ],
      "href": "wss://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/events?topic=thermostat%2F89f5424e-7de2-4303-86bf-75bff9874fff%2Fheating-setpoint"
    },
    {
      "title": "cooling-setpoint",
      "rel": [
        "monitor",
        "http://rels.zettajs.io/object-stream"
      ],
      "href": "wss://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/events?topic=thermostat%2F89f5424e-7de2-4303-86bf-75bff9874fff%2Fcooling-setpoint"
    },
    {
      "title": "system-mode",
      "rel": [
        "monitor",
        "http://rels.zettajs.io/object-stream"
      ],
      "href": "wss://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/events?topic=thermostat%2F89f5424e-7de2-4303-86bf-75bff9874fff%2Fsystem-mode"
    },
    {
      "title": "running-mode",
      "rel": [
        "monitor",
        "http://rels.zettajs.io/object-stream"
      ],
      "href": "wss://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/events?topic=thermostat%2F89f5424e-7de2-4303-86bf-75bff9874fff%2Frunning-mode"
    },
    {
      "title": "drift",
      "rel": [
        "monitor",
        "http://rels.zettajs.io/object-stream"
      ],
      "href": "wss://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/events?topic=thermostat%2F89f5424e-7de2-4303-86bf-75bff9874fff%2Fdrift"
    },
    {
      "title": "logs",
      "rel": [
        "monitor",
        "http://rels.zettajs.io/object-stream"
      ],
      "href": "wss://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/events?topic=thermostat%2F89f5424e-7de2-4303-86bf-75bff9874fff%2Flogs"
    }
  ]
}

Device Commands

In addition to the device properties, which we've already seen, we now have an "actions" collection. Each action defines a command than can be POST'd to the device. A useful command for a thermostat is to set the temperature at which it will switch to heat.

{
  "class": [
    "transition"
  ],
  "name": "heating-setpoint",
  "method": "POST",
  "href": "https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/devices/89f5424e-7de2-4303-86bf-75bff9874fff",
  "fields": [
    {
      "name": "temperature",
      "type": "number"
    },
    {
      "name": "action",
      "type": "hidden",
      "value": "heating-setpoint"
    }
  ]
},

The individual action object provides the information required to create and send a command to the target device.

As a basic example, the application interface might use the above action to create an HTML form. As with the previous example, note that no domain knowlege is required to create the input. The form can be created entirely using the action data. The only piece lacking in this example is the need to include the appropriate authorization header with the HTTP POST.

<strong>heating-setpoint</strong>
<form method="post"
      action="https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/devices/89f5424e-7de2-4303-86bf-75bff9874fff"
  <input type="hidden" value="heating-setpoint"/>
  <p>
    <label>temperature</label>:
    <input type="number" name="temperature" placeholder="temperature"/>
  </p>
  <button type="submit">Submit</button>
</form>

Below is an example of calling the "heating-setpoint" action with cURL. Note the -d argument used to specify the command and its value.

curl -XPOST https://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/devices/89f5424e-7de2-4303-86bf-75bff9874fff/devices/89f5424e-7de2-4303-86bf-75bff9874fff  -H "Authorization: Bearer {token}"  -d "action=heating-setpoint&temperature=68"

Device Monitor with WebSockets

A device might also provide realtime notifications of state changes via WebSockets. To find monitorable properties, filter the "links" collection for those containing a "rel" value of "monitor".

Here is the link object to monitor the thermostat's temperature.

{
  "title": "current-temperature",
  "rel": [
    "monitor",
    "http://rels.zettajs.io/object-stream"
  ],
  "href": "wss://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/events?topic=thermostat%2F89f5424e-7de2-4303-86bf-75bff9874fff%2Fcurrent-temperature"
},

A WebSocket can be opened using the provided "href".

Using the node application wscat, we can monitor for temperature changes with the following command:

wscat -H "Authorization: Bearer {token}" -c "wss://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/events?topic=thermostat%2F89f5424e-7de2-4303-86bf-75bff9874fff%2Fcurrent-temperature"

NOTE: If your WebSocket client does not provide the ability to add additional headers, you can include the authorization token as a query parameter named access_token.

For example, the call to wscat above can be rewritten as:

wscat -c "wss://api.jilia.io/v1/servers/HUB-8f9699a8-1b58-4c79-81a8-1e871caa46a6/events?topic=thermostat%2F89f5424e-7de2-4303-86bf-75bff9874fff%2Fcurrent-temperature&access_token={token}"

When successfully connected, you will begin receiving updates related to the property.

{"topic":"thermostat/89f5424e-7de2-4303-86bf-75bff9874fff/current-temperature","timestamp":1444148968304,"data":76.97}
{"topic":"thermostat/89f5424e-7de2-4303-86bf-75bff9874fff/current-temperature","timestamp":1444148973306,"data":75.58}
{"topic":"thermostat/89f5424e-7de2-4303-86bf-75bff9874fff/current-temperature","timestamp":1444148978309,"data":74.42}
{"topic":"thermostat/89f5424e-7de2-4303-86bf-75bff9874fff/current-temperature","timestamp":1444148983315,"data":75.92}

NOTE: Another important consideration when subscribing to changes to a device's state is that the WebSocket connection will automatically close when it sits inactive for approximately one minute. In order to keep the connection open, you can send a WebSocket PING. The ability to do so depends on the WebSocket client you use. If the client does not provide that functionality, an alternative is to automatically re-connect when the socket unexpectedly closes.

The following example is a node application that relies on the ws WebSocket module. After the socket opens, a PING is sent every thirty seconds to ensure the connection remains open.

var WebSocket = require('ws');

var url = '{wss url}';
var ws = new WebSocket(url);

ws.on('open', function open() {
  setInterval(function(){
      ws.ping(pingData);
  }, 30000);
});

To monitor for state changes across multiple devices, devices types, or even servers, see the Realtime Monitoring documentation page.