Copyright © 2017-2019 W3C® (MIT, ERCIM, Keio, Beihang). W3C liability, trademark and permissive document license rules apply.
The main Web of Things (WoT) concepts are described in the WoT Architecture document. The Web of Things is made of entities (Things) that can describe their capabilities in a machine-interpretable Thing Description (TD) and expose these capabilities through the WoT Interface, that is, network interactions modeled as Properties (for reading and writing values), Actions (to execute remote procedures with or without return values) and Events (for signaling notifications).
Scripting is an optional "convenience" building block in WoT and it is typically used in gateways that are able to run a WoT Runtime and script management, providing a convenient way to extend WoT support to new types of endpoints and implement WoT applications such as Thing Directory.
This specification describes a programming interface representing the WoT Interface that allows scripts to discover, operate Things and to expose locally defined Things characterized by WoT Interactions specified by a script.
The specification deliberately follows the WoT Thing Description specification closely. It is possible to implement simpler APIs on top of this API, or implementing directly the WoT network facing interface (i.e. the WoT Interface).
This specification is implemented at least by the Thingweb project also known as node-wot, which is considered the reference open source implementation at the moment. Check its source code, including examples. Other, closed source implementations have been made by WG member companies and tested against node-wot in plug-fests.
This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.
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 and take part in the discussions.
Please contribute to this draft using the GitHub Issue feature of the WoT Scripting API repository. For feedback on security and privacy considerations, please use the WoT Security and Privacy Issues.
This document was published by the Web of Things Working Group as a Working Draft. This document is intended to become a W3C Recommendation.
Comments regarding this document are welcome. Please send them to public-wot-wg@w3.org (archives).
Publication as a Working Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.
This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.
This document is governed by the 1 March 2019 W3C Process Document.
WoT provides layered interoperability based on how Things are used: "consumed" and "exposed", as defined in [WOT-ARCHITECTURE].
By consuming a TD, a client Thing creates a local runtime resource model that allows accessing the Properties, Actions and Events exposed by the server Thing on a remote device.
Exposing a Thing requires:
Typically scripts are meant to be used on bridges or gateways that expose and control simpler devices as WoT Things and have means to handle (e.g. install, uninstall, update etc.) and run scripts.
This specification does not make assumptions on how the WoT Runtime handles and runs scripts, including single or multiple tenancy, script deployment and lifecycle management. The API already supports the generic mechanisms that make it possible to implement script management, for instance by exposing a manager Thing whose Actions (action handlers) implement script lifecycle management operations.
This section is non-normative.
The following scripting use cases are supported in this specification:
As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.
The key words MAY, MUST, and SHOULD in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.
This specification describes the conformance criteria for the following classes of user agent (UA).
Due to requirements of small embedded implementations,
    splitting WoT client and server interfaces was needed. Then,
    discovery is a distributed application, but typical scenarios
    have been covered by a generic discovery API in this
    specification. This resulted in using 3 conformance classes for
    a UA that implements this API, one for client, one for
    server, and one for discovery. An application that uses this
    API can introspect for the presence of the
    consume(), produce() and
    discover() methods on the WoT API object in order to determine which
    conformance class the UA implements.
Implementations of this conformance class MUST implement the
        ConsumedThingconsume() method on the
        WoT API object.
Implementations of this conformance class MUST implement ExposedThingproduce() method on the WoT API object.
Implementations of this conformance class MUST implement the
        ThingDiscoverydiscover() method on the
        WoT API object.
These conformance classes MAY be implemented in a single UA.
This specification can be used for implementing the WoT Scripting API in multiple programming languages. The interface definitions are specified in [WEBIDL].
The UA may be implemented in the browser, or in a separate runtime environment, such as Node.js or in small embedded runtimes.
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].
ThingDescription
    typetypedef object ThingDescription;
    Represents a Thing Description (TD) as defined in [WOT-TD]. It is expected to be a parsed JSON object that is validated using JSON schema validation.
Fetching a TD given a URL should be done with an external method, such as the Fetch API or a HTTP client library, which offer already standardized options on specifying fetch details.
try {
  let res = await fetch('https://tds.mythings.biz/sensor11');
  // ... additional checks possible on res.headers
  let td = await res.json();
  let thing = new ConsumedThing(td);
  console.log("Thing name: " + thing.getThingDescription().title);
} catch (err) {
  console.log("Fetching TD failed", err.message);
}Note that [WOT-TD] allows using a shortened Thing Description by the means of defaults and requiring clients to expand them with default values specified in [WOT-TD] for the properties that are not explicitly defined in a given TD.
To expand a TD given td, run the following steps:
The [WOT-TD] specification defines how a
      TD should be validated. Therefore, this API expects
      the ThingDescription
      objects be validated before used as parameters. This
      specification defines a basic TD validation as
      follows.
To validate a TD given td, run the following steps:
"TypeError" and terminate these steps."TypeError" and terminate these
            steps.
          "TypeError" and terminate these
            steps.
          Defines the API entry point exposed as a singleton and contains the API methods.
