Skip to content
This repository has been archived by the owner on Nov 29, 2021. It is now read-only.

Latest commit

 

History

History
248 lines (181 loc) · 10.1 KB

bindings.md

File metadata and controls

248 lines (181 loc) · 10.1 KB

APIs and Bindings to Protocols

This file describes the APIs and bindings to protocols for the Web of Things Framework. Note that this is work in progress and may change at any time.

  • auto-gen TOC: {:toc}

Web of Things Framework

It is estimated that within 10 years there will be a hundred billion IoT devices and this is anticipated to have as big effect as the Web itself has had.

The IoT suffers from fragmentation and product silos, the W3C is one of the few organizations that can define global standards to enable discovery and interoperability of services on a world wide basis. We want to extend the Web from a Web of pages to a Web of Things. The Web grew strong on the back of an enthusiastic open source community. We would like to repeat that for the Web of Things.

The value proposition is enabling lowered development costs and unlocking data silos by bridging IoT platforms through the Web at a range of device scales from microcontrollers to cloud-based server farms.

We will do this via a core model of services in terms of metadata, events, properties and actions, that is bound to a variety of protocols as no one protocol will fulfill all needs. Some candidate protocols include HTTP, WebSockets, CoAP, MQTT and XMPP. By bindings, we mean how to use the protocols to notify events and property updates, and how to invoke actions and return the results via REST based messages for each protocol.

APIs

You can define a new thing that is hosted on this server as follows:

wot.thing(name, model, implementation);

where name is a string that uniquely identifies this thing on this server, and implementation is an object with start and stop methods that take a single argument, the thing object that is generated by the server from the thing's model. Here is an example.

var wot = require('./framework.js');  // launch the server

// define the things for the door, light and agent
  
wot.thing("agent12",
  {
    "@properties" : {
      "door" : { "type" = "thing", "uri" = "door12" },
      "light" : { "type" = "thing", "uri" = "switch12" }
    }
  }, {
    start: function (thing) {
      console.log("started agent12");
      console.log("door is " + (thing.door.is_open ? "open" : "closed"));
      console.log("light is " + (thing.light.on ? "on" : "off"));
    },
    stop: function (thing) {
    },
  });

wot.thing("door12",
  {
    "@events" : {
      "bell": null,
      "key": {
        "valid" : "boolean"
      }
    },
    "@properties" : {
      "is_open" : "boolean"
    },
    "@actions" : {
      "unlock" : null
    }
  }, {
    start: function (thing) {
      thing.is_open = false;
      console.log("started door12");
    },
    stop: function (thing) {
    },
    unlock: function () {
      console.log("unlocking door12");
    }
  });
  
wot.thing("switch12",
  {
    "@properties" : {
      "on" : {
        "type" : "boolean",
        "writeable" : true
      }
    }
  }, {
    start: function (thing) {
      thing.on = true;
      console.log("started switch12");
    },
    stop: function (thing) {
    },
  });

The start method can be used to set up sensors, e.g. to start a timer to poll the sensor value, or to set up a listener for sensor data events. NodeJS provides many modules that could be useful for interfacing with sensors and actuators. Note that the implementation should provide an implementation for each of the actions defined in the "thing's" model (see unlock in the above example).

To set a listener for a named event generated by a thing:

thing.observe(name, handler);

where name is a string with the event name, and handler is a function that has an optional single argument that passes the events data if any.

You can create a proxy for a "thing" on another server with:

wot.register_proxy(uri, succeed, fail);

where uri is the URI for the thing's model, succeed is a function that is called with a single argument for the object acting as the proxy for the designated thing, and fail is a function with a string argument that is called when there is an error. Once you have the proxy object for the thing, you can then set listeners for events, access properties and invoke actions as desired.

You can later unregister the proxy with:

wot.unregister_proxy(uri);

A complication is that there can be circular dependencies between things, so that things may be started before the things that they depend upon have been started. The web of things framework addresses this by queuing messages to a given thing prior to it being started, and delivering them after the thing's start method has returned. The metaproperty "_running" is true between the calls to the thing's start and stop methods. However, note that the distributed nature for the web of things means that a thing may have stopped prior to the delivery of a message.

