This specification describes a programming interface to [the Web of Things](http://w3c.github.io/wot/current-practices/wot-practices.html) (WoT), that allows scripts run on a Thing to discover and access other Things through a Client API, provide resources characterized by properties, actions and events through a Server API, and access locally attached hardware through a Physical API.

Implementers need to be aware that this specification is considered unstable. Vendors interested in implementing this specification before it eventually reaches the Candidate Recommendation phase should subscribe to the [repository](https://github.com/w3c/wot-scripting-api) and take part in the discussions.

This document defines conformance criteria that apply to a single product: the UA (user agent) that implements the interfaces it contains.

This specification can be used for implementing the WoT Scripting API in multiple language bindings. Currently ECMAScript and TypeScript definitions are described in this document. For specifying bindings in other languages, extensions of this document may be created later.

The UA may be implemented in the browser, or in a separate runtime environment, such as [Node.js](https://nodejs.org/en/) or small embedded runtimes such as the [JavaScript Runtime for Zephyr OS](https://www.zephyrproject.org/community/blog/introducing-javascript-runtime-zephyr-os).

Implementations that use ECMAScript executed in a browser to implement the APIs defined in this document MUST implement them in a manner consistent with the ECMAScript Bindings defined in the Web IDL specification [[!WEBIDL]].

Implementations that use TypeScript or ECMAScript in a runtime to implement the APIs defined in this document MUST implement them in a manner consistent with the TypeScript Bindings defined in the TypeScript specification [[!TYPESCRIPT]].

This document serves a general description of the WoT Scripting API. Language and runtime specific issues are discussed in separate extensions of this document.

Terminology and conventions

The following terms are defined in [[!WOT-PRACTICES]]:

Thing
A physical or virtual entity that represents physicality, such as a device, a group of devices, a room, or a software stack that exposes WoT interfaces.
Thing Description
An RDF document (currently serialized in JSON-LD by default) that contains semantic and functional descriptions of a Thing: semantic context, interaction resources (such as properties, actions and events), communication (protocol, data serialization, bindings), and security related data (e.g. keys, certificates, policies etc).
WoT Interface
Resource-oriented Web interface (often called "Web API") that allows access to Things exposed over a network using different Protocol Bindings, as defined by Thing Descriptions.
Protocol Bindings
Mapping WoT Thing interactions to specific protocol suites. Protocol bindings are being defined for HTTP, CoAP, Bluetooth Smart (BLE), MQTT, WebSockets, etc.
JSON-LD
A JSON document that is augmented with support for Linked Data by providing an @context property with a defining URI [[!JSON-LD]].

Consuming a Thing Description means parsing the Thing Description and building a resource model with the use of Protocol Bindings that can be used in a script for accessing and controlling the Thing. A Thing when starts up, consumes its own TD, then it exposes its WoT interface as a server. Note that a script on a Thing may consume other TDs and expose a combined interface of the consumed Things.

A WoT Runtime or WR is defined as a script execution environment that manages the lifecycle of WoT application scripts, implements a script interpreter, an event loop, a security enforcement point for access management and uses lower-level APIs to provide access to local and remote resources. A WR should be isolated from other execution environments on memory address space, storage address space (file system), network namespace, etc.

In this version of the specification, a WR is assumed to run a single script that uses this API to define one or more Things that share a common event loop. Script deployment methods are out of scope of this version. In future versions, running multiple scripts (as modules) may be possible, and script deployment MAY be implemented using a manager Thing whose actions permit script lifecycle management operations.

A Thing is represented either as a ConsumedThing (for Things obtained by discovery or retrieve operations) or ExposedThing (for Things created with this API inside the WR). All ExposedThings contained in a WR are serving external requests through the same event loop, and are said to be local Things to each other. All other Things that run in a different WR, even if on the same physical hardware, are said to be remote Things to each other. Note that there may also be local ConsumedThing objects as well.

The terms URL and URL path are defined in [[!URL]].

The following terms are defined in [[!HTML5]] and are used in the context of browser implementations: browsing context, top-level browsing context, global object, incumbent settings object, Document, document base URL, Window, WindowProxy, origin, ASCII serialized origin, executing algorithms in parallel, queue a task, task source, iframe, valid MIME type.

A browsing context refers to the environment in which Document objects are presented to the user. A given browsing context has a single WindowProxy object, but it can have many Document objects, with their associated Window objects. The script execution context associated with the browsing context identifies the entity which invokes this API, which can be a web app, a web page, or an iframe.

The term secure context is defined in [[!WEBAPPSEC]].

script execution context, Promise, JSON, JSON.stringify and JSON.parse are defined in [[!ECMASCRIPT]].

The algorithms utf-8 encode, and utf-8 decode are defined in [[!ENCODING]].

IANA media types (formerly known as MIME types) are defined in RFC2046.

Introduction

As described in the [WoT Current Practices](http://w3c.github.io/wot/current-practices/wot-practices.html#vision), the Web of Things is made of Things that can describe their capabilities in a machine-interpretable format, the Thing Description (TD). By consuming a TD, a Thing creates a runtime resource model that allows accessing the Thing by an application. The overall WoT concepts are described in the [WoT Architecture](https://w3c.github.io/wot-architecture/) document.

Use Cases

The following scripting use cases are covered in this specification:

The following use cases are being considered for next versions:

WoT Data Representation

WoT provides a unified representation for data exchange between Things, standardized in the [Wot Things Description](https://w3c.github.io/wot-thing-description/) specification. Thing Descriptions are represented as dictionary objects in this API.

The WoT Scripting API

The API object represents an implementation of the WoT Runtime and provides functionality to obtain Things by discovery or creation.

The WoT object exposes only functions and has no internal state.

Browser implementations SHOULD use a namespace object such as `wot`, and [Node.js](https://nodejs.org/en/)-like runtimes MAY provide the API object through the [`require()`](https://nodejs.org/api/modules.html) or [`import`](http://www.ecma-international.org/ecma-262/6.0/#sec-imports) mechanism.

    // [SecureContext]
    // [NamespaceObject]
    interface WoT {
      Observable<ConsumedThing> discover(optional ThingFilter filter);
      Promise<ConsumedThing> retrieve(USVString url);
      Promise<ExposedThing> createLocalThing(ThingInit init);
    };

    dictionary ThingInit {
      DOMString name;
      USVString url;
      Dictionary description;
    };

    enum DiscoveryType { "any", "local", "nearby", "directory", "broadcast", "other" };

    dictionary ThingFilter: ThingInit {
      (DiscoveryType or DOMString) type = "any";
    };
  

The discover() method returns an [Observable](https://github.com/tc39/proposal-observable) object that can be subscribed and unsubscribed to.

    let subscription = wot.discover({ type: "registry", url: "http://wot.registry.org" }).subscribe(
      thing => { console.log("Found Thing " + thing.url); },
      error => { console.log("Discovery finished because an error: " + error.message); },
      () => { console.log("Discovery finished successfully");}
    );

    setTimeout(
      () => { subscription.unsubscribe(); console.log("Discovery timeout"); },
      5000);
  

Note that canceling a discovery (through unsubscribe) may not be successful in all cases, for instance when discovery is based on open ended broadcast requests. However, once `unsubscribe()` has been called, implementations MUST suppress further event handling ( i.e. further discoveries and errors) on the Observable. Also, a discovery error may not mean the end of the discovery process. However, in order to respect Observable semantics (error always terminates processing), implementations MUST close or suppress further event handling on the Observable.

    let subscription = wot.discover({ type: "local" }).subscribe(
      thing => { console.log("Found local Thing " + thing.url); },
      error => { console.log("Discovery finished because an error: " + error.message); },
      () => { console.log("Discovery finished successfully");}
    );
  
    let subscription = wot.discover({ type: "nearby", description: {protocol: "BLE4.2"} }).subscribe(
      thing => { console.log("Found nearby Thing " + thing.url); },
      error => { console.log("Discovery finished because an error: " + error.message); },
      () => { console.log("Discovery finished successfully");}
    );
  
    let subscription = wot.discover({ type: "other", description: { solution: "XYZ123", key: "..."} }).subscribe(
      thing => { console.log("Found Thing " + thing.url); },
      error => { console.log("Discovery finished because an error: " + error.message); },
      () => { console.log("Discovery finished successfully");}
    );
  

The Thing Server API

    interface ExposedThing {

      // define TD modifiers
      ExposedThing addProperty(ThingPropertyInit property);
      ExposedThing removeProperty(DOMString name);

      ExposedThing addAction(ThingActionInit action);
      ExposedThing removeAction(DOMString name);

      ExposedThing addEvent(ThingEventInit event);
      ExposedThing removeEvent(DOMString name);

      Promise<void> register(optional USVString directory);
      Promise<void> unregister();

      Promise<void> start();
      Promise<void> stop();

      Promise<void> emitEvent(DOMString eventName, any payload);

      // define request handlers (one per request type, so no events here)

      ExposedThing onRetrieveProperty(PropertyRequestHandler handler);
      ExposedThing onUpdateProperty(PropertyRequestHandler handler);

      ExposedThing onInvokeAction(ActionRequestHandler handler);

      ExposedThing onObserve(ObserveRequestHandler handler);
    };

    ExposedThing implements ConsumedThing;

    callback PropertyRequestHandler = any (PropertyRequest request);
    callback ActionRequestHandler = any (ActionRequest request);
    callback ObserveRequestHandler = any (ObserveRequest request);

    dictionary PropertyRequest {
        USVString from;
        ThingPropertyInit property;
        Dictionary options;
    };

    dictionary ActionRequest {
        USVString from;
        ThingActionInit action;
        any inputData;
    };

    dictionary ObserveRequest {
        USVString from;
        ObserveType type;
        boolean subscribe;
        DOMString name;
    };

    enum ObserveType { "property", "action", "event", "td" };

    dictionary SemanticType {
      DOMString name;
      DOMString context;
    };

    dictionary ThingPropertyInit {
      DOMString name;
      boolean configurable = true;
      boolean enumerable = true;
      boolean writable = true;
      sequence<SemanticType> semanticTypes;
      Dictionary dataDescription;
      any value;
    };

    dictionary ThingEventInit {
      DOMString name;
      sequence<SemanticType> semanticTypes;
      Dictionary outputDataDescription;
    };

    dictionary ThingActionInit {
      DOMString name;
      Dictionary inputDataDescription;
      Dictionary outputDataDescription;
      sequence<SemanticType> semanticTypes;
      Function action;
    };
  
    WoT.createLocalThing(thingDescription)
      .then(function(thing) {
        // manually add properties, actions, and events
        thing.addProperty({
          name: "temperature",
          value: "0",
          writable: false
          // use default values for the rest
        }).addEvent({
          name: "onchange",
          outputDataDescription: {
            type: "float32"
          }
        }).addAction({
          name: "reset",
          action: () => { this.temperature = 0; }
        })
        // add server functionality
        .onRetrieveProperty( request => {
          console.log("Handling read request");
          return this.temperature;
        }).onObserve( request => {
          console.log("Handling observe request");
          // add listener identified by request.from
        }).onInvokeAction( request => {
          console.log("Handling action request");
        }).start();
      });
  
    let thingDescription = {
      name: "mySensor",
      uri: "http://myregistry.org/mySensor/description"
    };

    WoT.createLocalThing(thingDescription)
      .then(function(thing) {
        // properties, actions and events are added based on the TD
        console.log("created " + thing.name });
        // now add the requests handlers
        thing.onRetrieveProperty(function(request) {
            console.log("Sending property '" + request.property + "' to " + request.from);
        }).onUpdateProperty(function(request) {
            console.log("Updating property '" + request.property + "' by " + request.from);
        }).onObserve(function(request) {
            console.log("Adding listener " + request.from);
            console.log("Observing " + request.type + " " + request.name +
                (request.subscribe? " recursively" : ""));
        }).start().then(function() {
           console.log("Thing started serving requests");
        });
      })
  
    let thingDescription = {
      name: "mySensor",
      description: {
        "@context": [
          "http://w3c.github.io/wot/w3c-wot-td-context.jsonld",
          "http://w3c.github.io/wot/w3c-wot-common-context.jsonld",
        ],
        "@type": [ "Thing" ],
        "interaction": [
          // ...
        ]
        // ...
      }
    };

    WoT.createLocalThing(thingDescription)
      .then(function(thing) {
        // properties, actions and events are added based on the TD
        // ...
      });
  

The Thing Client API

    interface ConsumedThing {
      readonly attribute DOMString name;
      readonly attribute USVString url;
      readonly attribute Dictionary description;

      Promise<any> invokeAction(DOMString name, sequence<any> parameters);
      Promise<void> setProperty(DOMString name, any value);
      Promise<any> getProperty(DOMString name);

      ConsumedThing addListener(DOMString eventName, ThingEventListener listener);
      ConsumedThing removeListener(DOMString eventName, ThingEventListener listener);
      ConsumedThing removeAllListeners(DOMString eventName);
    };

    callback ThingEventListener = void (Event event);

    [Constructor(PropertyChangeEventInit init)]
    interface PropertyChangeEvent: Event {
        readonly attribute PropertyChangeEventInit data;
    };

    [Constructor(ActionInvocationEventInit init)]
    interface ActionInvocationEvent: Event {
        readonly attribute ActionInvocationEventInit data;
    };

    [Constructor(ThingDescriptionChangeEventInit init)]
    interface ThingDescriptionChangeEvent: Event {
        readonly attribute ThingDescriptionChangeEventInit data;
    };

    dictionary PropertyChangeEventInit {
        DOMString name;
        any value;
        any oldValue;
    };

    dictionary ActionInvocationEventInit {
        DOMString actionName;
        any returnValue;
    };

    dictionary ThingDescriptionChangeEventInit {
        TDChangeType type;
        TDChangeMethod method;
        DOMString name;
        TDChangeData data;
    };

    enum TDChangeMethod { "add", "remove", "change" };
    enum TDChangeType { "property", "action", "event" };
    typedef (ThingPropertyInit or ThingActionInit or ThingEventInit) TDChangeData;
  
    wot.retrieve("http://myregistry.org/mySensor").then(
      error => { console.log("Discovery finished because an error: " + error.message); }
      thing => {
        console.log("Thing " + thing.name + " has been consumed.");
        console.log("{ " + JSON.serialize(thing) + " }");
        thing.addListener("onchange", function(event) {
            if (event instanceof PropertyChangeEvent) {
                console.log("Property " + event.name + " has changed to " + event.value);
            }
        }).invokeAction("startMeasurement", ["Celsius"]);
      },
    );
  

Security and Privacy

The trust model, attacker model, threat model and possible mitigation proposals for the Wot Scripting API are presented in the WoT Security and Privacy document. This section presents the chosen security and privacy model through normative requirements to implementations.

Chain of Trust

Things discoverable and accessible in a WoT network SHOULD be identified and authenticated.

The integrity of WoT communication SHOULD be ensured by implementations.

Threat Model

The main threats are summarized in the WoT Security and Privacy document.

In this specification the following threats are considered of the highest priority:

Security Mechanisms

Identification, Authentication, Authorization

Transport-level security

Application-level security

Security policies

This section summarizes the security policies which are specified as normative requirements in the respective algorithms of this specification.

Changes

The following is a list of major changes to the document. For a complete list of changes, see the [github change log](https://github.com/w3c/wot-scripting-api/commits/master). You can also view the [recently closed bugs](https://github.com/w3c/wot-scripting-api/issues?page=1&state=closed).

Open issues

The following problems are being discussed and need most attention:

Acknowledgements

The editors would like to thank Dave Raggett, Matthias Kovatsch, and Michael Koster for their comments and guidance to this document.