WOT interface[SecureContext, Exposed=(Window,Worker)]
interface WOT {
  // methods defined in UA conformance classes
};
      
    consume()
      methodpartial interface WOT {
  Promise<ConsumedThing> consume(ThingDescription td);
};
      Belongs to the WoT Consumer
        conformance class. Expects an td argument and
        returns a Promise
        that resolves with a ConsumedThing object that represents
        a client interface to operate with the Thing. The method MUST run the following steps:
SecurityError and
          terminate these steps.ConsumedThing
            object constructed from td.
          produce()
      methodpartial interface WOT {
  Promise<ExposedThing> produce(ThingDescription td);
};
      Belongs to the WoT Producer
        conformance class. Expects a td argument and
        returns a Promise
        that resolves with an ExposedThing object that extends
        ConsumedThing with a
        server interface, i.e. the ability to define request
        handlers. The method MUST run the following steps:
SecurityError and terminate these
          steps.
          ExposedThing object constructed
          with td.
          discover()
      methodpartial interface WOT {
  ThingDiscovery discover(optional ThingFilter filter = null);
};
      Belongs to the WoT Discovery
        conformance class. Starts the discovery process that will
        provide ThingDescription
        objects for Thing Descriptions
        that match an optional filter argument. The
        method MUST run the
        following steps:
SecurityError and terminate these
          steps.ThingDiscovery object
          discovery with filter.
          Refer to the ThingDiscovery section for how
      discovery should be implemented.
ConsumedThing
    interfaceRepresents a client API to operate a Thing. Belongs to the WoT Consumer conformance class.
[SecureContext, Exposed=(Window,Worker)] interfaceConsumedThing{constructor(ThingDescriptiontd); Promise<any>readProperty(DOMStringpropertyName, optionalInteractionOptionsoptions = null); Promise<PropertyMap>readAllProperties(optionalInteractionOptionsoptions = null); Promise<PropertyMap>readMultipleProperties( sequence<DOMString> propertyNames, optionalInteractionOptionsoptions = null); Promise<void>writeProperty(DOMStringpropertyName,anyvalue, optionalInteractionOptionsoptions = null); Promise<void>writeMultipleProperties(PropertyMapvalueMap, optionalInteractionOptionsoptions = null); Promise<any>invokeAction(DOMStringactionName, optionalanyparams = null, optionalInteractionOptionsoptions = null); Promise<void>observeProperty(DOMStringname,WotListenerlistener, optionalInteractionOptionsoptions = null); Promise<void>unobserveProperty(DOMStringname); Promise<void>subscribeEvent(DOMStringname,WotListenerlistener, optionalInteractionOptionsoptions = null); Promise<void>unsubscribeEvent(DOMStringname);ThingDescriptiongetThingDescription(); }; dictionaryInteractionOptions{ objecturiVariables; }; typedef objectPropertyMap; callbackWotListener= void(anydata);
ConsumedThingAfter fetching
      a Thing Description as a JSON object,
      one can create a ConsumedThing object.
To create ConsumedThing with the ThingDescription td, run
        the following steps:
ConsumedThing
            object.
          getThingDescription() methodReturns the internal slot |td| of the ConsumedThing object that represents
      the Thing Description of the ConsumedThing. Applications may
      consult the Thing metadata stored in
      |td| in order to introspect its capabilities
      before interacting with it.
InteractionOptions
      dictionaryHolds the interaction options that need to be exposed for application scripts according to the Thing Description. In this version of the specification only URI template variables are used, represented as parsed JSON objects defined in [WOT-TD].
The support for URI variables comes from the need exposed by [WOT-TD] to be able to describe existing TDs that use them, but it should be possible to write a Thing Description that would use Actions for representing the interactions that need URI variables and represent the URI variables as parameters to the Action and in that case that could be encapsulated by the implementations and the options parameter could be dismissed from the methods exposed by this API.
PropertyMap
      typeRepresents a map of Property names as strings to a value that the Property can take. It is used as a property bag for interactions that involve multiple Properties at once.
It could be defined in Web IDL (as well as
        ThingDescription) as
        a maplike
        interface from string to any.
readProperty() methodReads a Property value. Takes a string
        argument propertyName and and an optional
        InteractionOptions
        options argument. It returns a Property value represented as any
        type. The method MUST
        run the following steps:
SecurityError and
          terminate these steps.this.getThingDescription().properties[propertyName].
              SyntaxError,
              otherwise return value.SyntaxError and
          terminate these steps.readMultipleProperties() methodReads multiple Property values with
        one or multiple requests. Takes the
        propertyNames argument as a sequence of strings
        and an optional InteractionOptions
        options argument. It returns an object with keys
        from propertyNames and values returned by this
        algorithm. The method MUST run the following steps:
SecurityError and
          terminate these steps.null.NotSupportedError
            and terminate these steps.
          SyntaxError and
          terminate these steps.readAllProperties() methodReads all properties of the Thing with one or
        multiple requests. Takes an optional InteractionOptions
        options argument. It returns an object with keys
        from Property names and values returned
        by this algorithm. The method MUST run the following steps:
SecurityError and
          terminate these steps.null.NotSupportedError
            and terminate these steps.
          writeProperty() methodWrites a single Property. Takes a
        string argument propertyName, a value argument
        value and an optional InteractionOptions
        options argument. It returns success or failure.
        The method MUST run
        the following steps:
SecurityError and
          terminate these steps.SyntaxError and terminate these
            steps.
          writeMultipleProperties() methodWrites a multiple Property values with
        one request. Takes a properties argument as an
        object with keys being Property names and
        values as Property values and an optional
        InteractionOptions
        options argument. It returns success or failure.
        The method MUST run
        the following steps:
SecurityError and
          terminate these steps.SyntaxError and terminate these
            steps.
          NotSupportedError
            and terminate these steps.
          WotListener
      callbackUser provided callback that takes any
      argument and is used for observing Property changes
      and handling Event notifications. Since
      subscribing to these are WoT interactions, they are not
      modelled with software events.
observeProperty() methodMakes a request for Property value
        change notifications. Takes a string argument
        propertyName, a WotListener callback function
        listener and an optional InteractionOptions
        options argument. It returns success or failure.
        The method MUST run
        the following steps:
SecurityError and
          terminate these steps."TypeError" and
          terminate these steps.unobserveProperty() methodMakes a request for unsubscribing from Property value change notifications. Takes a string argument propertyName and returns success or failure. The method MUST run the following steps:
SecurityError and
          terminate these steps.invokeAction() methodMakes a request for invoking an Action and
        return the result. Takes a string argument
        actionName, an optional argument
        params of type any and an optional
        InteractionOptions
        options argument. It returns the result of the
        Action or an error. The method MUST run the following
        steps:
SecurityError and
          terminate these steps.
            SyntaxError and terminate these steps.
          subscribeEvent() methodMakes a request for subscribing to Event
        notifications. Takes a string argument
        eventName, a WotListener callback function
        listener and an optional InteractionOptions
        options argument. It returns success or failure.
        The method MUST run
        the following steps:
SecurityError and
          terminate these steps."TypeError" and
          terminate these steps.unsubscribeEvent() methodMakes a request for unsubscribing from Event notifications. Takes a string argument eventName and returns success or failure. The method MUST run the following steps:
SecurityError and
          terminate these steps.The next example illustrates how to fetch a TD by
      URL, create a ConsumedThing, read metadata (title),
      read property value, subscribe to property change, subscribe
      to a WoT event, unsubscribe.
try {
  let res = await fetch("https://tds.mythings.org/sensor11");
  let td = res.json();
  let thing = new ConsumedThing(td);
  console.log("Thing " + thing.getThingDescription().title + " consumed.");
} catch(e) {
  console.log("TD fetch error: " + e.message); },
};
try {
  // subscribe to property change for “temperature”
  await thing.observeProperty("temperature", value => {
    console.log("Temperature changed to: " + value);
  });
  // subscribe to the “ready” event defined in the TD
  await thing.subscribeEvent("ready", eventData => {
    console.log("Ready; index: " + eventData);
    // run the “startMeasurement” action defined by TD
    await thing.invokeAction("startMeasurement", { units: "Celsius" });
    console.log("Measurement started.");
  });
} catch(e) {
  console.log("Error starting measurement.");
}
setTimeout( () => {
  console.log(“Temperature: “ + await thing.readProperty(“temperature”));
  await thing.unsubscribe(“ready”);
  console.log("Unsubscribed from the ‘ready’ event.");
},
10000);ExposedThing
    interfaceThe ExposedThing interface is
    the server API to operate the Thing that allows
    defining request handlers, Property, Action, and Event interactions.
[SecureContext, Exposed=(Window,Worker)] interfaceExposedThing:ConsumedThing{ExposedThingsetPropertyReadHandler(DOMStringname,PropertyReadHandlerreadHandler);ExposedThingsetPropertyWriteHandler(DOMStringname,PropertyWriteHandlerwriteHandler);ExposedThingsetActionHandler(DOMStringname,ActionHandleraction); voidemitEvent(DOMStringname,anydata); Promise<void>expose(); Promise<void>destroy(); }; callbackPropertyReadHandler= Promise<any>( optionalInteractionOptionsoptions = null); callbackPropertyWriteHandler= Promise<void>(anyvalue, optionalInteractionOptionsoptions = null); callbackActionHandler= Promise<any>(anyparams, optionalInteractionOptionsoptions = null);
ExposedThingThe ExposedThing interface extends ConsumedThing. It is constructed from
      a full or partial ThingDescription object.
Note that an existing ThingDescription object can be
        optionally modified (for instance by adding or removing
        elements on its properties, actions
        and events internal properties) and the
        resulting object can used for constructing an ExposedThing object. This is the
        current way of adding and removing Property, Action and Event definitions, as illustrated in the examples.