HTTP

This code currently only supports HTTP for downloading resources for use by Web browsers, and for access to "thing" descriptions in JSON-LD.

"Things" that are locally hosted on the server are exposed under the top level path /wot/, e.g. http://localhost:8888/wot/door12

The files that you want to serve up to web browsers should be put into the folder "www". The "thing" models are registed by scripts and dynamically served, so you should not put these models in this folder.

Future work will add HTTP bindings. This will involve clients providing an HTTP end point for delivering events, property updates and action results. Here is a possible API for registering a proxy.

wot.register_proxy(thing uri, handler, call back uri);

where thing uri is the URI for the thing you want to proxy, handler is a function that will be called with the new proxy object, and call back uri is the HTTP URI that the server hosting the thing can deliver notifications to.

An open question is whether the call back URI should have a unique path for each thing, or whether the thing is identified via a field in the JSON payload as it is for the Web Sockets bindings. This latter approach might be simpler to implement.

In addition, we will define a means for registering a proxy for a thing hosted by a client. This is intended to allow a web of things server within a firewall to set up a proxy on a cloud based server accessible on the Internet. Here is a possible API for this.

wot.publish(thing, uri);

Where thing is the object for a local thing or a proxy, and uri is the HTTP URI for the target server on which you wish to publish a proxy for the given thing. Calling this API results in the request being forwarded to the target server along with the thing's model.

Given that this API is expected to be most commonly used from a server within a firewall, the request may be passed via Web Sockets, opening a new connection to the target server if one isn't already open. Further work is anticipated on allowing the request to be passed via HTTP and other protocols.

Web Sockets

This code establishes a single web socket connection between any pair of servers or server/web page combination.

To register a proxy for a thing, the proxy server sends:

{
  register: thing's uri
}

The server hosting the proxied thing replies with:

{
  uri: thing's uri,
  state: thing's properties as an associative array
}

The proxy server can later unregister with:

{
  unregister: thing's uri
}

If the server hosting the proxied thing unregisters the thing, all proxy servers (or web pages) are notified with:

{
  unregister: thing's uri
}

When a property for the thing is updated, the thing server sends to all proxies:

{
  uri: thing's uri
  patch: property name
  data: property value
}

The same message is sent to the thing server when the proxy updates a property.

Note: the thing should echo this back when the change has taken effect. For IoT devices that spend most of the time asleep, there will be a lag until the device's next receiving window opens. By echoing the property update back to the proxy after the update has actually been sent to the IoT device, user interfaces can indicate that an update is in progress during the lag.

Note: the thing._running is a metaproperty that signals that the thing is running, i.e. it is true between calls to the thing's start and stop methods.

If an event is fired on the thing, the thing server sends a notification to all proxies:

{
  uri: thing's uri
  event: event name
  data: event data
}

If the proxy invokes an action on the thing it sends the following:

{
  uri: thing's uri
  call: call id
  action: action name
  data: action data
}

The Thing server later sends the action's result (if any) with:

{
  uri: thing's uri
  call: call id
  data: result data
}

MQTT

We invite feedback from MQTT experts on the following proposed binding of the Web of Things Framework to MQTT

MQTT is a pub-sub messaging protocol consisting of clients and brokers that communicate over TCP/IP. Each messag is associated with a topic that is used by brokers to route the message to clients who have registered an interest in that topic. Topics can be defined hierarchically with slash (/) as a separator.

One complication is the need to identify a broker that can connect directly or indirecly the client and target servers.

For the Web of Things, a pair of topics is required for each thing. The first is used to forward messages to that thing. The second is used to forward messages to proxies for that thing on other servers.

  • thing/THING ID
  • proxy/THING ID

Where THING ID is a unique identifier for the thing being proxied.

Question: should there be a prefix, e.g. wot/ in case the brokers are being used for messages other than for the Web of Things?

The MQTT payload is essentially the same as for Web sockets except for the URI which can be omitted as it is implied by the THING ID.

Question: would it be better to use a topic for the server ID rather than for the thing ID? The latter would include the thing ID as part of the message payload. This would increase the amount of code in common with Web Sockets, and potentially, might reduce the memory demands compared with one topic per thing.