Lelylan API

API for the Internet of Things

Light-32px


Overview

Lelylan API let you monitor and control in realtime any physical devices. It's based on three key concepts: devices, physicals and types.

Devices

A Device represents everything you interact with everyday of your life like sensors, lights, thermostats, locks, cameras, multimedia, bikes, cars and much, much more. The Device API lets you create, monitor and control any those objects you interact with.

Physicals

Whereas Devices are a virtual representation, Physicals are the real objects you interact with. To enable the communication between Lelylan and the physical world, the physical device must be able to connect to the Internet and to implement the Physical API.

Types

A Type describes the structure of a device. In its simplest form every Type is defined as the combination of three key elements: properties (what vary during time), functions (what a device can do) and statuses (what a device is in a specific time of its life).

Device API

Modeling the physical world

Light-32px


The device API defines a set of services to create, monitor, control and activate any devices.


GET /devices/:id

Returns extended information for a given device identified from its ID.

Resource URL

GET https://api.lelylan.com/devices/:id

Parameters

id required The ID of the desired device.

Example Request

curl https://api.lelylan.com/devices/<id> \
     -H 'Authorization: Bearer <token>'
var Lelylan = require('lelylan-node')({ token: token });
Lelylan.Device.find('<id>', callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{device}}</div>
  <script>
    function LelylanController($scope, Device) {
      Device.find('<id>').success(function(response) { $scope.device = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
device = lelylan.device('<id>')
oauth.set_access_token()
from lelylan import devices
device = devices.get_dev('<id>')

Example header response

HTTP/1.1 200 OK

Example body response

{
  "uri": "https://api.lelylan.com/devices/5042344b95fc441000000001",
  "id": "5042344b95fc441000000001",
  "name": "Light",
  "type": {
    "id": "5042310470eda60a0000000d",
    "uri": "https://api.lelylan.com/types/5042310470eda60a0000000d"
  },
  "categories": ["lights"],
  "physical": { "uri": "https://arduino.house.com/5042205c70eda61" },
  "pending": false,
  "properties": [{
    "uri": "https://api.lelylan.com/properties/518be5a700045e1521000001",
    "id": "518be5a700045e1521000001",
    "value": "off",
    "expected": "off",
    "pending": false,
    "accepted": [
      { "key": "on", "value": "On" },
      { "key": "off", "value": "Off" }
    ]
  }, {
    "uri": "https://api.lelylan.com/properties/50420c3a267ff51300000001",
    "id": "50420c3a267ff51300000001",
    "value": "100",
    "expected": "100",
    "pending": false,
    "accepted": []
  }],
  "owner": {
    "id": "5036227a4f1b030200000001",
    "uri": "https://api.lelylan.com/people/5036227a4f1b030200000001"
  },
  "maker": {
    "id": "5036227a4f1b030200000001",
    "uri": "https://api.lelylan.com/people/5036227a4f1b030200000001"
  },
  "activated": true,
  "created_at": "2013-05-13T14:22:11Z",
  "updated_at": "2013-05-13T16:41:57Z",
  "updated_from": "Alice"
}

Body Response explained

uri Device URI.
id Device ID.
name Device name.
type Device typeID and URI.
physical Connected physical device URI.
owner Device owner having full control on the device.
maker Device creator.
activated Device activation status<./td>
categories Device categories.
properties Device properties.
» property.uri. Property URI
» property.id. Property ID
» property.value Property value. Learn more about.
» property.expected Expected property value. Learn more about.
» property.pending Property pending status. Learn more about.
» property.accepted List of accepted values. Learn more about.

GET /devices/:id/privates

Returns private information for a given device identified from its ID.

notice To access this service you must use the privates scope.

Resource URL

GET https://api.lelylan.com/devices/:id/privates

Parameters

id required The ID of the desired device.

Example Request

curl https://api.lelylan.com/devices/<id>/privates \
    -H 'Authorization: Bearer <token>'
var Lelylan = require('lelylan-node')({ token: token });
Lelylan.Device.privates('<id>', callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{device}}</div>
  <script>
    function LelylanController($scope, Device) {
      Device.privates('<id>').success(function(response) { $scope.device = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
device = lelylan.device_privates('<id>');
oauth.set_access_token()
from lelylan import devices
device = devices.get_dev_privates('<id>')

Example header response

HTTP/1.1 200 OK

Example body response

{
  "uri": "https://api.lelylan.com/devices/5042344b95fc441000000001",
  "id": "5042344b95fc441000000001",
  "name": "Closet dimmer",
  "secret": "fkJVeURdtONfXw40wB+K0xI2Zxxxxxxxx",
  "activation_code": "3a9e8732e30c7c06031ad28fd5310425xxxxxxxx",
}

Body Response explained

uri Device URI.
id Device ID.
name Device name.
secret Unique device secret used to secure the communication with the physical world.
activation_code Device activation code used to claim the device ownership.

GET /devices

Returns a list of owned devices.

Resource URL

GET https://api.lelylan.com/devices

Parameters

name optional Device name to search.
type optional Type ID to search.
physical optional Physical device URI to search.
properties[id] optional Property ID to search.
properties[value] optional Property value to search. Learn more about.
properties[expected] optional Expected value to search. Learn more about.
properties[pending] optional Pending property status to search. Learn more about. Valid values: true, false.
start optional ID of the last fetched record. Learn more about pagination.
per optional default to '25' Number of resources per page.

Example Request

curl https://api.lelylan.com/devices?name=light \
     -H 'Authorization: Bearer <token>'
var Lelylan = require('lelylan-node')({ token: token });
Lelylan.Device.all({ 'name': 'light' }, callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">
    <div ng-repeat="device in devices">{{device}}</div>
  </div>
  <script>
    function LelylanController($scope, Device) {
      Device.all({ name: 'light' }).success(function(response) { $scope.devices = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
devices = lelylan.devices(name: 'light')
oauth.set_access_token()
from lelylan import devices
devices = devices.get_all_dev({ name: 'light' })

Example header response

HTTP/1.1 200 OK

Example body response

[
  { # device representation },
  { # device representation },
  ...
]

See device representation for body response.


POST /devices

Create a device and returns extended information for it.

Resource URL

POST  https://api.lelylan.com/devices

Parameters

name required Device name.
type.id required Type ID (check out available types or create a new one).
physical.uri optional Physical device URI. Learn more about.

Example Request

curl -X POST https://api.lelylan.com/devices \
     -H 'Authorization: Bearer <token>' \
     -H 'Content-Type: application/json' \
     -d '{
           "name": "Closet dimmer",
           "type": { "id": "<id>" },
           "physical": { "uri": "<physical-device-uri>" }
         }'
var Lelylan = require('lelylan-node')({ token: token });
var params = {
  name: 'Closet dimmer',
  type: { id: '<id>' },
  physical: { uri: '<physical-device-uri>' } };

device = Lelylan.Device.create(params, callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{device}}</div>
  <script>
    function LelylanController($scope, Device) {
      var params = {
        name: 'Closet dimmer',
        type: { id: '<id>' },
        physical: { uri: '<physical-device-uri>' } };

      Device.create(params).success(function(response) { $scope.device = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
params = {
  name: 'Closet dimmer',
  type: { id: '<id>' },
  physical: { uri: '<physical-device-uri>' } };

device = lelylan.create_device(params)
oauth.set_access_token()
params = {
  name: 'Closet dimmer',
  type: { id: '<id>' },
  physical: { uri: '<physical-device-uri>' } };

from lelylan import devices
device = devices.create_dev(params)

Example header response

HTTP/1.1 201 Created
Location: https://api.lelylan.com/devices/5042344b95fc441000000001

Example body response

See device representation for body response.


PUT /devices/:id

Update a device identified from its ID and returns extended information for it.

notice The device type can't be updated.

Resource URL

PUT  https://api.lelylan.com/devices/:id

Parameters

id required The ID of the desired device.
name optional Device name.
physical.uri optional Physical device URI. Learn more about

Example Request

curl -X PUT https://api.lelylan.com/devices/<id> \
     -H 'Authorization: Bearer <token>' \
     -H 'Content-Type: application/json' \
     -d '{ "name": "Zigbee closet dimmer" }'
var Lelylan = require('lelylan-node')({ token: token });
var params = { name: 'Zigbee closet dimmer' };

Lelylan.Device.update('<id>', params, callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{device}}</div>
  <script>
    function LelylanController($scope, Device) {
      var params = { name: 'Zigbee closet dimmer' };
      Device.update('<id>', params).success(function(response) { $scope.device = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
params = { name: 'Zigbee closet dimmer' }

device = lelylan.update_device('<id>', params)
oauth.set_access_token()
params = { name: 'Zigbee closet dimmer' }

from lelylan import devices
device = devices.update_dev('<id>', params)

Example header response

HTTP/1.1 200 OK

Example body response

See device representation for body response.


PUT /devices/:id/properties

Update properties on a device identified from its ID and returns extended representation for it. If a physical device is connected, Lelylan forward the changes to the physical world.

Resource URL

PUT https://api.lelylan.com/devices/:id/properties

Parameters

id required The ID of the desired device.
properties required Array of properties to change.
» property.id required Property ID.
» property.value optional Property value. Learn more about.
» property.expected optional Expected property value. Learn more about.
» property.pending optional Property pending status. Learn more about. Valid values: true, false.
» property.accepted optional List of accepted values. Learn more about. Valid values: JSON objects.

Example Requests

Set the device status to on and the device intensity to 50%.

curl -X PUT https://api.lelylan.com/devices/<id>/properties \
     -H 'Authorization: Bearer <token>' \
     -H 'Content-Type: application/json' \
     -d '{
           "properties": [{
             "id": "<status>",
             "value": "on"
           },{
             "id": "<intensity>",
             "value": "50" }
           ]
         }'
var Lelylan = require('lelylan-node')({ token: token });
var params = {
  properties:
    [ { id: '<status>',
        value: 'on' },
      { id: '<intensity>',
        value: '50' } ] };

Lelylan.Device.properties('<id>', params, callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{device}}</div>
  <script>
    function LelylanController($scope, Device) {
    var params = {
      properties:
        [ { id: '<status>', value: 'on' },
          { id: '<intensity>', value: '50' } ] };

      Device.properties('<id>', params).success(function(response) { $scope.device = response });
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
params = {
  properties:
    [ { id: '<status>',
        value: 'on' },
      { id: '<intensity>',
        value: '50' } ] }

device = lelylan.device_properties('<id>', params)
oauth.set_access_token()
params = {
  properties:
    [ { id: '<status>',
        value: 'on' },
      { id: '<intensity>',
        value: '50' } ] }

from lelylan import devices
device = devices.update_dev_prop('<id>', params)

Example body response

See device representation for body response.


DELETE /devices/:id

Delete a device identified from its ID and return extended information for it.

Resource URL

DELETE https://api.lelylan.com/devices/:id

Parameters

id required The ID of the desired device.

Example Request

curl -X DELETE https://api.lelylan.com/devices/<id> \
     -H 'Authorization: Bearer <token>'
var Lelylan = require('lelylan-node')({ token: token });
Lelylan.Device.delete('<id>', callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{device}}</div>
  <script>
    function LelylanController($scope, Device) {
      Device.delete('<id>').success(function(response) { $scope.device = response })
    };
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
device = lelylan.delete_device('<id>')
oauth.set_access_token()
from lelylan import devices
device = devices.delete_dev('<id>')

Example header response

HTTP/1.1 200 OK

Example body response

See device representation for body response.


POST /activations

Activate a device through its activation code and returns extended information for it.

notice When you create a device you own it. To let others own your devices deactivate them first.

Resource URL

POST  https://api.lelylan.com/activations

Parameters

activation_code required Device activation code.

Example Request

curl -X POST https://api.lelylan.com/activations \
     -H 'Authorization: Bearer <token>' \
     -H 'Content-Type: application/json' \
     -d '{ "activation_code": "<activation-code>" }'
var Lelylan = require('lelylan-node')({ token: token });
var params = { activation_code: '<activation-code>' };
Lelylan.Device.activate(params, callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{activated}}</div>
  <script>
    function LelylanController($scope, Activation) {
      var params = { activation_code: '<activation-code>' };
      Activation.activate(params).success(function(response) { $scope.activated = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
params = { activation_code: '<activation-code>' }
device = lelylan.activate_device(params)
oauth.set_access_token()
params = { activation_code: '<activation-code>' }

from lelylan import activations
device = activations.activate_dev(params)

Example header response

HTTP/1.1 201 Created
Location: https://api.lelylan.com/devices/5042344b95fc441000000001

See device representation for body response.

What if the device is already activated?

Trying to activate a device already activate will return a not valid response.

HTTP/1.1 422 Not Valid

{ "status": "422", "method": "POST", "request": "https://api.lelylan.com/activations", "error": { "code": "notifications.resource.already_activated", "description": "Resource already activated", "body": { "activation_code": "3cd6360a163b112c5eb910967c5866f77f019694" } } }

DELETE /activations/:activation-code

Deactivate a device identified from its activation code and returns extended information for it. When a device is deactivated, anyone owning the activation code can claim the device ownership. Until the claim happents, the previous owner remains.

Resource URL

DELETE https://api.lelylan.com/activations/:activation-code

Parameters

activation_code required The activation code of the desired device.

Example Request

curl -X DELETE https://api.lelylan.com/activations/<activation-code> \
     -H 'Authorization: Bearer <token>'
var Lelylan = require('lelylan-node')({ token: token });
Lelylan.Device.deactivate('<activation-code>', callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{deactivated}}</div>
  <script>
    function LelylanController($scope, Deactivation) {
      var params = { activation_code: '<activation-code>' };
      Activation.deactivate('1').success(function(response) { $scope.deactivated = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
device = lelylan.deactivate_device('<activation-code>')
oauth.set_access_token()
params = { activation_code: '<activation-code>' }

from lelylan import activations
device = activations.deactivate_dev(params)

Example header response

HTTP/1.1 200 OK

Example body response

See device representation for body response.

Physical API

Connecting the physical world

Light-32px


What is a Physical Device

Physical devices are the objects you touch and interact with everyday of your life like lights, bikes, cars and more. Physical devices are characterized only by their properties. This way physical devices are easy to build, still giving the chance to extend their functionalities by adding new services (e.g. functions, rules engines, sceneries, etc.) on top of Lelylan.


How does it work

Any request sent to the Lelylan API is forwarded to the physical device, which is in charge of applying the desired changes and of sending back a confirmation message when those are made. The physical device also needs to send to communicate to Lelylan when its status changes (for example somebody turns a light on using a physical switch).


Any Hardware

To communicate with Lelylan a physical device needs an hardware with Internet connectivity. There are many solutions are available, varying on computational power, wireless capability and budget.

Model Description
Arduino Open-source electronics prototyping platform.
Raspberry Pi Credit-card sized computer as ARM GNU/Linux box for $25.
Raspberry Zero A tiny Raspberry Pi for $5
C.H.I.P. The World's First $9 Computer with integrated Bluetooth and Wifi
ESP8266 SOC with integrated TCP/IP protocol stack to get WiFi access.
Electric imp A complete and cheap solution to connect devices to the internet, wirelessly.
Spark Core A tiny Wi-Fi development board to create internet-connected hardware.
CC3200 Evaluation development platform for the CC3200 wireless microcontroller.
Netduino Open-source electronics platform using the .NET Micro Framework.
TinyDuino Arduino compatible boards smaller than a quarter.
mbed Rapid Prototyping for Microcontrollers.
udoo Android, Linux and Arduino in a tiny single-board computer.
Pinoccio A wireless microcontroller to connect anything to the web.
Tessel Hardware development for software developers.
openPICUS Platform to control and communication for your electronic devices through the web.
Libelium Hardware technology for the implementation of wireless sensor networks.
Beagle Board Open OMAP 4 mobile software development platform.
Cubieboard A small, high-performance arm box.
The Rascal Small computer to monitor and control the world remotely.


notice Add any hardware you find essential for the list to be completed.


MQTT

MQTT is a publish/subscribe, extremely simple and lightweight messaging protocol, designed for constrained devices and low-bandwidth, high-latency or unreliable networks. The Lelylan MQTT Server is ready to be used way to interact with Lelylan from any MQTT client. It provides full API interaction, as well as push notifications and subscriptions. It gives you a MQTT 3.1 compliant solution supporting QoS 0 and QoS 1 levels on message delivering.

MQTT Broker IP Port Secure
178.62.108.47 1883 No
178.62.108.47 8883 Yes

Setup

To connet your hardware with MQTT you first need to create a new device. Open Lelylan Dashboard and create a new device by following 3 steps (first you need to sign up for free).


Set a meaningful name (e.g. bedroom light).

Step-1


Choose the device type (for this tutorial choose Basic Light).

Step-2


Choose "Connect with MQTT" as connectivity option.

Step-3


Once the device is created click on settings (under the device name) and get the device info: ID (unique device identifier - MQTT username), Secret (device secret code - MQTT password), Type (link to the type definition useful to check the device properties).

Step-4


Connecting to Lelylan

Lelylan MQTT accepts connections from any MQTT client supporting the MQTT 3.1 specs. During the connection phase the client needs to set the device id as username and the device secret as password. Once the connection is successful, you need to implement two services: one to receive data form and one send data to Lelylan.

Receive data (devices/:id/get)

The MQTT client (hardware) receives all messages coming from Lelylan (e.g API, dashboard) through the devices/:id/get topic. Every message is made up from a list of properties with the following structure.

properties Array of properties (received from your hardware).
» property.id Property ID.
» property.value Property value.

Send data (devices/:id/set)

When a physical device updates its status through a physical interaction (e.g turn on a light using the physical switch), Lelylan must be notified by publishing a message to the topic devices/:id/set with the list of the updated properties. Every sent message is made up from a list of properties with the following structure.

properties optional Array of properties (sent from your hardware).
» property.id required Property ID.
» property.value required Desired property value. Learn more about.
» property.expected optional Expected property value. Learn more about.
» property.pending optional Property pending status. Learn more about. Valid values: true, false.

Async physical flow

When sending a message to the physical world, Lelylan expects a feedback form it. Suppose a request is sent to turn a light on through the API. Your hardware, should send back to Lelylan a confirmation message abut the light physically turning on (when this happen). From the moment the request is sent to the moment the confirmation message is received, the device is pending.

To close the pending status through MQTT and tell Lelylan that the requested changes were applied, the physical device must publish a message to the out topic /devices/:id/set with the new property values.


Code Examples

/* -------------------------------------------------------
 * Install https://github.com/andreareginato/pubsubclient
 * It sets the max packet size to 512 bytes.
 * ------------------------------------------------------- */

#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>

// Update these with values suitable for your network.
byte mac[] = { 0xA0, 0xA0, 0xBA, 0xAC, 0xAE, 0x12 };

/* device credentials */
char* deviceId = "<DEVICE-ID>";         // set your device id (will be the MQTT client username)
char* deviceSecret = "<DEVICE-SECRET>"; // set your device secret (will be the MQTT client password)
char* clientId = "<CLIENT-ID>";         // MQTT client id (random, max 23 bytes)

/* device topics */
char* outTopic = "devices/<DEVICE-ID>/set"; // where physical updates are published
char* inTopic = "devices/<DEVICE-ID>/get";  // where lelylan updates are received

/* Access settings */
byte server[] = { 178, 62, 108, 47 }; // MQTT server address

/* sample payload published to lelylan */
/* (to get the desired property-id go to the device settings and click on the type link) */
char* payload = "{\"properties\":[{ \"id\": \"<PROPERTY-ID>\", \"value\": \"<VALUE>\" }]}";

/* Ethernet configuration */
byte mac[] = { 0xA0, 0xA0, 0xBA, 0xAC, 0xAE, 0x12 };
EthernetClient ethClient;

/* MQTT communication */
void callback(char* topic, byte* payload, unsigned int length); // subscription callback
PubSubClient client(server, 1883, callback, ethClient);         // mqtt client

/* arduino setup */
void setup() {
  Serial.begin(9600);
  delay(500);

  Ethernet.begin(mac);
  Serial.print("Connected with IP: ");
  Serial.println(Ethernet.localIP());

  lelylanConnection(); // MQTT server connection
}

/* arduino loop */
void loop() {
  lelylanConnection();
}

/* MQTT server connection */
void lelylanConnection() {
  // add reconnection logics
  if (!client.connected()) {
    // connection to MQTT server
    if (client.connect(clientId, deviceId, deviceSecret)) {
      Serial.print("[OK] Connected with MQTT");
      lelylanSubscribe();  // topic subscription
      lelylanPublish();    // topic publishing
    }
  }

  client.loop();
}

/* MQTT publish */
void lelylanPublish() {
  client.publish(outTopic, payload);
}

/* MQTT subscribe */
void lelylanSubscribe() {
  client.subscribe(inTopic);
}

// debug to show the received message
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Receiving subscribed message");
  Serial.println(topic);
  Serial.write(payload, length);
}
// ------------------------------
// Install first the mqtt client
// $ npm install mqtt
// ------------------------------

// device credentials
var device = { id: '<device-id>', secret: '<device-secret>' }

// device topics
var in_topic  = 'devices/' + device.id + '/get'   // receiving messages
  , out_topic = 'devices/' + device.id + '/set';  // publishing messages

// connection settings
var mqtt = require('mqtt')
  , host = '178.62.108.47'
  , port = '1883'

// client settings
var settings = {
  username: device.id,     // device.id as client username
  password: device.secret  // device.secret as client password
}

// client connection
var client = mqtt.createClient(port, host, settings);
client.on('connect', function() {
  console.log('Client successfully connected');

  // subscribe for incoming messages
  client.subscribe(in_topic);
  client.on('message', function(topic, message) {
    console.log('received', topic, message);
  });

  // publish a message
  var payload = { properties: [{ id: '<property-id>', value: '<value>' }] };
  client.publish(out_topic, JSON.stringify(payload));
});
# ------------------------------
# Install first the mqtt client
# $ gem install mqtt
# ------------------------------

require 'mqtt'
require 'uri'
require 'json'

# device credentials
device = { id: '<device-id>', secret: '<device-secret>' }

# device topics
in_topic  = 'devices/' + device[:id] + '/get' # receiving messages
out_topic = 'devices/' + device[:id] + '/set' # published messages

# connection parameters
settings = {
  remote_host: '178.62.108.47',
  remote_port: '1883',
  username: device[:id],
  password: device[:secret]
}

# client connection
MQTT::Client.connect(settings) do |client|
  Thread.new do
    # subscribe for incoming messages
    client.get(in_topic) do |topic, message|
      puts "Received #{topic}: #{message}"
    end
  end

  # publish a message
  payload = { properties: [{ id: '<property-id>', value: '<value>' }] };
  client.publish(out_topic, payload.to_json)
  puts "Published #{out_topic}: #{payload}"
  loop { sleep 0.1 } # keep listening
end
# --------------------------
# Install first mosquitto
# $ pip install mosquitto
# --------------------------

import mosquitto, json

# event callbacks
def on_connect(mosq, obj, rc):
    print("rc: " + str(rc))

def on_message(mosq, obj, msg):
    print(msg.topic + " " + str(msg.qos) + " " + str(msg.payload))

def on_publish(mosq, obj, mid):
    print("mid: " + str(mid))

def on_subscribe(mosq, obj, mid, granted_qos):
    print("Subscribed: " + str(mid) + " " + str(granted_qos))

# create client
client = mosquitto.Mosquitto()

# assign event callbacks
client.on_message = on_message
client.on_connect = on_connect
client.on_publish = on_publish
client.on_subscribe = on_subscribe

# device credentials
device_id     = '<device-id>'
device_secret = '<device-secret>'

# device topics
in_topic  = 'devices/' + device_id + '/get'  # receiving messages
out_topic = 'devices/' + device_id + '/set'  # publishing messages

# client connection
client.username_pw_set(device_id, device_secret)
client.connect('178.62.108.47', '1883')

# subscribe (with QoS level 0)
client.subscribe(in_topic, 0)

# publish a message
payload = { 'properties': [{ 'id': '<property-id>', 'value': '<value>' }] };
client.publish(out_topic, json.dumps(payload))

# Continue the network loop, exit when an error occurs
rc = 0
while rc == 0:
    rc = client.loop()
print("rc: " + str(rc))

You are done. Open the Dashboard and control your connected device from the web.

notice Check out the Raspberry Pi tutorial for a pratical use of MQTT.


URI Flow

If MQTT isn't available use the URI Flow. It makes the physical device accessible through a Public IP Address and if you do not have one, you can create a tunnel with solutions like ngrock, Forward or Pagekite.


Setup

To connet your hardware with MQTT you first need to create a new device. Open Lelylan Dashboard and create a new device by following 3 simple steps (if you are new to Lelylan, you can sign up for free).


Set a meaningful name (e.g. bedroom light).

Step-1


Choose the device type (for this tutorial choose Basic Light).

Step-2


Choose "Custom URI" as connectivity option. and set the physical device URI to forward requests to the physical device.


Direct

Once the device is created click on settings (under the device name) and get the device info: ID (unique device identifier - MQTT username), Secret (device secret code - MQTT password), Type (link to the type definition useful to check the device properties).

Step-4


How to Receive Data

When a user updates the device properties using Lelylan API a request is forwarded to the physical device. Follow a request example the physical device should be able to handle.


Received Request

PUT http://pi.connectedhome.com/:physical-id

Received Headers

X-Physical-Secret The device secret (checked by the physical device).

Received Params

properties Array of properties to change in the physical world.
» property.id Property ID.
» property.value Property value to set.

Received Request Example

Follows a request example the physical device needs to handle.

curl -X PUT <physical-device-uri> \
     -H 'Content-Type: application/json' \
     -H 'X-Physical-Secret: <secret>' \
     -d '{ "properties": [{ "id": "<status-id>", "value": "on" }] }'
var Lelylan = require('lelylan-node')();
var uri = '<physical-device-uri>'; // physical device URI you want to interact with
var secret = '<secret>'; // see dev.lelylan.com/api/devices#get-a-device-private-info
var params = { 'properties': [{ 'id': '<status-id>', 'value': 'on' }] };

Lelylan.Physical.properties(uri, secret, params, callback);
<html ng-app="lelylan">
<body>
  <div ng-controller="LelylanController">{{type}}</div>
  <script>
    function LelylanController($scope, $resource) {
      var uri = '<physical-device-uri>'; // physical device URI you want to interact with
      var secret = '<secret>';  // see dev.lelylan.com/api/devices#get-a-device-private-info
      var params = { 'properties': [{ 'id': '<status>', 'value': 'on' }] };

      var action = { update: { method: 'PUT', headers: { 'X-Physical-Secret': secret } } };
      var Physical = $resource(uri, {}, action);

      $scope.physical = new Physical(params);
      $scope.physical.$update();
    }
  </script>

  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular.min.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular-resource.min.js"></script>
</body>
</html>
lelylan = Lelylan::Client.new
uri = '<physical-device-uri>' # physical device URI you want to interact with
secret = '<secret>'; # see dev.lelylan.com/api/devices#get-a-device-private-info
params = { properties: [{ id: '<status>', value: 'on' }] }

response = lelylan.physical_properties(uri, secret, params)
uri = '<physical-device-uri>' # physical device URI you want to interact with
secret = '<secret>'; # see dev.lelylan.com/api/devices#get-a-device-private-info
params = { properties: [{ id: '<status>', value: 'on' }] }

from lelylan import physical
response = physical.receive_update(uri, secret, params)

Response Example

When receiving a request the physical device should respond with the 202 Accepted status code. This lets Lelylan know that the request has been accepted and it's processing (it's now pending).

HTTP/1.1 202 Accepted
{ "status": "202" }

How to Send Data

When a physical device applies the request (apply the changes to the real world object), a request with the list of updated properties is sent back to Lelylan through the properties API.


Sent Request Example

PUT http://api.lelylan.com/devices/:device-id/properties

Sent Headers

X-Physical-Secret required The device secret. Lelylan needs it to reject not valid requests.

Sent Params

properties optional Array of properties to change in the physical world.
» property.id required Property ID.
» property.value required Desired property value. Learn more about.
» property.expected optional Expected property value. Learn more about.
» property.pending optional Property pending status. Learn more about. Valid values: true, false.

Example Request

curl -X PUT http://api.lelylan.com/devices/<id>/properties \
    -H 'X-Physical-Secret: <secret>' \
    -H 'Content-Type: application/json' \
    -d '{ "properties": [{ "id": "<status>", "value": "on" }] }'
var Lelylan = require('lelylan-node')();
var uri = 'http://api.lelylan.com/devices/<id>/properties'; // device property API
var secret = '<secret>'; // see dev.lelylan.com/api/devices#get-a-device-private-info
var params = { 'properties': [{ 'id': '<status>', 'value': 'on' }] };

Lelylan.Physical.properties(uri, secret, params, callback);
<html ng-app="lelylan">
<body>
  <div ng-controller="LelylanController">{{type}}</div>
  <script>
    function LelylanController($scope, $resource) {
      var uri = 'http://api.lelylan.com/devices/<id>/properties'; // device property API
      var secret = '<device-secret>'; //see dev.lelylan.com/api/devices#get-a-device-private-info
      var params = { 'properties': [{ 'id': '<status>', 'value': 'on' }] };

      var action = { update: { method: 'PUT', headers: { 'X-Physical-Secret': secret } } };
      var Physical = $resource(uri, {}, action);

      $scope.physical = new Physical(params);
      $scope.physical.$update();
    }
  </script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular.min.js"/></script>
  <script src="//cdn.lelylan.com/angular/0.1/lelylan.min.js"/></script>
</body>
</html>
lelylan = Lelylan::Client.new
uri = 'http://api.lelylan.com/devices/<id>/properties' # device property API
secret = '<device-secret>' # see dev.lelylan.com/api/devices#get-a-device-private-info
params = { properties: [{ id: '<status>', value: 'on' }] }
response = lelylan.physical_properties(uri, secret, params)
uri = 'http://api.lelylan.com/devices/<id>/properties' # device property API
secret = '<device-secret>' # see dev.lelylan.com/api/devices#get-a-device-private-info
params = { properties: [{ id: '<status>', value: 'on', pending: false }] }

from lelylan import physical
response = physical.receive_update(uri, secret, params)


Accessible services

For security reasons a physical device has access to a limited set of Lelylan API. All of these needs to set the X-Physical-Secret to be authenticated and authorized.


Control your device

You are done. Open the Dashboard and control your connected device from the web.

notice Check out the Electric Imp Tutorial for a pratical use of MQTT.

Type API

Structuring the physical world

Light-32px


Overview

A type describes the structure of a device. In its simplest form every type can be defined as the combination of three key elements.

  • Properties (what vary during time)
  • Functions (what a device can do)
  • Statuses (what a device is in a specific time of its life)

Follow a sample type structure for a basic light.

Propertiesstatus, intensity
Functionsturn on, turn off, set intensity
Statuseson, off, turning on, turning off, setting intensity

Do I need to learn the Type API?

No, you don't. The Types Dashboard gives you everything you need to create new and browse existing types. This is all you need to get started. But if you want to fully understand how a type is structured and how to use the Type API, this is the right place where to start.


Types


GET /types/:id

Returns extended information for a given public type identified from its ID.

Resource URL

GET http://api.lelylan.com/types/:id

Parameters

id required The ID of the desired type.

Example Request

curl http://api.lelylan.com/types/<id>
var Lelylan = require('lelylan-node')();
Lelylan.Type.find('<id>', callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{type}}</div>
  <script>
    function LelylanController($scope, Type) {
      Type.find('<ID>').success(function(response) { $scope.type = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new
type = lelylan.type('<id>')
from lelylan import types
type = types.get_type('<id>')

Example header response

HTTP/1.1 200 OK

Example body response

{
  "id": "5042234270eda61200000006",
  "uri": "http://api.lelylan.com/types/5042234270eda61200000006",
  "name": "Connected Light",
  "description": "Define a *basic* light structure",
  "owner": {
    "id": "5036227a4f1b030200000001",
    "uri": "http://api.lelylan.com/people/5036227a4f1b030200000001"
  },
  "created_at": "2012-09-01T15:01:22Z",
  "updated_at": "2012-09-01T15:01:22Z",
  "categories": ["lights"],
  "properties": [{
    "name": "Status",
    "id": "50420c3a267ff51300000001",
    "uri": "http://api.lelylan.com/properties/50420c3a267ff51300000001",
    "type": "text",
    "default": "off",
    "accepted": [
      { "key": "on", "value": "On" },
      { "key": "off", "value": "Off" }
    ]
  }, {
    "name": "Intensity",
    "id": "518be8b100045e99cc000004",
    "uri": "http://api.lelylan.com/properties/518be8b100045e99cc000004",
    "type": "range",
    "range": { "min": "0", "max": "100", "step": "1" }
    "default": "50",
    "accepted": null,
  }],
  "functions": [{
    "name": "Turn on",
    "id": "5042205c70eda61000000003",
    "uri": "http://api.lelylan.com/functions/5042205c70eda61000000003",
    "properties": [{
      "id": "50420c3a267ff51300000001",
      "uri": "http://api.lelylan.com/properties/50420c3a267ff51300000001",
      "expected": "on",
      "pending": true
    }]
  }, {
    "name": "Turn off",
    "uri": "http://api.lelylan.com/functions/5042209370eda61000000004",
    "id": "5042209370eda61000000004",
    "properties": [{
      "id": "50420c3a267ff51300000001",
      "uri": "http://api.lelylan.com/properties/50420c3a267ff51300000001",
      "expected": "off",
      "pending": true
    }]
  }, {
    "name": "Set intensity",
    "uri": "http://api.lelylan.com/functions/504220c470eda60d00000005",
    "id": "504220c470eda60d00000005",
    "properties": [{
      "uri": "http://api.lelylan.com/properties/518be8b100045e99cc000004",
      "id": "518be8b100045e99cc000004",
      "expected": null,
      "pending": true
    }]
  }],
  "statuses": [{
    "name": "Light is On",
    "id": "518bea2500045e99cc000009",
    "uri": "http://api.lelylan.com/statuses/518bea2500045e99cc000009",
    "function": {
      "id": "5042209370eda61000000004",
      "uri": "http://api.lelylan.com/functions/5042209370eda61000000004"
    },
    "properties": [{
      "id": "50420c3a267ff51300000001",
      "uri": "http://api.lelylan.com/properties/50420c3a267ff51300000001",
      "pending": false,
      "values": ["on"],
      "range": null
    }],
  }, {
    "name": "Light is Off",
    "id": "518bea3100045e99cc00000b",
    "uri": "http://api.lelylan.com/statuses/518bea3100045e99cc00000b",
    "function": {
      "id": "5042205c70eda61000000003",
      "uri": "http://api.lelylan.com/functions/5042205c70eda61000000003"
    },
    "properties": [{
      "id": "50420c3a267ff51300000001",
      "uri": "http://api.lelylan.com/properties/50420c3a267ff51300000001",
      "pending": false,
      "values": ["off"],
      "range": null
    }],
  }]
}

Body Response explained

uri Type URI
id Type ID
name Type name
description Type description
owner Type creator
categories List of type categories
properties Learn more about properties
functions Learn more about functions
statuses Learn more about statuses

GET /types

Returns a list of owned (and public) types.

Resource URL

# owned types
GET http://api.lelylan.com/types

# all types GET http://api.lelylan.com/types/public

Parameters

name optional Type name to search.
start optional ID of the last fetched record. Learn more about pagination.
per optional default to '25' Number of resources per page.

Example Request

curl http://api.lelylan.com/types \
     -H 'Authorization: Bearer <token>'
var Lelylan = require('lelylan-node')({ token: token });
Lelylan.Type.all({}, callback); // owned resources

var Lelylan = require('lelylan-node')();
Lelylan.Type.public({}, callback); // all existing resources
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">
    <div ng-repeat="type in owned">{{type}}</div>
    <div ng-repeat="type in public">{{type}}</div>
  </div>
  <script>
    function LelylanController($scope, Type) {
      Type.all({ name: 'light' }).success(function(response) { $scope.owned = response })
      Type.public({ name: 'light' }).success(function(response) { $scope.public = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
types = lelylan.types # owned resources

lelylan = Lelylan::Client.new
types = lelylan.public_types # all existing resources
oauth.set_access_token()
from lelylan import types
types = types.get_owned_type() # owned resources

from lelylan import types
types = types.get_all_type() # all existing resources

Example header response

HTTP/1.1 200 OK

Example body response

[
  {
    "uri": "http://api.lelylan.com/types/5042223470eda60d00000005",
    "id": "5042223470eda60d00000005",
    "name": "Light",
    "categories": ["lights"],
    "created_at": "2012-09-01T14:56:52Z",
    "updated_at": "2012-09-01T14:56:52Z"
  }, {
    "uri": "http://api.lelylan.com/types/5042229270eda61200000005",
    "id": "5042229270eda61200000005",
    "name": "Basic Light",
    "categories": ["lights"],
    "created_at": "2012-09-01T14:58:26Z",
    "updated_at": "2012-09-01T14:58:26Z"
  }, ...
]

notice Due to the type complex structure when you get a list of types each of them is returned with a simplified JSON representation. Having the name, the ID and the category will be more than enough.


POST /types

Create a type and returns extended information for it.

Resource URL

POST http://api.lelylan.com/types

Parameters

name required Type name.
description optional Type description.
categories optional List of categories.
properties optional List of property IDs.
functions optional List of function IDs.
statuses optional List of status IDs.

Example Request

curl -X POST http://api.lelylan.com/types \
     -H 'Authorization: Bearer <token>' \
     -H 'Content-Type: application/json' \
     -d '{
           "name": "Light",
           "categories": ["lights"],
           "properties": [ "<status>", "<intensity>" ],
           "functions": [ "<turn-on>", "<turn-off>", "<set-intensity>" ],
           "statuses": [ "<light-on>", "<light-off>" ]
         }'
var Lelylan = require('lelylan-node')({ token: token });
var params = {
  name: 'Light',
  categories: [ 'lights' ],
  properties: [ '<status>', '<intensity>' ],
  functions: [ '<turn-on>', '<turn-off>', '<set-intensity>' ],
  statuses: [ '<light-on>', '<light-off>' ] };

Lelylan.Type.create(params, callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{type}}</div>
  <script>
    function LelylanController($scope, Type) {
      var params = {
        name: 'Light',
        categories: [ 'light' ],
        properties: [ '<status-id>', '<intensity-id>' ],
        functions: [ '<turn-on-id>', '<turn-off-id>', '<set-intensity-id>' ],
        statuses: [ '<light-on-id>', '<light-off-id>' ] };

      Type.create(params).success(function(response) { $scope.type = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
params = {
  name: 'Light',
  categories: [ 'lights' ],
  properties: [ '<status-id>', '<intensity-id>' ],
  functions: [ '<turn-on-id>', '<turn-off-id>', '<set-intensity-id>' ],
  statuses: [ '<light-on-id>', '<light-off-id>' ] };

type = lelylan.create_type(params)
oauth.set_access_token()
params = {
  name: 'Light',
  categories: [ 'lights' ],
  properties: [ '<status>', '<intensity>' ],
  functions: [ '<turn-on>', '<turn-off>', '<set-intensity>' ],
  statuses: [ '<light-on>', '<light-off>' ] };

from lelylan import types
type = types.create_type(params)

notice In this example the resource IDs are replaced from friendly names.

Example header response

HTTP/1.1 201 Created
Location: http://api.lelylan.com/types/5042234270eda61200000006

Example body response

See type representation for body response.


PUT /types/:id

Update a type identified from its ID and returns extended information for it.

Resource URL

PUT  http://api.lelylan.com/types/:id

Parameters

id required The ID of the desired type.
name optional Type name.
description optional Type description.
categories optional List of categories.
properties optional List of property IDs.
functions optional List of function IDs.
statuses optional List of status IDs.

Example Request

curl -X PUT http://api.lelylan.com/types/<id> \
     -H 'Authorization: Bearer <token>' \
     -H 'Content-Type: application/json' \
     -d '{ "name": "RBG Light" }'
var Lelylan = require('lelylan-node')({ token: token });
var params = { name: 'RBG Light' };
Lelylan.Type.update('<id>', params, callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{type}}</div>
  <script>
    function LelylanController($scope, Type) {
      var params = { name: 'RBG Light' };
      Type.update('<ID>', params).success(function(response) { $scope.type = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
params = { name: 'RBG Light' }
type = lelylan.update_type('<id>', params)
oauth.set_access_token()
params = { name: 'RBG Light' }

from lelylan import types
type = types.update_type('<id>', params)

notice When updating a type connection (e.g. properties) all previous connected resources are replaced.

Example header response

HTTP/1.1 200 OK

Example body response

See type representation for body response.


DELETE /types/:id

Delete a type identified from its ID and returns extended information for it. All connected resources (e.g. properties, functions and statuses) are not deleted.

Resource URL

DELETE http://api.lelylan.com/types/:id

Parameters

id required The ID of the desired type.

Example Request

curl -X DELETE http://api.lelylan.com/types/<id> \
     -H 'Authorization: Bearer <token>'
var Lelylan = require('lelylan-node')({ token: token });
Lelylan.Type.delete('<id>', callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{type}}</div>
  <script>
    function LelylanController($scope, Type) {
      Type.delete('<ID>').success(function(response) { $scope.type = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
type = lelylan.delete_type('<id>')
oauth.set_access_token()
from lelylan import types
type = types.delete_type('<id>')

Example header response

HTTP/1.1 200 OK

Example body response

See type representation for body response.


GET /categories

Returns a list of common categories to associate to your types.

Resource URL

GET http://api.lelylan.com/categories

Example Request

curl http://api.lelylan.com/categories
var Lelylan = require('lelylan-node')();
Lelylan.Category.all({}, callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">
    <div ng-repeat="category in categories">{{category}}</div>
  </div>
  <script>
    function LelylanController($scope, Category) {
      Category.all().success(function(response) { $scope.categories = response });
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new
categories = lelylan.categories
from lelylan import types
categories = types.get_type_cat()

Example header response

HTTP/1.1 200 OK

Example body response

[
  { "name": "lights" },
  { "name": "locks" },
  { "name": "thermostats" },
  ...
]

Property API

A property is whatever vary in a device during time. It can be the intensity in a dimmer, the temperature in a cooling system or the speed in a car.


GET /properties/:id

Returns extended information for a given public property identified from its ID.

Resource URL

GET http://api.lelylan.com/properties/:id

Parameters

id required The ID of the desired property.

Example Request

curl http://api.lelylan.com/properties/<id>
var Lelylan = require('lelylan-node')();
Lelylan.Property.find('<id>', callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{property}}</div>
  <script>
    function LelylanController($scope, Property) {
      Property.find('<ID>').success(function(response) { $scope.property = response });
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new
property = lelylan.property('<id>')
from lelylan import properties
property = properties.get_prop('<id>')

Example header response

HTTP/1.1 200 OK

Example body response

Light status accepting text values on and off

{
  "name": "Status",
  "id": "50420c3a267ff51300000001",
  "uri": "http://api.lelylan.com/properties/50420c3a267ff51300000001",
  "type": "text",
  "default": "0",
  "accepted": [{ "key": "on", "value": "On" }, { "key": "off", "value": "Off" }],
  "created_at": "2012-09-01T10:56:41Z",
  "updated_at": "2012-09-01T10:56:41Z"
}

Light intensity accepting any value between the range that goes from 0 to 100 with step 1

{
  "name": "Intensity",
  "id": "518be8b100045e99cc000004",
  "uri": "http://api.lelylan.com/properties/518be8b100045e99cc000004",
  "type": "range",
  "range": { "min": "0", "max": "100", "step": "1" }
  "default": "50",
  "accepted": null,
  "created_at": "2012-09-01T10:56:41Z",
  "updated_at": "2012-09-01T10:56:41Z"
}

Body Response explained

uri Type URI
id Type ID
name Type name
type Property type
range Accepted range values. Visible only when type has range as value
default Default property value assigned to the property when the device is created
accepted List of accepted values from the property. Learn more about

GET /properties

Returns a list of owned (and public) properties.

Resource URL

# created properties
GET http://api.lelylan.com/properties

# all properties GET http://api.lelylan.com/properties/public

Parameters

name optional Property name to search.
start optional ID of the last fetched record. Learn more about pagination.
per optional default to '25' Number of resources per page.

Example Request

curl http://api.lelylan.com/properties \
     -H 'Authorization: Bearer <token>'
var Lelylan = require('lelylan-node')({ token: token });
Lelylan.Property.all({}, callback); // owned resources

var Lelylan = require('lelylan-node')();
Lelylan.Property.public({}, callback); // all existing resources
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">
    <div ng-repeat="property in owned">{{property}}</div>
    <div ng-repeat="property in public">{{property}}</div>
  </div>
  <script>
    function LelylanController($scope, Property) {
      Property.all({ name: 'status' }).success(function(response)    { $scope.owned = response });
      Property.public({ name: 'status' }).success(function(response) { $scope.public = response });
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
properties = lelylan.properties # owned resources

lelylan = Lelylan::Client.new
properties = lelylan.public_properties # all existing resources
oauth.set_access_token()
from lelylan import properties
properties = properties.get_created_prop() # owned resources

from lelylan import properties
properties = properties.get_all_prop() # all existing resources

Example header response

HTTP/1.1 200 OK

Example body response

[
  { # property representation },
  { # property representation },
  ...
]

See property representation for body response.


POST /properties

Create a property and returns extended information for it.

Resource URL

POST  http://api.lelylan.com/properties

Parameters

name required Property name.
type optional default to 'text' Property type. Valid values: text, number, range, color, password, date, time, datetime, url.
range optional Object containing the min, max and step range values.
Usi it only when type is set to range.
default optional Default property value assigned to the device when created.
accepted optional List of accepted values. Learn more about.

Example Request

curl -X POST http://api.lelylan.com/properties \
     -H 'Authorization: Bearer <token>' \
     -H 'Content-Type: application/json' \
     -d '{
           "name": "Intensity",
           "default": "50",
           "type": "range",
           "range": {"min": "0", "max": "100", "step": "1"}
         }'
var Lelylan = require('lelylan-node')({ token: token });
var params = {
  name: 'Intensity',
  default: '50',
  type: 'range',
  range: {'min': '0', 'max': '100', 'step': '1'} };

Lelylan.Property.create(params, callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{property}}</div>
  <script>
    function LelylanController($scope, Property) {
      var params = {
        name: 'Intensity',
        default: '50',
        type: 'range',
        range: {'min': '0', 'max': '100', 'step': '1'} };

      Property.create(params).success(function(response) { $scope.property = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
var params = {
  name: 'Intensity',
  default: '50',
  type: 'range',
  range: {'min': '0', 'max': '100', 'step': '1'} };

property = lelylan.create_property(params)
oauth.set_access_token()
var params = {
  name: 'Intensity',
  default: '50',
  type: 'range',
  range: {'min': '0', 'max': '100', 'step': '1'} };

from lelylan import properties
property = properties.create_prop(params)

Example header response

HTTP/1.1 201 Created
Location: http://api.lelylan.com/properties/5041e9e9230de31300000001

Example body response

See property representation for body response.


PUT /properties/:id

Update a property identified from its ID and returns extended information for it.

Resource URL

PUT  http://api.lelylan.com/properties/:id

Parameters

id required The ID of the desired property.
name optional Property name.
type optional Property type. Valid values: text, number, range, color, password, date, time, datetime, url.
range required Object containing the min, max and step range values.
Usi it only when type is set to range.
default optional Default property value assigned to the device when created.
accepted optional List of accepted values. Learn more about.

Example Request

curl -X PUT http://api.lelylan.com/properties/<id> \
     -H 'Authorization: Bearer <token>' \
     -H 'Content-Type: application/json' \
     -d '{ "default": "100" }'
var Lelylan = require('lelylan-node')({ token: token });
var params = { default: '100' };
Lelylan.Property.update('<id>', params, callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{property}}</div>
  <script>
    function LelylanController($scope, Property) {
      var params = { default: '100' };
      Property.update('<ID>', params).success(function(response) { $scope.property = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
params = { default: '100' };
property = lelylan.update_property('<id>', params)
oauth.set_access_token()
params = { default: '100' };

from lelylan import properties
property = properties.update_prop('<id>', params)

Example header response

HTTP/1.1 200 OK

Example body response

See property representation for body response.


DELETE /properties/:id

Delete a property identified from its ID and returns extended information for it.

Resource URL

DELETE http://api.lelylan.com/properties/:id

Parameters

id required The ID of the desired property.

Example Request

curl -X DELETE http://api.lelylan.com/properties/<id> \
     -H 'Authorization: Bearer <token>'
var Lelylan = require('lelylan-node')({ token: token });
Lelylan.Property.delete('<id>', callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{property}}</div>
  <script>
    function LelylanController($scope, Property) {
      Property.delete('<ID>').success(function(response) { $scope.property = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
property = lelylan.delete_property('<id>')
oauth.set_access_token()
from lelylan import properties
property = properties.delete_prop('<id>')

Example header response

HTTP/1.1 200 OK

Example body response

See property representation for body response.


Function API

A Function defines the daily interactions you have with the devices in your house, for example turning on a light, closing a door or raising the temperature of your thermostat. With functions you can control any device in the same way you do everyday.


GET /functions/:id

Returns extended information for a given public function identified from its ID.

Resource URL

GET http://api.lelylan.com/functions/:id

Parameters

id required The ID of the desired function.

Example Request

curl http://api.lelylan.com/functions/<id>
var Lelylan = require('lelylan-node')();
Lelylan.Function.find('<id>', callback);
<html ng-app="lelylan">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{function}}</div>
  <script>
    function LelylanController($scope, Function) {
      Function.find('<ID>').success(function(response) { $scope.function = response })
    }
  </script>

  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular.min.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular-resource.min.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular-cookies.min.js"></script>
  <script src="//s.lelylan.com/angularjs/0.1.0/lelylan.min.js"/></script>
</body>
</html>
lelylan = Lelylan::Client.new
function = lelylan.function('<id>')
from lelylan import functions
function = functions.get_func('<id>')

Example header response

HTTP/1.1 200 OK

Example body response

{
  "uri": "http://api.lelylan.com/functions/504210df70eda60a00000003",
  "id": "504210df70eda60a00000003",
  "name": "Set intensity",
  "created_at": "2012-09-01T13:42:55Z",
  "updated_at": "2012-09-01T13:42:55Z",
  "properties": [{
    "id": "50420c7e267ff51000000001",
    "uri": "http://api.lelylan.com/properties/50420c7e267ff51000000001",
    "expected": "on"
    "pending": "true"
  }, {
    "id": "50420c7e267ff51000000001",
    "uri": "http://api.lelylan.com/properties/50420c3a267ff51300000001",
    "expected": null
    "pending": true
  }]
}

Body response explained

uri Function URI
id Function ID
name Function name
properties Function properties
» property.uri Property URI
» property.id Property ID
» property.expected Expected property value.
» property.pending Property pending status.

GET /functions

Returns a list of owned (and public) functions.

Resource URL

# created functions
GET http://api.lelylan.com/functions

# all existing functions GET http://api.lelylan.com/functions/public

Parameters

name optional Property name to search.
start optional ID of the last fetched record. Learn more about pagination.
per optional default to '25' Number of resources per page.

Example Request

curl http://api.lelylan.com/functions \
     -H 'Authorization: Bearer <token>'
var Lelylan = require('lelylan-node')({ token: token });
Lelylan.Function.all({}, callback); // owned resources

var Lelylan = require('lelylan-node')();
Lelylan.Function.public({}, callback); // all existing resources
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">
    <div ng-repeat="function in owned">{{function}}</div>
    <div ng-repeat="function in public">{{function}}</div>
  </div>
  <script>
    function LelylanController($scope, Function) {
      Function.all({ name: 'turn' }).success(function(response) { $scope.owned = response })
      Function.public({ name: 'turn' }).success(function(response) { $scope.public = response })
    }
  </script>

  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular.min.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular-resource.min.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular-cookies.min.js"></script>
  <script src="//s.lelylan.com/angularjs/0.1.0/lelylan.min.js"/></script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
functions = lelylan.functions # owned resources

lelylan = Lelylan::Client.new
functions = lelylan.public_functions # all existing resources
oauth.set_access_token()
from lelylan import functions
functions = functions.get_created_func() # owned resources

from lelylan import functions
functions = functions.get_all_func() # all existing resources

Example header response

HTTP/1.1 200 OK

Example body response

[
  { # function representation },
  { # function representation },
  ...
]

See function representation for body response.


POST /functions

Create a function and returns extended information for it.

Resource URL

POST http://api.lelylan.com/functions

Parameters

name required Function name.
properties optional Array of properties.
» property.id required Property ID.
» property.expected optional Expected value assigned to the device property when the function is executed. Don't set it (null) if the value needs to be fullfilled by the user. Learn more about.

Example Request

curl -X POST http://api.lelylan.com/functions \
     -H 'Authorization: Bearer <token>' \
     -H 'Content-Type: application/json' \
     -d '{
          "name": "Set intensity",
          "properties": [
            { "id": "<status>", "expected": "on" },
            { "id": "<intensity>" }
          ]
         }'
var Lelylan = require('lelylan-node')({ token: token });
var params = {
  name: 'Set intensity',
  properties:
    [ { id: '<status>', expected: 'on'},
      { id: '<intensity>' } ] };

Lelylan.Function.create(params, callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{function}}</div>
  <script>
    function LelylanController($scope, Function) {
      var params = {
        name: 'Set intensity',
        properties: [
          { id: '<status>', expected: 'on'},
          { id: '<intensity>' } ] };

      Function.create(params).success(function(response) { $scope.function = response })
    }
  </script>

  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular.min.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular-resource.min.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular-cookies.min.js"></script>
  <script src="//s.lelylan.com/angularjs/0.1.0/lelylan.min.js"/></script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
params = {
  name: 'Set intensity',
  properties:
    [ { id: '<status>', expected: 'on'},
      { id: '<intensity>' } ] }

function = lelylan.create_function(params)
oauth.set_access_token()
params = {
  name: 'Set intensity',
  properties:
    [ { id: '<status>', expected: 'on'},
      { id: '<intensity>' } ] }

from lelylan import functions
function = functions.create_func(params)

Example header response

HTTP/1.1 201 Created
Location: http://api.lelylan.com/functions/504210df70eda60a00000003

Example body response

See function representation for body response.


PUT /functions/:id

Update a function identified from its ID and returns extended information for it.

Resource URL

PUT  http://api.lelylan.com/functions/:id

Parameters

id required The ID of the desired function.
name optional Function name.
properties optional Array of properties.
» property.id required Property ID.
» property.expected optional Expected value assigned to the device property when the function is executed. Set it to null if the value needs to be fullfilled by the user. Learn more about.

Example Request


curl -X PUT http://api.lelylan.com/functions/<id> \
     -H 'Authorization: Bearer <token>' \
     -H 'Content-Type: application/json' \
     -d '{
           "name": "Set intensity to 50%",
           "properties": [
             { "id": "<status>", "expected": "on"},
             { "id": "<intensity>", "expected": "50" }
           ]
         }'
var Lelylan = require('lelylan-node')({ token: token });
var params = {
  name: 'Set intensity to 50%',
  properties:
    [ { id: '<status>', expected: 'on'},
      { id: '<intensity>', expected: '50' } ] };

Lelylan.Function.update('<id>', params, callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{function}}</div>
  <script>
    function LelylanController($scope, Function) {
      var params = {
        name: 'Set intensity to 50%',
        properties:
          [ { id: '<status>', expected: 'on'},
            { id: '<intensity>', expected: '50' } ] };

      Function.update('<ID>', params).success(function(response) { $scope.function = response })
    }
  </script>

  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular.min.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular-resource.min.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular-cookies.min.js"></script>
  <script src="//s.lelylan.com/angularjs/0.1.0/lelylan.min.js"/></script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
params = {
  name: 'Set intensity to 50%',
  properties:
    [ { id: '<status>', expected: 'on'},
      { id: '<intensity>', expected: '50' } ] }

function = lelylan.update_function('<id>', params)
oauth.set_access_token()
params = {
  name: 'Set intensity to 50%',
  properties:
    [ { id: '<status>', expected: 'on'},
      { id: '<intensity>', expected: '50' } ] }

from lelylan import functions
function = functions.update_func('<id>', params)

notice When updating properties all previous connected properties are replaced.

Example header response

HTTP/1.1 200 OK

Example body response

See function representation for body response.


DELETE /functions/:id

Delete a function identified from its ID and returns extended information for it.
Connected properties are not deleted.

Resource URL

DELETE http://api.lelylan.com/functions/:id

Parameters

id required The ID of the desired function.

Example Request

curl -X DELETE http://api.lelylan.com/functions/<id> \
     -H 'Authorization: Bearer <token>'
var Lelylan = require('lelylan-node')({ token: token });
Lelylan.Function.delete('<id>', callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{function}}</div>
  <script>
    function LelylanController($scope, Function) {
      Function.delete('<ID>').success(function(response) { $scope.function = response })
    }
  </script>

  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular.min.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular-resource.min.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular-cookies.min.js"></script>
  <script src="//s.lelylan.com/angularjs/0.1.0/lelylan.min.js"/></script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
function = lelylan.delete_function('<id>')
oauth.set_access_token()
from lelylan import functions
function = functions.delete_func('<id>')

Example header response

HTTP/1.1 200 OK

Example body response

See function representation for body response.


Status API

Properties are not always enough to describe the status of a device. Think at a window for example. It has the property aperture to 100 when open or 0 when closed. But what if the roller shutter is opening? It is nether open or close. To have a complete control over the device status in every specific moment of its life use the Status API.


GET /statuses/:id

Returns extended information for a given public status identified from its ID.

Resource URL

GET http://api.lelylan.com/statuses/:id

Parameters

id required The ID of the desired status.

Example Request

curl http://api.lelylan.com/statuses/<id>
var Lelylan = require('lelylan-node')();
Lelylan.Status.find('<id>', callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{status}}</div>
  <script>
    function LelylanController($scope, Status) {
      Status.find('<ID>').success(function(response) { $scope.status = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new
status = lelylan.status('<id>')
from lelylan import statuses
status = statuses.get_status('<id>')

Example header response

HTTP/1.1 200 OK

Example body response

{
  "name": "Setting intensity",
  "id": "5042176370eda61200000004",
  "uri": "http://api.lelylan.com/statuses/5042176370eda61200000004",
  "created_at": "2012-09-01T14:10:43Z",
  "updated_at": "2012-09-01T14:10:43Z",
  "function": {
    "id": "5042205c70eda61000000003",
    "uri": "http://api.lelylan.com/functions/5042205c70eda61000000003"
  },
  "properties":[{
    "id": "50420c7e267ff51000000001",
    "uri": "http://api.lelylan.com/properties/50420c7e267ff51000000001",
    "pending": false,
    "values": ["on"],
  }, {
    "id": "50420c7e267ff51300000001",
    "uri": "http://api.lelylan.com/properties/50420c7e267ff51300000001",
    "pending": true,
    "ranges": [{"min": "0", "max": "100"}]
  }]
}

Body response explained

uri Device URI.
id Device ID.
name Device name.
function Default function associated to the status.
properties Device properties.
» property.uri Property URI.
» property.id Property ID.
» property.values Property values. Learn more about.
» property.pending Property pending status. Learn more about.

How do I use the matching values?

A status is based on its properties. To understand when a status match to a device in a specific moment of its life you need to check two things.

  • If the current device property value is included into the status values list.
  • If the current device property value is included into the status ranges list.
  • If the current device property pending status is the same as the status pending value.

notice At the moment it's not possible to get the device status from server side. A basic implementation is available on the AngularJS Device Component.


GET /statuses

Returns a list of owned (and public) statuses.

Resource URL

# created statuses
GET http://api.lelylan.com/statuses

# all statuses GET http://api.lelylan.com/statuses/public

Parameters

name optional Status name to search.
start optional ID of the last fetched record. Learn more about pagination.
per optional default to '25' Number of resources per page.

Example Request

curl http://api.lelylan.com/statuses \
     -H 'Authorization: Bearer <token>'
var Lelylan = require('lelylan-node')({ token: token });
Lelylan.Status.all({}, callback); // owned resources

var Lelylan = require('lelylan-node')();
Lelylan.Status.public({}, callback); // all existing resources
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">
    <div ng-repeat="status in owned">{{status}}</div>
    <div ng-repeat="status in public">{{status}}</div>
  </div>
  <script>
    function LelylanController($scope, Status) {
      Status.all({ name: 'turn' }).success(function(response) { $scope.owned = response })
      Status.public({ name: 'turn' }).success(function(response) { $scope.public = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
statuses = lelylan.statuses # owned resources

lelylan = Lelylan::Client.new
statuses = lelylan.public_statuses # all existing resources
oauth.set_access_token()
from lelylan import statuses
statuses = statuses.get_created_status() # owned resources

from lelylan import statuses
statuses = statuses.get_all_status() # all existing resources

Example header response

HTTP/1.1 200 OK

Example body response

[
  { # status representation },
  { # status representation },
  ...
]

See status representation for body response.


POST /statuses

Create a status and returns extended information for it.

Resource URL

POST  http://api.lelylan.com/statuses

Parameters

name required Status name.
properties optional Array of properties affecting the status.
» property.id required Property ID.
» property.value optional List of matching values. Learn more about.
» property.pending optional Property pending status. Learn more about. Valid values: true, false.

Example Request

curl -X POST http://api.lelylan.com/statuses \
     -H 'Authorization: Bearer <token>' \
     -H 'Content-Type: application/json' \
     -d '{
           "name": "On",
           "function": {"id": "<turn-off>" },
           "properties": [{
              "id": "<intensity>",
              "values": ["100"],
              "pending": false
            }]
         }'
var Lelylan = require('lelylan-node')({ token: token });
var params = {
  name: 'On',
  function: { id: '<turn-off>' },
  properties:
    [ { id: '<intensity>',
        value: ['100'],
        pending: false } ] };

Lelylan.Status.create(params, callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{status}}</div>
  <script>
    function LelylanController($scope, Status) {
      var params = {
        name: 'On',
        function: { id: '<turn-off>' },
        properties:
          [ { id: '<status>',
              value: ['on'] } ] };

      Status.create(params).success(function(response) { $scope.status = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
params = {
  name: 'On',
  function: { id: '<turn-off>' },
  properties:
    [ { id: '<intensity>',
        value: ['100'],
        pending: false } ] }

status = lelylan.create_status(params)
oauth.set_access_token()
params = {
  name: 'On',
  function: { id: '<turn-off>' },
  properties:
    [ { id: '<intensity>',
        value: ['100'],
        pending: false } ] }

from lelylan import statuses
status = statuses.create_status(params)

Example header response

HTTP/1.1 201 Created
Location: http://api.lelylan.com/statuses/5042176370eda61200000004

Example body response

See status representation for body response.


PUT /statuses/:id

Update a status identified from its ID and returns extended information for it.

Resource URL

PUT  http://api.lelylan.com/statuses/:id

Parameters

id required The ID of the desired status.
name optional Status name.
properties optional Array of properties affecting the status.
» property.id required Property ID.
» property.values optional List of matching values. Learn more about.
» property.ranges optional List of matching ranges. Learn more about.
» property.pending optional Property pending status. Learn more about. Valid values: true, false.

Example Request

curl -X PUT http://api.lelylan.com/statuses/5042176370eda61200000004 \
     -H 'Authorization: Bearer <token>' \
     -H 'Content-Type: application/json' \
     -d '{ "name": "Light On" }'
var Lelylan = require('lelylan-node')({ token: token });
var params = { name: 'Light On' }
Lelylan.Status.update('<id>', params, callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{status}}</div>
  <script>
    function LelylanController($scope, Status) {
      var params = { name: 'Light On' };
      Status.update('<ID>', params).success(function(response) { $scope.status = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
params = { name: 'Light On' }
status = lelylan.update_status('<id>', params)
oauth.set_access_token()
params = { name: 'Light On' }

from lelylan import statuses
status = statuses.update_status('<id>', params)

Notice When updating the properties field all previous connected properties are replaced.

Example header response

HTTP/1.1 200 OK

Example body response

See status representation for body response.


DELETE /statuses/:id

Delete a statuses identified from its ID and returns extended information for it.
Connected properties are not deleted.

Resource URL

DELETE http://api.lelylan.com/statuses/:id

Parameters

id required The ID of the desired status.

Example Request

curl -X DELETE http://api.lelylan.com/statuses/<id> \
     -H 'Authorization: Bearer <token>'
var Lelylan = require('lelylan-node')({ token: token });
Lelylan.Status.delete('<id>', callback);
<html ng-app="lelylan.client">
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{status}}</div>
  <script>
    function LelylanController($scope, Status) {
      Status.delete('<ID>').success(function(response) { $scope.status = response })
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
status = lelylan.delete_status('<id>')
oauth.set_access_token()
from lelylan import statuses
status = statuses.delete_status('<id>')

Example header response

HTTP/1.1 200 OK

Example body response

See status representation for body response.

Realtime API

Updates from the physical world

Light-32px


Overview

Real-time Lelylan updates provide your application with instant notifications of what happens to Lelylan and the physical world. The most interesting way to view the world is live, as it happens. Through the use of web hooks we've been able to create a system where your application gets notified of changes as they happen.

Our real-time API serves a couple of basic needs. First, instead of polling the Lelylan servers to check to see what's happening, we POST to your servers new data when available. Second, developers running servers like Node.js can provide real-time experiences to their users.


Getting Started

Register a client

Register a client to obtain a Client ID and Client Secret.

Create a callback URL

Create a service able to receive Lelylan updates. This callback URL must support the POST method because when we have new data we POST this data to your callback.


Events

You can get real-time updates by subscribing to a resource and its related event. The following table describes the available resources and events you can subscribe at.

RESOURCE EVENT RECEIVED DATA DESCRIPTION
devices property-update device Notified when a device updates its properties.
devices create device Notified when a device is created.
devices update device Notified when a device is update.
devices destroy device Notified when a device is deleted.

Receiving updates

When a user fires an event (for example creating a new device), a POST request is sent to the callback URL defined in the subscription.


Payload example

{
  "subscription": { "id": "50f54f5f5e74ef2873000006" },
  "resource": "devices",
  "event": "create",
  "nonce": "6c84fb90-12c4-11e1-840d-7b25c5ee775a",
  "data": {
     "uri": "http://api.lelylan.com/devices/5042344b95fc441000000001",
     "id": "5042344b95fc441000000001",
     "name": "Closet dimmer",
     "type": { "uri": "http://api.lelylan.com/types/5042310470eda60a0000000d" },
     "pending": false,
     "created_at": "2012-09-01T16:00:32Z",
     "updated_at": "2012-09-01T16:00:32Z",
     "properties": [{
        "uri": "http://api.lelylan.com/properties/50420c7e267ff51000000001",
        "id": "50420c7e267ff51000000001",
        "value": "on", "physical": "on" }],
     "physical": {
        "uri": "http://nodes.lelylan.com/mqtt/devices/5042205c70eda61000000003"
     }
  }
}

Payload explained

subscription Subscription ID firing the notification.
resource Notified resource.
event Notified event.
nonce Random unique identifier to avoid replay attacks.
data Notified resource representation.

Callback response

Respond with a 2xx HTTP status when the callback URL is called. If a different HTTP status is received, Lelylan will try to re-send the payload 10 times with an exponentially growing delay between the requests (1 sec, 3 secs, 9 secs, 27 secs, ..., 16 hours). After the 10th failed attempt the event is set as processed and no more calls are made.

Security check

Verify the X-Hub-Signature header to see if the received payload comes from Lelylan. This is a SHA-1 signed hexadecimal digest using your client secret as key and the payload as message.


Subscribtion API

To get realtime notification you first need to create a subscription. Check out the Subscriptions API above to undestand how to receive the desired updates.


GET /subscriptions/:id

Returns extended information for a given subscription identified from its ID.

Resource URL

GET http://api.lelylan.com/subscriptions/:id

Parameters

id required The ID of the desired subscription.

Example Request

curl http://api.lelylan.com/subscriptions/<id> \
     -u <client-id>:<client-secret>
var credentials = { clientID: '<client-id>', clientSecret: '<client-secret>' };
var Lelylan = require('lelylan-node')(credentials);

Lelylan.Subscription.find('<id>', callback);
<html ng-app="lelylan.client">
<body>
  <div ng-controller="LelylanController">{{subscription}}</div>
  <script>
    function LelylanController($scope, Subscription) {
      var credentials = { clientID: '<CLIENT-ID>', clientSecret: '<CLIENT-SECRET>' };
      Subscription.auth(creadentials);
      Subscription.get('<ID>').success(function(response) { $scope.subscriptions = response });
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(client_id: '<client-id>', client_secret: '<client-secret>')
subscription = lelylan.subscription('<id>')
import lelylan
from lelylan import oauth, subscriptions
oauth.oauth('<client_id>', '<client_secret>')

subscription = subscriptions.get_sub('<id>')

Example header response

HTTP/1.1 200 OK

Example body response

{
  "uri": "http://api.lelylan.com/subscriptions/508acdffd033a9ea77000026",
  "id": "508acdffd033a9ea77000026",
  "client": { "uri": "http://people.lelylan.com/oauth/applications/508acdffd033a9ea77025" },
  "resource": "devices",
  "event": "property-update",
  "callback_uri": "http://callback.com/lelylan",
  "created_at": "2012-10-26T17:53:03Z",
  "updated_at": "2012-10-26T17:53:03Z"
}

Body Response explained

uri Subscription URI.
id Subscription ID.
client.uri Client URI.
resource Notified resource.
event Notified event.
callback_uri Callback URI receiving the real-time updates.

GET /subscriptions

Returns the list of all subscriptions related to a client.

Resource URL

GET http://api.lelylan.com/subscriptions

Parameters

resource optional Resource name to search. See available resources.
event optional Event name to search. See available events.

Example Request

curl http://api.lelylan.com/subscriptions \
     -u <client-id>:<client-secret>
var credentials = { clientID: '<client-id>', clientSecret: '<client-secret>' };
var Lelylan = require('lelylan-node')(credentials);

Lelylan.Subscription.all({}, callback);
<html ng-app="lelylan.client">
<body>
  <div ng-controller="LelylanController">
    <div ng-repeat="subscription in subscriptions">{{subscription}}</div>
  </div>
  <script>
    function LelylanController($scope, Subscription) {
      var credentials = { clientID: '<CLIENT-ID>', clientSecret: '<CLIENT-SECRET>' };
      Subscription.auth(creadentials);
      Subscription.all().success(function(response) { $scope.subscriptions = response });
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(client_id: '<client-id>', client_secret: '<client-secret>')
subscriptions = lelylan.subscriptions
import lelylan
from lelylan import oauth, subscriptions
oauth.oauth('<client_id>', '<client_secret>')

subs = subscriptions.get_all_sub()

Example header response

HTTP/1.1 200 OK

Example body response

[
  { # subscription representation },
  { # subscription representation },
  ...
]

See subscription representation for body response.


POST /subscriptions

Create a subscription and returns extended information for it.

Resource URL

POST  http://api.lelylan.com/subscriptions

Parameters

resource required Notified resource. See available resources.
event required Notified event. See available events.
callback_uri required Callback URI receiving the real-time updates.

Example Request

curl -X POST http://api.lelylan.com/subscriptions \
    -u <client-id>:<client-secret> \
    -H 'Content-Type: application/json' \
    -d '{
          "resource": "<resource>",
          "event": "<event>",
          "callback_uri": "<callback-uri>"
        }'
var credentials = { clientID: '<client-id>', clientSecret: '<client-secret>' };
var Lelylan = require('lelylan-node')(credentials);

var params = {
  resource: '<resource>',
  event: '<event>',
  callback_uri: '<callback-uri>' };

Lelylan.Subscription.create(params, callback);
<html ng-app="lelylan.client">
<body>
  <div ng-controller="LelylanController">{{subscription}}</div>
  <script>
    function LelylanController($scope, Subscription) {
      var credentials = { clientID: '<CLIENT-ID>', clientSecret: '<CLIENT-SECRET>' };
      Subscription.auth(creadentials);
      var params = { resource: '<resource>', event: '<event>', callback_uri: '<callback-uri>' };
      Subscription.create(params).success(function(response) { $scope.subscriptions = response });
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(client_id: '<client-id>', client_secret: '<client-secret>')
params = {
  resource: '<resource>',
  event: '<event>',
  callback_uri: '<callback-uri>' }

subscription = lelylan.create_subscription(params)
import lelylan
from lelylan import oauth, subscriptions
oauth.oauth('<client_id>', '<client_secret>')

params = {
  resource: '<resource>',
  event: '<event>',
  callback_uri: '<callback-uri>' }

subscription = subscriptions.create_sub(params)

Example header response

HTTP/1.1 201 Created
Location: http://api.lelylan.com/subscriptions/508acdffd033a9ea77000026

Example body response

See subscription representation for body response.


PUT /subscriptions/:id

Update a subscription identified from its ID and returns extended information for it.

Resource URL

PUT  http://api.lelylan.com/subscriptions/:id

Parameters

id required The ID of the desired subscriptions.
resource optional Notified resource. See available resources.
event optional Notified event. See available events.
callback_uri optional Callback URI receiving the real-time updates.

Example Request

curl -X PUT http://api.lelylan.com/subscriptions/<id> \
     -u <client-id>:<client-secret> \
     -H 'Content-Type: application/json' \
     -d '{ "resource": "<resource>" }'
var credentials = { clientID: '<client-id>', clientSecret: '<client-secret>' };
var Lelylan = require('lelylan-node')(credentials);
var params = { resource: 'devices' };

Lelylan.Subscription.update('<id>', params, callback);
<html ng-app="lelylan.client">
<body>
  <div ng-controller="LelylanController">{{subscription}}</div>
  <script>
    function LelylanController($scope, Subscription) {
      var credentials = { clientID: '<CLIENT-ID>', clientSecret: '<CLIENT-SECRET>' };
      Subscription.auth(creadentials);
      var params = { resource: 'devices' };
      Subscription.update('<ID>', params).success(function(response) { $scope.subscriptions = response });
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(client_id: '<client-id>', client_secret: '<client-secret>')
params = { resource: 'devices' }

subscription = lelylan.update_subscription('<id>', params)
import lelylan
from lelylan import oauth, subscriptions
oauth.oauth('<client_id>', '<client_secret>')

params = { resource: 'devices' }
subscription = subscriptions.update_sub('<id>', params)

Example header response

HTTP/1.1 200 OK

Example body response

See subscription representation for body response.


DELETE /subscriptions/:id

Delete a subscription identified from its ID and returns extended information for it.

Resource URL

DELETE  http://api.lelylan.com/subscriptions/:id

Parameters

id required The ID of the desired subscription.

Example Request

curl -X DELETE http://api.lelylan.com/subscriptions/<id>
     -u <client-id>:<client-secret>
var credentials = { clientID: '<client-id>', clientSecret: '<client-secret>' };
var Lelylan = require('lelylan-node')(credentials);

Lelylan.Subscription.delete('<id>', callback);
<html ng-app="lelylan.client">
<body>
  <div ng-controller="LelylanController">{{subscription}}</div>
  <script>
    function LelylanController($scope, Subscription) {
      var credentials = { clientID: '<CLIENT-ID>', clientSecret: '<CLIENT-SECRET>' };
      Subscription.auth(creadentials);
      Subscription.delete('<ID>').success(function(response) { $scope.subscriptions = response });
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(client_id: '<client-id>', client_secret: '<client-secret>')
subscription = lelylan.delete_subscription('<id>')
import lelylan
from lelylan import oauth, subscriptions
oauth.oauth('<client_id>', '<client_secret>')

subscription = subscriptions.delete_sub('<id>')

Example header response

HTTP/1.1 200 OK

Example body response

See subscription representation for body response.

OAuth 2.0

Autorizing the physical world

Light-32px


Why OAuth 2.0

Lelylan API uses the OAuth 2.0 protocol for authentication and authorization. OAuth lets users grant the access to the desired resources to third party applications, giving them the possibility to enable and disable those accesses whenever they want.

Registration

All developers need to register their applications before getting started. A registered OAuth application is assigned with a unique Client ID and Client Secret.

OAuth Flows

Lelylan supports the following flows.


Authorization Code

The Authorization Code flow is made up from two parts. At first your application asks to the user the permission to access their data. If the user approves Lelylan sends to the client an authorization code. In the second part, the client POST the authorization code along with its client secret to the Lelylan in order to get the access token.

1. Redirect the user to the authorization page

Redirect the user to the authorization endpoint.

http://people.lelylan.com/oauth/authorize?
       response_type=code&
       client_id=<client-id>&
       redirect_uri=<redirect-uri>&
       scope=<scope>&
       state=<state>
var credentials = {
  clientID: '<client-id>',
  clientSecret: '<client-secret>',
  site: 'http://people.lelylan.com' };

var OAuth2 = require('simple-oauth2')(credentials);

var authorization_uri = OAuth2.AuthCode.authorizeURL({
  redirect_uri: '<callback-uri>',
  scope: '<scopes>' });

// => http://people.lelylan.com/oauth/authorize?
//      redirect_uri=<callback-uri>&
//      scope=<scopes>&
//      response_type=code&
//      client_id=<client-id>
// This flow is not available in AngularJS.
// To authenticate an AngularJS app use the Implicit Grant flow
oauth = OAuth2::Client.new(
  '<client-id>',
  '<client-secret>',
  site: 'http://people.lelylan.com' )

oauth.auth_code.authorize_url(redirect_uri: '<callback-uri>', scope: '<scopes>' )

# => http://people.lelylan.com/oauth/authorize?
#      redirect_uri=<callback-uri>&
#      scope=<scopes>&
#      response_type=code&
#      client_id=<client-id>
import lelylan
from lelylan import oauth

oauth.oauth('<client_id>', '<client_secret>', '<redirect_uri>', '<scope>')
oauth.get_auth_uri()

# => http://people.lelylan.com/oauth/authorize?
#      redirect_uri=<redirect-uri>&
#      client_id=<client-id>&
#      scope=<scope>&
#      response_type=code

Follow a description of the needed parameters.

response_type Always use 'code' as response type
client_id Registered Client ID.
redirect_uri Registered application URI where the user is redirected after authorization.
scope Application privileges. Learn more about.
state Optional opaque value used by the client to maintain state between the request and callback.

If the user grants the access, Lelylan redirects the user to redirect_uri and appends the authorization code in the query string. This code will be used to get the final access token.

http://example.com/redirect-uri?code=code&state=state

2. Get the access token

Get the access token (remember to basic authenticate with client id and client secret).

curl -X POST http://people.lelylan.com/oauth/token \
     -u  <client-id>:<client-secret> \
     -d 'grant_type=authorization_code' \
     -d 'code=<code>' \
     -d 'redirect_uri=<redirect_uri>'
OAuth2.AuthCode.getToken({ code: '<code>', redirect_uri: '<redirect-uri>' }, saveToken);

var token;
var saveToken = function(error, result) {
  if (error) console.log('Access Token Error', error.message);
  token = OAuth2.AccessToken.create(result);
  console.log('Access Token successfully saved');
});
// This flow is not available in AngularJS.
// To authenticate an AngularJS app use the Implicit Grant Flow
token = oauth.auth_code.get_token('<code>', redirect_uri: '<redirect-uri>')
# automatically parse the query string and set the access token
oauth.set_access_token()

Here a description of the needed parameters.

client_id Registered Client ID.
client_secret Registered Client Secret.
grant_type Always use 'authorization_code' as grant type.
code Authorization code (from the previous step).
redirect_uri Application URI where the user is redirected after authorization.

Here a response example.

HTTP/1.1 200 OK

{ "access_token": "4adc339e06c20e84c41d0c04c8ad5daf89cc3655d79b399930d112f5f7fXXXXX", "refresh_token": "ec1a59d298aa51b3f133b6135b817bb19eb917aac5bc7821d410ffbf5ebXXXXX", "token_type": "bearer", "expires_in": 7200, "scope": "user" }

Implicit Grant

This flow is meant for Javascript-based web applications that can't maintain state over time. The flow starts redirecting the user to the authorization endpoint.

http://people.lelylan.com/oauth/authorize?
       response_type=token&
       client_id=<client-id>&
       redirect_uri=<redirect-uri>&
       scope=<scope>&
       state=<state>
// This flow is not available in Node.js.
// To authenticate a Node app use the Authorization Code Flow
<html ng-app="app">
<body>
  <div ng-controller="LoginController">
    <!-- http://lelylan.github.io/lelylan-ng/ -->
    <oauth
      site="http://people.lelylan.com"
      client-id="<CLIENT_ID>"
      redirect-uri="<REDIRECT_URI>"
      profile-uri="http://api.lelylan.com/me"
      scope="resources">
    </oauth>
  </div>
</body>
</html>
// This flow is not available in Ruby.
// To authenticate a Ruby app use the Authorization Code Flow
// This flow is not available in Python.
// To authenticate a Python app use the Authorization Code Flow

Here a description of the needed parameters.

response_type Always use 'token' as response type.
client_id Registered Client ID.
client_secret Registered Client Secret.
scope Application privileges. Learn more about
state Optional opaque value used by the client to maintain state between the request and callback.

If the user grants the access, Lelylan redirects the user to redirect_uri and appends the access token to the fragment URI.

http://example.com/redirect-uri#
  access_token=1146eeea7054e3fc39aebb0f3820742981c65e243a2fa85174ce98b564XXXXX&
  token_type=bearer&
  expires_in=7200&
  state=remember-me

notice This flow does not return the refresh token. When the token is expired you need to go to the authorization page to get the new access token.

If your application is pure Javascript, you can easily parse the token from the URI. If your application is a native phone app you perform the flow in an embedded webview and redirect the user to a dummy website where you can grab the token from the URL.


Password Credentials

This flow is suitable when the resource owner has a trust relationship with the client, such as its computer operating system or a highly privileged application. Use this flow only when other flows are not viable or when you need a fast way to test your application.

notice Remember to basic authenticate your request using the Client ID and Secret.

curl -X POST http://people.lelylan.com/oauth/token \
     -u <client-id>:<client-secret> \
     -d 'grant_type=password' \
     -d 'username=<email>' \
     -d 'password=<password>' \
     -d 'scope=<scopes>
var token;
var credentials = {
  clientID: '<client-id>',
  clientSecret: '<client-secret>',
  site: 'http://people.lelylan.com' };

var OAuth2 = require('simple-oauth2')(credentials);
OAuth2.Password.getToken({
  username: '<username>',
  password: '<password>',
  scope: '<scopes>'
}, saveToken);

var saveToken = function(error, result) {
  if (error) console.log('Access Token Error', error.message);
  token = OAuth2.AccessToken.create(result);
  console.log('Access Token successfully saved');
});
// This flow is not available in AngularJS.
// To authenticate an AngularJS app use the Implicit Grant Flow
oauth = OAuth2::Client.new(
  '<client-id>',
  '<client-secret>',
  site: 'http://people.lelylan.com' )

token = oauth.password.get_token('<email>', '<password>', scopes: '<scopes>')
import lelylan
from lelylan import oauth
oauth.oauth('<client_id>', '<client_secret>')

# automatically store the access token for future requests
oauth.password_credentials('<email>', '<password>', '<scopes>')

Here a description of the needed parameters.

client_id Registered Client ID.
client_secret Registered Client Secret.
grant_type Always use 'password' as grant type.
username Registered user email.
password Registered user password.
scope Application privileges. Learn more about.

Here a response example.

HTTP/1.1 200 OK

{ "access_token": "4adc339e06c20e84c41d0c04c8ad5daf89cc3655d79b399930d112f5f7fXXXXX", "refresh_token": "ec1a59d298aa51b3f133b6135b817bb19eb917aac5bc7821d410ffbf5ebXXXXX", "token_type": "bearer", "expires_in": 7200, "scope": "user" }

notice Lelylan can block clients using this flow whenever the security for the final user is compromised.


Refresh Access Token

For security reasons an access token expires every 24 hours. To get a new one use the refresh token you previously received together with the access token.

notice Remember to basic authenticate your request using the Client ID and Secret.

curl -X POST http://people.lelylan.com/oauth/token \
     -u <client-id>:<client-secret> \
     -d 'grant_type=refresh_token' \
     -d 'refresh_token=<refresh-token>'
// The access token is automatically refreshed by the Node.js Client.
// The access token is automatically refreshed by AngularJS Client.
// The access token is automatically refreshed by the Ruby Client.
// The access token is automatically refreshed by the Python Client.

Here a description of the needed parameters.

client_id Registered Client ID.
client_secret Registered Client Secret.
grant_type Always use 'refresh_token' as grant type.
refresh_token Refresh token code (from previous access token request).

Here a response example.

HTTP/1.1 200 OK

{ "access_token": "4adc339e06c20e84c41d0c04c8ad5daf89cc3655d79b399930d112f5f7fXXXXX", "refresh_token": "ec1a59d298aa51b3f133b6135b817bb19eb917aac5bc7821d410ffbf5ebXXXXX", "token_type": "bearer", "expires_in": 7200 }


API requests

Once you have the access token you can access the API in different ways.

Authorization header

curl http://api.lelylan.com/devices.json -H "Authorization: Bearer <access-token>"

Query string access_token param

curl http://api.lelylan.com/devices?access_token=<access-token>

Body access_token param

curl http://api.lelylan.com/devices -X POST -d 'access_token=<access-token>'

Scopes

Scopes let you specify what can and can't be accessed from a third party application.

notice When no scopes are defined user is the default one.


Scope Accessible Services
resources Full access to all services. all services (the private service is not included)
resources:read Full access to all read resources. all read only services (the private service is not included)
privates Access to the owned devices private info. Get a device private info
devices Read, control and write access to owned devices. get a device get all devices create a device update a device delete a device update properties activate a device deactivate a device
devices:control Read and control access to owned devices. get a device get all devices update a device update properties
devices:read Read access to owned devices. get a device get all devices
types Read and write access to owned types. Get a type Get all types Create a type Update a type Delete a type Get a property Get all properties Create a property Update a property Delete a property Get a function Get all functions Create a function Update a function Delete a function Get a status Get all statuses Create a status Update a status Delete a status
types:read Read access to owned types. Get a type Get all types Get a property Get all properties Get a function Get all functions Get a status Get all statuses
user Read access to user profile Get /me

Example

In the following request an application requires read access for all resources and write access for all devices. You can specify multiple scopes by separating them with a space (encoded as a +).

http://people.lelylan.com/oauth/authorize?
  response_type=code&
  client_id=<client_id>&
  redirect_uri=<redirect_uri>&
  scope=resources:read+devices

Filtering resources

Lelylan offers a detailed control about the resource a third party application can access. Simply click to the Filter Accessible Devices button (in the authorization page) and add the desired accessible devices. A user can filter a specific device or all devices contained into a location. In case you add all devices contained in a location (keep in mind that when new ones are added into the location, the access to them is not inherited, and a new authorization is needed).


Expiring token

When authorizing a third party application an access token expires in 2 hours (for security reasons). Anyway, when developing your applications a not expiring token could be something useful. To have it, simply uncheck the Expiring token checkbox on the authorization page.


Learn more

It can be a little tricky to get started with OAuth 2. Here are a few links that might be of help.

Core Concepts

Shared Ideas between Lelylan

Light-32px


GET /me

Returns extended information for the authenticated user.

Example Request

curl http://api.lelylan.com/me \
    -H 'Authorization: Bearer <token>'
var Lelylan = require('lelylan-node')({ token: token });
Lelylan.Profile.me(callback);
<html ng-app="lelylan.client">
<head></head>
<body>
  <!-- login component here (dev.lelylan.com/api/oauth#implicit-grant-angular) -->
  <div ng-controller="LelylanController">{{profile}}</div>
  <script>
    function LelylanController($scope, Profile) {
      $scope.profile = Profile.get(); // form oauth-ng
    }
  </script>
</body>
</html>
lelylan = Lelylan::Client.new(token: token)
profile = lelylan.me
oauth.set_access_token()
from lelylan import user
profile = user.me()

Example header response

HTTP/1.1 200 OK

Example body response

{
  "id": "5036227a4f1b030200009000",
  "email": "reggie@lelylan.com",
  "full_name": "Reggie",
  "homepage": "http://lelylan.com",
  "location": "New York",
  "rate_limit": 5000
}

Rate Limit

We limit requests to 5000 per day. You can check the returned HTTP headers of any authenticated API request to see your current status.

HTTP/1.1 200 OK
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4999

When you exceed the API calls limit your request is forbidden.

HTTP/1.1 403 Forbidden
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 0

{ "status": 403, "method": "POST", "request": "http://api.lelylan.com/devices", "error": { "code": "notifications.access.rate_limit", "description": "Rate limit exceeded", "daily_rate_limit": 5000, } }

notice Do you need white listed access? Contact us and tell us why you need it.


Response Formats

Lelylan responds only with JSON representations.

Format Suffix Content-Type
JSON .json application/json

CORS

Lelylan supports CORS (Cross Origin Resource Sharing) for AJAX requests. CORS allows cross-domain communication from the browser. By building on top of the XmlHttpRequest object, CORS allows developers to work with the same idioms as same-domain requests. Here a complete list of browsers supporting CORS.


Infinite Scrolling Pagination

Sometimes your database will be so large that it's impossible to pre-load it client-side. The solution is to pre-load a segment of the database and then use pagination to load more data. Pagination is better with an infinite scrolling pattern, rather than showing a list of pages. When fetching the next page set the parameter start with the last fetched resource ID.

curl -L http://api.lelylan.com/devices.json?start=5042205c70eda61000000003

The default number of resources per page is 25 and the maximum is 100.


Accepted Time Formats

The preferred time format accepted from time attributes is ISO 8601 but orher formats are accepted. The following examples describe the same time using two different accepted formats.

2012-07-03 12:45:29Z        # ISO 8601
2012-07-03 14:45:29 +0200   # UTC local time with offset "+hh:mm"

Public Resources

API Endpoint Description
GET /types/public Get all public types
GET /types/:id Get a type
GET /properties/public Get all public properties
GET /properties/:id Get a property
GET /functions/public Get all public functions
GET /functions/:id Get a function
GET /statuses/public Get all public statuses
GET /statuses/:id Get a status

Error Responses

401 - Unauthorized

HTTP/1.1 401 Unauthorized

{ "status": 401, "method": "POST", "request": "http://api.lelylan.com/devices/4f4bb686d033a957c1000251", "error": { "code": "notifications.access.not_authorized", "description": "Token not valid" } }

404 - Not Found

HTTP/1.1 404 Not Found

{ "status": 404, "method": "GET", "request": "http://api.lelylan.com/devices/4f4ba959d033a95549000261", "error": { "code": "notifications.resource.not_found", "description": "Resource not found", "uri": "http://api.lelylan.com/devices/4f4ba959d033a95549000261" } }

422 - Not Valid

HTTP/1.1 422 Not Valid

{ "status": 422, "method": "POST", "request": "http://api.lelylan.com/devices", "error": { "code": "notifications.resource.not_valid", "description": "Name can't be blank.", "body": { "name": "" }, } }

HTTP Caching

HTTP caching is a universally adopted specification across all modern web browsers. Appropriate use of these standards let you improve response times and reduce server load. At Lelylan we focus on conditional requests. When a conditional requests is made across the network, unmodified resources result in an empty response body, aka 304 Not Modified, saving the cost of transferring the resource back to the end client. To make this possible Lelylan returns the Etag and Last-Modified headers.

Etag: "e59b3c1fca50b5d0bf44c931253ded86"
Last-Modified: Sat, 01 Sep 2012 16:00:32 GMT

REST Interface

Lelylan API attempts to conform to the design principles of Representational State Transfer (REST). The basic idea is to define services that look like the web, through the definition and the representation of all the resources that interact with the house. If you are thinking about what a resource is, think at it as anything important enough to be referenced as a thing by itself.

That said, following the REST architecture, Lelylan defined its API following these rules.

Connectedness You can move between resources using the links contained in them
Addressability Every resource is identified from an address
Uniform interface The HTTP verb is used to define the action to apply

HTTP verbs limitations

Not all HTTP clients are able to make PUT or DELETE requests. In order to work around this limitation, it is possible to send a POST request with a _method parameter set to put or delete.