Before invoking expose(), the ExposedThing object does not serve
        any requests. This allows first constructing ExposedThing and then initialize its
        Properties and service handlers
        before starting serving requests.
To construct an ExposedThing with the ThingDescription td, run
        the following steps:
SecurityError and terminate these
          steps.ExposedThing object.
          ConsumedThingThe readProperty(),
      readMultipleProperties(),
      readAllProperties(),
      writeProperty(),
      writeMultipleProperties(),
      writeAllProperties() methods have the same
      algorithmic steps as described in ConsumedThing,
      with the difference that making a request to the underlying
      platform MAY be
      implemented with local methods or libraries and don't
      necessarily need to involve network operations.
The implementation of ConsumedThing interface in an ExposedThing provide the
      default methods to interact with the ExposedThing.
After constructing an ExposedThing, a script can initialize
      its Properties and can set up the
      optional read, write and action request handlers (the default
      ones are provided by the implementation). The script provided
      handlers MAY use the
      default handlers, thereby extending the default behavior, but
      they can also bypass them, overriding the default behavior.
      Finally, the script would call expose() on the
      ExposedThing in order
      to start serving external requests.
PropertyReadHandler
      callbackA function that is called when an external request for
      reading a Property is received and defines
      what to do with such requests. It returns a
      Promise
      and resolves it when the value of the Property matching the name argument is
      obtained, or rejects with an error if the property is not
      found or the value cannot be retrieved.
setPropertyReadHandler() methodTakes name as string argument and
      readHandler as argument of type PropertyReadHandler.
      Sets the service handler for reading the specified Property matched by name. Throws on
      error. Returns a reference to this object for
      supporting chaining.
The readHandler callback function should
      implement reading a Property and
      SHOULD be called by
      implementations when a request for reading a Property is received from the underlying
      platform.
There MUST be at most one handler for any given Property, so newly added handlers MUST replace the previous handlers. If no handler is initialized for any given Property, implementations SHOULD implement a default property read handler based on the Thing Description.
When a network request for reading Property propertyName is received by the implementation, run the following steps:
ReferenceError in the reply and terminate
          these steps.
          setPropertyReadHandler(), invoke that
          wih propertyName, return the value with the
          reply and terminate these steps.
            NotSupportedError with the reply and
            terminate these steps.
          When a network request for observing a Property propertyName is received by the implementation, run the following steps:
PropertyWriteHandler
      callbackA function that is called when an external request for
      writing a Property is received and defines
      what to do with such requests. It expects the requested new
      value as argument and returns a
      Promise
      which is resolved when the value of the Property that matches the name
      argument has been updated with value, or rejects
      with an error if the property is not found or the value
      cannot be updated.
Note that the code in this callback function can read the property before updating it in order to find out the old value, if needed. Therefore the old value is not provided to this function.
setPropertyWriteHandler() methodTakes name as string argument and
      writeHandler as argument of type PropertyWriteHandler.
      Sets the service handler for writing the specified Property matched by name. Throws on
      error. Returns a reference to this object for
      supporting chaining.
There MUST be at most one write handler for any given Property, so newly added handlers MUST replace the previous handlers. If no write handler is initialized for any given Property, implementations SHOULD implement default property update and notifying observers on change, based on the Thing Description.
When a network request for writing a Property propertyName with a new value
        value is received, implementations SHOULD run the following
        update property steps,
        given propertyName, value and
        mode set to "single":
ReferenceError in the reply and terminate
          these steps.
          setPropertyWriteHandler(), or if there
          is a default write handler,
            "single", reply to the request with the
              new value, following to the Protocol Bindings.
              
            NotSupportedError in the reply and terminate
            these steps.
          When a network request for writing multiple Properties given in an object propertyNames is received, run the following steps:
"multiple".
          ActionHandler
      callbackA function that is called when an external request for
      invoking an Action is received and defines what
      to do with such requests. It is invoked with a
      params dictionary argument. It returns a
      Promise
      that rejects with an error or resolves if the action is
      successful.
setActionHandler() methodTakes name as string argument and
      action as argument of type ActionHandler. Sets the handler
      function for the specified Action matched by
      name. Throws on error. Returns a reference to
      this object for supporting chaining.
The action callback function will implement
      an Action and SHOULD be called by implementations when a
      request for invoking the Action is received
      from the underlying platform.
There MUST be at most one handler for any given Action, so newly added handlers MUST replace the previous handlers.
When a network request for invoking the Action identified by name is received, the runtime SHOULD execute the following steps:
ReferenceError in the reply and terminate
          these steps.
          setActionHandler(), invoke that wih
          name, return the resulting value with the
          reply and terminate these steps.
            NotSupportedError with the reply and
            terminate these steps.
          emitEvent() methodTakes name as string argument denoting an
        Event name, and a data argument of
        any type. The method MUST run the following steps:
SecurityError and terminate these
          steps.NotFoundError and terminate these steps.
          
      expose() methodStart serving external requests for the Thing, so that WoT Interactions using Properties, Actions and Events will be possible. The method MUST run the following steps:
SecurityError and
          terminate these steps."TypeError" and terminate these steps.
          Error object
          error with error.message set to
          the error code seen by the Protocol Bindings and terminate
          these steps.
          destroy() methodStop serving external requests for the Thing and destroy the object. Note that eventual unregistering should be done before invoking this method. The method MUST run the following steps:
SecurityError and
          terminate these steps.Error object
          error with error.message set to
          the error code seen by the Protocol Bindings and terminate
          these steps.
          The next example illustrates how to create an
      ExposedThing
try {
  let temperaturePropertyDefinition = {
    type: "number",
    minimum: -50,
    maximum: 10000
  };
  let tdFragment = {
    properties: {
      temperature: temperaturePropertyDefinition
    },
    actions: {
      reset: {
        description: "Reset the temperature sensor",
        input: {
          temperature: temperatureValueDefinition
        },
        output: null,
        forms: []
      },
    },
    events: {
      onchange: temperatureValueDefinition
    }
  };
  let thing1 = await WOT.produce(tdFragment);
  // initialize Properties
  await thing1.writeProperty("temperature", 0);
  // add service handlers
  thing1.setPropertyReadHandler("temperature", () => {
     return readLocalTemperatureSensor();  // Promise
  });
  // start serving requests
  await thing1.expose();
} catch (err) {
   console.log("Error creating ExposedThing: " + err);
}The next example illustrates how to add or modify a
      Property definition on an existing ExposedThingExposedThing with
      that.
try {
  // create a deep copy of thing1's TD
  let instance = JSON.parse(JSON.stringify(thing1.td));
  const statusValueDefinition = {
    type: "object",
    properties: {
      brightness: {
        type: "number",
        minimum: 0.0,
        maximum: 100.0,
        required: true
      },
      rgb: {
        type: "array",
        "minItems": 3,
        "maxItems": 3,
        items : {
            "type" : "number",
            "minimum": 0,
            "maximum": 255
        }
      }
  };
  instance["name"] = "mySensor";
  instance.properties["brightness"] = {
    type: "number",
    minimum: 0.0,
    maximum: 100.0,
    required: true,
  };
  instance.properties["status"] = statusValueDefinition;
  instance.actions["getStatus"] = {
    description: "Get status object",
    input: null,
    output: {
      status : statusValueDefinition;
    },
    forms: [...]
  };
  instance.events["onstatuschange"] = statusValueDefinition;
  instance.forms = [...];  // update
  var thing2 = new ExposedThing(instance);
  // TODO: add service handlers
  await thing2.expose();
  });
} catch (err) {
   console.log("Error creating ExposedThing: " + err);
}ThingDiscovery
    interfaceDiscovery is a distributed application that requires provisioning and support from participating network nodes (clients, servers, directory services). This API models the client side of typical discovery schemes supported by various IoT deployments.
The ThingDiscovery object is
    constructed given a filter and provides the properties and
    methods controlling the discovery process.
[SecureContext, Exposed=(Window,Worker)]
interface ThingDiscovery {
  constructor(optional ThingFilter filter = null);
  readonly attribute ThingFilter? filter;
  readonly attribute boolean active;
  readonly attribute boolean done;
  readonly attribute Error? error;
  void start();
  Promise<ThingDescription> next();
  void stop();
};
    The ThingDiscovery interface has a
      next() method and a done property,
      but it is not an Iterable. Look into Issue
      177 for rationale.
The discovery results internal slot
    is an internal queue for temporarily storing the found ThingDescription objects until they are
    consumed by the application using the next() method. Implementations MAY optimize the size of this queue
    based on e.g. the available resources and the frequency of
    invoking the next() method.
The filter
    property represents the discovery filter of type ThingFilter specified for the
    discovery.
The active
    property is true when the discovery is actively
    ongoing on protocol level (i.e. new TDs may still arrive)
    and false otherwise.
The done
    property is true if the discovery has been
    completed with no more results to report and discovery results is also empty.
The error
    property represents the last error that occured during the
    discovery process. Typically used for critical errors that stop
    discovery.
ThingDiscoveryTo create ThingDiscovery with the ThingFilter filter, run
        the following steps:
null, throw "TypeError" and
          terminate these steps.ThingDiscovery
            object.
          
            false. Set error to
            null.
          The start() method sets
      active to
      true. The stop()
      method sets active
      to false, but done may be still
      false if there are ThingDescription objects in the
      discovery results not yet consumed
      with next().
During successive calls of next(), active may be
      true or false, but done is set to
      false by next()
      only when both active is false
      and discovery results is empty.
DiscoveryMethod
      enumerationtypedef DOMString DiscoveryMethod;
      Represents the discovery type to be used:
ThingFilter
      dictionaryRepresents an object containing the constraints for discovering Things as key-value pairs.
dictionary ThingFilter {
  (DiscoveryMethod or DOMString) method = "any";
  USVString? url;
  USVString? query;
  object? fragment;
};
      The method
      property represents the discovery type that should be used in
      the discovery process. The possible values are defined by the
      DiscoveryMethod
The url property represents
      additional information for the discovery method, such as the
      URL of the target entity serving the discovery request, for
      instance the URL of a Thing Directory (if
      method is "directory") or that of a
      Thing (otherwise).
The query property represents a
      query string accepted by the implementation, for instance a
      SPARQL or JSON query. Support may be implemented locally in
      the WoT Runtime or remotely as a service
      in a Thing Directory.
The fragment property represents a
      template object used for matching property by property
      against discovered Things.
      start() methodStarts the discovery process. The method MUST run the following steps:
SecurityError and
          terminate these steps.NotSupportedError and terminate these
          steps.NotSupportedError and terminate these
                steps.
              ThingDescription
          objects.
          "any", use the widest discovery
              method supported by the underlying platform."local", use the local Thing Directory for
              discovery. Usually that defines Things
                deployed in the same device, or connected to the
                device in slave mode (e.g. sensors connected via
                Bluetooth or a serial connection).
              "directory", use the remote Thing Directory specified in
              filter.url.
              "multicast", use all the multicast
              discovery protocols supported by the underlying
              platform.active property
          to true.SyntaxError, discard td and
                continue the discovery process.
              false,
              discard td and continue the discovery
              process.false in any checks,
              discard td and continue the discovery
              process.Error object error. Set
              error.name to
              'DiscoveryError'.this.active to false.false.
      next() methodProvides the next discovered ThingDescription object. The method
        MUST run the
        following steps:
true, wait
          until the discovery results
          internal slot is not empty.
          false, set this.done to
          true and reject promise.
          ThingDescription object
          td from discovery
            results.
          
      stop() methodStops or suppresses the discovery process. It might not be supported by all discovery methods and endpoints, however, any further discovery results or errors will be discarded and the discovery is marked inactive. The method MUST run the following steps:
this.active to
          false.The following example finds ThingDescription objects of Things that are exposed by local hardware,
      regardless how many instances of WoT Runtime it
      is running. Note that the discovery can end (become inactive)
      before the internal discovery results
      queue is emptied, so we need to continue reading ThingDescription objects until done.
      This is typical with local and directory type
      discoveries.
let discovery = new ThingDiscovery({ method: "local" });
do {
  let td = await discovery.next();
  console.log("Found Thing Description for " + td.title);
  let thing = new ConsumedThing(td);
  console.log("Thing name: " + thing.getThingDescription().title);
} while (!discovery.done);The next example finds ThingDescription objects of Things listed in a Thing Directory
      service. We set a timeout for safety.
let discoveryFilter = {
  method: "directory",
  url: "http://directory.wotservice.org"
};
let discovery = new ThingDiscovery(discoveryFilter);
setTimeout( () => {
    discovery.stop();
    console.log("Discovery stopped after timeout.");
  },
  3000);
do {
  let td = await discovery.next();
  console.log("Found Thing Description for " + td.title);
  let thing = new ConsumedThing(td);
  console.log("Thing name: " + thing.getThingDescription().title);
} while (!discovery.done);
if (discovery.error) {
  console.log("Discovery stopped because of an error: " + error.message);
}The next example is for an open-ended multicast discovery, which likely won't complete soon (depending on the underlying protocol), so stopping it with a timeout is a good idea. It will likely deliver results one by one.
let discovery = new ThingDiscovery({ method: "multicast" });
setTimeout( () => {
    discovery.stop();
    console.log("Stopped open-ended discovery");
  },
  10000);
do {
  let td = await discovery.next();
  let thing = new ConsumedThing(td);
  console.log("Thing name: " + thing.getThingDescription().title);
} while (!discovery.done);A detailed discussion of security and privacy considerations for the Web of Things, including a threat model that can be adapted to various circumstances, is presented in the informative document [WOT-SECURITY-GUIDELINES]. This section discusses only security and privacy risks and possible mitigations directly relevant to the scripts and WoT Scripting API.
A suggested set of best practices to improve security for WoT devices and services has been documented in [WOT-SECURITY-BEST-PRACTICES]. That document may be updated as security measures evolve. Following these practices does not guarantee security, but it might help avoid common known vulnerabilities.
The WoT security risks and possible mitigations are concerning the following groups:
This section is normative and contains specific risks relevant for the WoT Scripting Runtime.
A typical way to compromise any process is to send it a corrupted input via one of the exposed interfaces. This can be done to a script instance using WoT interface it exposes.
In case a script is compromised or misbehaving, the underlying physical device (and potentially surrounded environment) can be damaged if a script can use directly exposed native device interfaces. If such interfaces lack safety checks on their inputs, they might bring the underlying physical device (or environment) to an unsafe state (i.e. device overheats and explodes).
If the WoT Scripting Runtime supports post-manufacturing provisioning or updates of scripts, WoT Scripting Runtime or any related data (including security credentials), it can be a major attack vector. An attacker can try to modify any above described element during the update or provisioning process or simply provision attacker's code and data directly.
Typically the WoT Scripting Runtime needs to store the security credentials that are provisioned to a WoT device to operate in WoT network. If an attacker can compromise the confidentiality or integrity of these credentials, then it can obtain access to the WoT assets, impersonate WoT things or devices or create Denial-Of-Service (DoS) attacks.
This section is non-normative.
This section describes specific risks relevant for script developers.
A script instance may receive data formats defined by the TD, or data formats defined by the applications. While the WoT Scripting Runtime SHOULD perform validation on all input fields defined by the TD, scripts may be still exploited by input data.
If a script performs a heavy functional processing on received requests before the request is authenticated, it presents a great risk for Denial-Of-Service (DOS) attacks.
During the lifetime of a WoT network, a content of a TD can change. This includes its identifier, which might not be an immutable one and might be updated periodically.
While stale TDs can present a potential problem for WoT network operation, it might not be a security risk.
The generic WoT terminology is defined in [WOT-ARCHITECTURE]: Thing, Thing Description (in short TD), Web of Things (in short WoT), WoT Interface (same as WoT network interface), Protocol Bindings, WoT Runtime, Consuming a Thing Description, Thing Directory, WoT Interactions, Property, Action, Event etc.
JSON-LD is defined in [JSON-LD] as a JSON document that is augmented with support for Linked Data.
The terms URL, URL scheme, URL host, URL path, URL record, parse a URL, absolute-URL string, path-absolute-URL string, basic URL parser are defined in [URL].
The terms MIME type, Parsing a MIME type, Serializing a MIME type, valid MIME type string, JSON MIME type are defined in [MIMESNIFF].
The terms UTF-8 encoding, UTF-8 decode, encode, decode are defined in [ENCODING].
string, parse JSON from bytes and serialize JSON to bytes, are defined in [INFRA].
The terms throw,
    creating,
    DOMString,
    Dictionary,
    ArrayBuffer,
    BufferSource,
    any,
    not
    present, DOMException,
    AbortError,
    SyntaxError,
    NotSupportedError,
    NetworkError,
    
    TypeError, NotReadableError,
    TimeoutError,
    
    NoModificationAllowedError,
    SecurityError,
    are defined in [WEBIDL].
Promise, Error, JSON, JSON.stringify, JSON.parse and internal slots are defined in [ECMASCRIPT].
The terms browsing context, top-level browsing context, global object, current settings object, executing algorithms in parallel are defined in [HTML5] and are used in the context of browser implementations.
The term secure context is defined in [WEBAPPSEC].
IANA media types (formerly known as MIME types) are defined in RFC2046.
The terms hyperlink reference and relation type are defined in [HTML5] and RFC8288.
API rationale usually belongs to a separate document, but in the WoT case the complexity of the context justifies including basic rationale here.
The WoT Interest Group and Working Group have explored different approaches to application development for WoT that have been all implemented and tested.
It is possible to develop WoT applications that only use the WoT network interface, typically exposed by a WoT gateway that presents a REST-ful API towards clients and implements IoT protocol plugins that communicate with supported IoT deployments. One such implementation is the Mozilla WebThings platform.
WoT Things show good synergy with software objects, so a Thing can be represented as a software object, with Properties represented as object properties, Actions as methods, and Events as events. In addition, metadata is stored in special properties. Consuming and exposing is done with factory methods that produce a software object that directly represents a remote Thing and its interactions. One such implementation is the Arena Web Hub project.
In the next example, a Thing that
        represents interactions with a lock would look like the
        following: the status property and the
        open() method are directly exposed on the
        object.
let lock = await WoT.consume(‘https://td.my.com/lock-00123’);
console.log(lock.status);
lock.open('withThisKey');Since the direct mapping of Things to software objects have had some challenges, this specification takes another approach that exposes software objects to represent the Thing metadata as data property and the WoT interactions as methods. One implementation is node-wot in the the Eclipse ThingWeb project, which is the current reference implementation of the API specified in this document.
The same example now would look like the following: the
        status property and the open()
        method are represented indirectly.
let res = await fetch(‘https://td.my.com/lock-00123’);
let td = await res.json();
let lock = new ConsumedThing(td);
console.log(lock.readProperty(‘status’));
lock.invokeAction(‘open’, 'withThisKey');In conclusion, the WoT WG decided to explore the third option that closely follows the [WOT-TD] specification. Based on this, a simple API can also be implemented. Since Scripting is an optional module in WoT, this leaves room for applications that only use the WoT network interface. Therefore all three approaches above are supported by [WOT-TD].
Moreover, the WoT network interface can be implemented in many languages and runtimes. Consider this API an example for what needs to be taken into consideration when designing a Scripting API for WoT.
The fetch(url) method has been part of this
      API in earlier versions. However, now fetching a TD
      given a URL should be done with an external method, such as
      the Fetch
      API or a HTTP client library, which offer already
      standardized options on specifying fetch details. The reason
      is that while simple fetch operations (covering most use
      cases) could be done in this API, when various fetch options
      were needed, there was no point in duplicating existing work
      to re-expose those options in this API.
Since fetching a TD has been scoped out, and TD validation is defined externally in [WOT-TD], that is scoped out, too. This specification expects a TD as parsed JSON object that has been validated according to the [WOT-TD] specification.
The factory methods for consuming and exposing Things are asynchronous and fully validate the
      input TD. In addition, one can also construct ConsumedThing and ExposedThing by providing a parsed and
      validated TD. Platform initialization is then
      done when needed during the WoT interactions. So applications
      that prefer validating a TD themselves, may use
      the constructors, whereas applications that leave validation
      to implementations and prefer interactions initialized up
      front SHOULD use the
      factory methods on the WoT API object.
Earlier drafts used the Observer construct, but since it has not become standard, a new design was needed that was light enough for embedded implementations. Therefore observing Property changes and handling WoT Events is done with callback registrations.
This API ended up not using software events at all, for the following reasons:
The reason to use function names like
      readProperty(),
      readMultipleProperties() etc. instead of a
      generic polymorphic read() function is that the
      current names map exactly to the "op" vocabulary
      from the Form
      definition in [WOT-TD].
The following is a list of major changes to the document. Major versions of this specification are the following:
fetch() for fetching a
              TD (delegated to external API).
            Observer and use 
              W3C TAG recommended design patterns.
            ThingDescription instead.
            ConsumedThing and ExposedThing.
            For a complete list of changes, see the github change log. You can also view the recently closed issues.
The following problems are being discussed and need most attention:
ExposedThing (it was present in
          earlier versions, but removed for complexity and a
          simpler way to do it.
        typedef objectThingDescription; [SecureContext, Exposed=(Window,Worker)] interfaceWOT{ // methods defined in UA conformance classes }; partial interfaceWOT{ Promise<ConsumedThing>consume(ThingDescriptiontd); }; partial interfaceWOT{ Promise<ExposedThing>produce(ThingDescriptiontd); }; partial interfaceWOT{ThingDiscoverydiscover(optionalThingFilterfilter = null); }; [SecureContext, Exposed=(Window,Worker)] interfaceConsumedThing{constructor(ThingDescriptiontd); Promise<any>readProperty(DOMStringpropertyName, optionalInteractionOptionsoptions = null); Promise<PropertyMap>readAllProperties(optionalInteractionOptionsoptions = null); Promise<PropertyMap>readMultipleProperties( sequence<DOMString> propertyNames, optionalInteractionOptionsoptions = null); Promise<void>writeProperty(DOMStringpropertyName,anyvalue, optionalInteractionOptionsoptions = null); Promise<void>writeMultipleProperties(PropertyMapvalueMap, optionalInteractionOptionsoptions = null); Promise<any>invokeAction(DOMStringactionName, optionalanyparams = null, optionalInteractionOptionsoptions = null); Promise<void>observeProperty(DOMStringname,WotListenerlistener, optionalInteractionOptionsoptions = null); Promise<void>unobserveProperty(DOMStringname); Promise<void>subscribeEvent(DOMStringname,WotListenerlistener, optionalInteractionOptionsoptions = null); Promise<void>unsubscribeEvent(DOMStringname);ThingDescriptiongetThingDescription(); }; dictionaryInteractionOptions{ objecturiVariables; }; typedef objectPropertyMap; callbackWotListener= void(anydata); [SecureContext, Exposed=(Window,Worker)] interfaceExposedThing:ConsumedThing{ExposedThingsetPropertyReadHandler(DOMStringname,PropertyReadHandlerreadHandler);ExposedThingsetPropertyWriteHandler(DOMStringname,PropertyWriteHandlerwriteHandler);ExposedThingsetActionHandler(DOMStringname,ActionHandleraction); voidemitEvent(DOMStringname,anydata); Promise<void>expose(); Promise<void>destroy(); }; callbackPropertyReadHandler= Promise<any>( optionalInteractionOptionsoptions = null); callbackPropertyWriteHandler= Promise<void>(anyvalue, optionalInteractionOptionsoptions = null); callbackActionHandler= Promise<any>(anyparams, optionalInteractionOptionsoptions = null); [SecureContext, Exposed=(Window,Worker)] interfaceThingDiscovery{constructor(optionalThingFilterfilter = null); readonly attributeThingFilter?filter; readonly attribute booleanactive; readonly attribute booleandone; readonly attribute Error?error; voidstart(); Promise<ThingDescription>next(); voidstop(); }; typedefDOMStringDiscoveryMethod; dictionaryThingFilter{ (DiscoveryMethodorDOMString)method= "any"; USVString?url; USVString?query; object?fragment; };
Special thanks to former editor Johannes Hund (until August 2017, when at Siemens AG) and Kazuaki Nimura (until December 2018) for developing this specification. Also, the editors would like to thank Dave Raggett, Matthias Kovatsch, Michael Koster, Elena Reshetova, Michael McCool as well as the other WoT WG members for their comments, contributions and guidance.