diff --git a/releases/note2/Overview.html b/releases/note2/Overview.html index 693d602..7a42654 100644 --- a/releases/note2/Overview.html +++ b/releases/note2/Overview.html @@ -2309,7 +2309,7 @@

6. The
WebIDL[SecureContext, WebIDLpartial namespace WOT {ExposedThingInit;
+"" class="idlNamespace" id="idl-def-wot-631108207" data-title="WOT">
 
 partial namespace WebIDLpartial namespace WOT {WebIDLpartial namespace WOT {
       
WebIDLpartial namespace WOT {
+
+
+  
+  
+  
+  
+  
+  
+  
+  
+  Web of Things (WoT) Scripting API
+  
+  
+  
+  
+  
+  
+  
+  
+  
+
+
+  
+

+

Web of Things (WoT) Scripting + API

+

W3C Group Note +

+
+ + More details about this document + +
+
This version:
+
+ + https://www.w3.org/TR/2023/NOTE-wot-scripting-api-20230928/ +
+
Latest published version:
+
+ https://www.w3.org/TR/wot-scripting-api/ +
+
Latest editor's draft:
+
+ https://w3c.github.io/wot-scripting-api/ +
+
History:
+
+ + https://www.w3.org/standards/history/wot-scripting-api/ +
+
+ Commit + history +
+
Editors:
+
+ Zoltan Kis (Intel) +
+
+ Daniel Peintner (Siemens + AG) +
+
+ Cristiano Aguzzi + (Invited Expert) +
+
Johannes Hund + (Former Editor, when at Siemens AG)
+
Kazuaki Nimura + (Former Editor, at Fujitsu Ltd.)
+
Feedback:
+
+ GitHub + w3c/wot-scripting-api (pull + requests, + new issue, open + issues) +
+
+ + public-wot-wg@w3.org with subject line + [wot-scripting-api] … message topic … + (archives) +
+
Repository
+
+ On + GitHub +
+
+ File a + bug +
+
Contributors
+
+ + Contributors on GitHub +
+
+
+ +
+
+
+

Abstract

+

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).

+

The main Web of + Things (WoT) concepts are described in the + Web + of Things (WoT) Architecture 1.1 specification.

+

Scripting is an optional building block in WoT and it is + typically used in gateways or browsers 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 + TD Directory.

+

This specification describes an application programming + interface (API) 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 APIs defined in this document deliberately follow the + + Web of Things (WoT) Thing Description 1.1 + specification closely. It is possible to implement more + abstract APIs on top of them, or implementing directly the WoT + network facing interface (i.e. the WoT Interface).

+
+
+ Editor's note +
+

This specification is implemented at least by the + Eclipse Thingweb + project also known as node-wot, + which is considered the reference open source implementation + at the moment. Check its source + code, including + examples.

+
+
+
+

Status of This Document

+

This section describes the status of this document at + the time of its publication. 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.

+
+
+ Editor's note: The + W3C WoT WG is asking for feedback +
+

Please contribute to this draft using the + GitHub + Issues page 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 Group Note using the Note + track.

+

This Group Note is endorsed by the Web of Things Working + Group, but is not endorsed by W3C itself nor its + Members.

+

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.

+

The W3C Patent Policy does + not carry any licensing requirements or commitments on this + document.

+

This document is governed by the 12 June 2023 + W3C Process + Document.

+
+ +
+
+

1. + Introduction

+
+

WoT provides layered interoperability based on how Things are used: "consumed" and + "exposed", as defined in the Web + of Things (WoT) Architecture 1.1 terminology.

+

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: + This specification describes how to expose and consume + Things by a script. Also, + it defines a generic API for Thing discovery. +
+
+
+ Note +
+

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.

+
+
+
+ Note +
+

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.

+
+
+
+
+

2. + Use Case Scenarios

+
+

This section is non-normative.

+

The business use cases listed in the [WOT-USE-CASES] + document may be implemented using this API, based on the + scripting use case scenarios described here.

+
+
+

2.1 Consuming a Thing

+
+ +
+
+
+

2.2 + Exposing a Thing

+
+
    +
  • Exposing the Thing + includes generating the protocol bindings in order to + access lower level functionality. +
  • +
  • Create a local Thing to be exposed, based on + + Thing Description. +
  • +
  • The following use cases can be implemented before + creating the Thing + by editing the Thing Description: + +
    +
    + Editor's note +
    +

    After evaluating dynamic modifications to + Thing + Descriptions through several versions of this API, + the editors concluded that the simplest way to + represent these use cases is to take an existing + TD, modify it (i.e. add + or remove definitions) and then create a new Thing based on the + modified TD.

    +
    +
  • +
  • Emit a WoT Event, + i.e. notify all subscribed listeners. +
  • +
  • Register service handlers for external requests: +
      +
    • to retrieve a Property value; +
    • +
    • to update a Property value; +
    • +
    • to observe a Property; +
    • +
    • to unobserve a Property; +
    • +
    • to invoke an Action: take the parameters + from the request, execute the defined action, and + return the result; +
    • +
    • to subscribe to an Event; +
    • +
    • to unsubscribe from an Event. +
    • +
    +
  • +
+
+
+
+

2.3 + Discovery

+
+
    +
  • Discover Things + in a network by sending a broadcast request. +
  • +
  • Discover Things + running in the local WoT Runtime. +
  • +
  • Discover nearby Things, for instance connected + by NFC or Bluetooth, or within a geo-fence. +
  • +
  • Discover Things + by sending a discovery request to a given TD Directory. +
  • +
  • Discover Things + filtered by filters defined on + Thing Descriptions +
  • +
  • Discover Things + filtered by semantic queries. +
  • +
  • Stop or suppress an ongoing discovery process.
  • +
  • Optionally specify a timeout to the discovery process + after which it is stopped/suppressed.
  • +
+
+
+
+
+

3. + Conformance

+
+

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.

+
+
+ Editor's note +
+

This specification used to be a Working Draft + which was expected to become a W3C Recommendation. + However, it is now a WG Note which contains informative + statements only. Therefore we need to consider how to deal + with the description within this Conformance section.

+
+

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.

+
+
+ WoT Consumer UA +
+
+

Implementations of this conformance class MUST implement the ConsumedThing + interface and the consume() method on the + WoT + API object.

+
+
+ WoT Producer UA +
+
+

Implementations of this conformance class MUST implement ExposedThing + interface and the produce() method on the + WoT + API object.

+
+
+ WoT Discovery UA +
+
+

Implementations of this conformance class MUST implement the ThingDiscoveryProcess + interface, the discover() method, the + exploreDirectory() method, and the + requestThingDescription() 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].

+
+
+
+

4. Terminology and conventions

+
+

The generic WoT terminology is defined in [WOT-ARCHITECTURE]: + Thing, + Thing + Description (in short TD), Partial TD, Web of Things (in short + WoT), WoT Interface, + Protocol Bindings, WoT + Runtime, Consuming a Thing Description, + TD Directory, Property, Action, Event, + + DataSchema, + Form, + SecurityScheme, + NoSecurityScheme + etc.

+

WoT Interaction is a synonym for + + Interaction + Affordance. An Interaction + Affordance (or shortly, affordance) is the term used in + [WOT-TD] + when referring to Thing + capabilities, as explained in TD + issue 282. However, this term is not well understood + outside the TD semantic + context. Hence for the sake of readability, this document will + use the previous term WoT interaction or, simply, + interaction instead.

+

WoT network + interface synonym for WoT Interface.

+

JSON Schema is defined in + these + specifications.

+

Promise, + Error, + JSON, + JSON.stringify, + JSON.parse, + + internal method and + internal slot are defined in [ECMASCRIPT].

+
+
+
+

5. The ThingDescription + type

+
+
WebIDLtypedef 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.

+
+
+

5.1 Fetching a Thing + Description

+
+

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.

+
+
+ Example + 1: Fetching a + Thing Description +
+
try {
+  let res = await fetch('https://tds.mythings.biz/sensor11');
+  // ... additional checks possible on res.headers
+  let td = await res.json();
+  let thing = await WOT.consume(td);
+  console.log("Thing name: " + thing.getThingDescription().title);
+} catch (err) {
+  console.log("Fetching TD failed", err.message);
+}
+
+
+
+
+

5.2 Expanding a Thing + Description

+
+

Note that the + Web of Things (WoT) Thing Description 1.1 + specification allows using a shortened Thing + Description by the means of + defaults and requiring clients to expand them with + default values specified in the + Web of Things (WoT) Thing Description 1.1 + specification for the properties that are not explicitly + defined in a given TD.

+
+ To expand a TD given + td, run the + following steps: +
    +
  1. For each item in the + TD default values table from [WOT-TD], + if the term is not defined in td, add the term definition + with the default value specified in [WOT-TD]. +
  2. +
+
+
+
+
+

5.3 Validating a Thing + Description

+
+

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: +
    +
  1. If + JSON Schema validation fails on td, throw a + "TypeError" + and stop. +
  2. +
+
+
+ Editor's note: + Handling default values +
+

Additional steps may be added to fill the + default values of mandatory fields.

+
+
+
+
+
+
+

6. The + WOT + namespace

+
+

Defines the WoT API object + as a singleton and contains the API methods, grouped by + conformance classes.

+
WebIDL[SecureContext, Exposed=(Window,Worker)]
+namespace WOT {
+  // methods defined in UA conformance classes
+};
+
+
+

6.1 The consume() + method

+
+
WebIDLpartial namespace 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: +
    +
  1. Return a + Promise promise and execute the next steps + + in parallel. +
  2. +
  3. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  4. +
  5. Let thing be a + new ConsumedThing + object constructed from td. +
  6. +
  7. Set up the WoT Interactions + based on introspecting td as explained in + [WOT-TD] + and [WOT-PROTOCOL-BINDINGS]. + Make a request to the underlying platform to initialize + the Protocol + Bindings. +
    +
    + Editor's note +
    +

    Implementations encapsulate the + complexity of how to use the Protocol + Bindings for implementing WoT interactions. + In the future elements of that could be + standardized.

    +
    +
  8. +
  9. + Resolve + promise with + thing. +
  10. +
+
+
+ Editor's note +
+

Note the difference between constructing + ConsumedThing + and using the consume() method: the latter + also initializes the protocol bindings, whereas a simple + constructed object will not have WoT Interactions + initialized until they are invoked.

+
+
+
+
+
+

6.2 The produce() + method

+
+
WebIDLtypedef object ExposedThingInit;
+
+partial namespace WOT {
+  Promise<ExposedThing> produce(ExposedThingInit init);
+};
+
+ Belongs to the WoT Producer conformance + class. Expects a init 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 init + object is an instance of the ExposedThingInit + type. Specifically, an ExposedThingInit + value is a dictionary used for the initialization of an + ExposedThing + and it represents a Partial TD as described in + the [WOT-ARCHITECTURE]. + As such, it has the same structure of a Thing Description + but it may omit some information. The method MUST run the following steps: +
    +
  1. Return a + Promise promise and execute the next steps + + in parallel. +
  2. +
  3. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  4. +
  5. Let thing be a + new ExposedThing + object constructed with init. +
  6. +
  7. + Resolve + promise with + thing. +
  8. +
+
+
+
+

6.2.1 Expand an + ExposedThingInit

+
To expand an + ExposedThingInit given init and obtain a valid + td as a result, run + the following steps: +
    +
  1. Run validate + an ExposedThingInit on init. If that fails, + throw + SyntaxError + and stop. +
  2. +
  3. Let td be the + result of running clone + given init. +
  4. +
  5. For each scheme + in td.["securityDefinitions"], + make a request to the underlying platform to check if it + is supported by at least one Protocol Binding. + If not, then remove scheme from td. +
  6. +
  7. If td.["security"] + does not exist in + td.["securityDefinitions"], + then remove security from td. +
  8. +
  9. For each affordance in td.properties, td.actions and td.events, run the following + sub-steps: +
      +
    1. For each form in + affordance.forms: +
        +
      1. If form.contentType is not recognized by + the runtime as valid remove contentType from form.
      2. +
      3. If form.href + has an unknown schema, remove href from form.
      4. +
      5. If form.href + is absolute and its authority it is + not recognized by the runtime as valid, remove + href from + form.
      6. +
      7. If form.href + is already in use by other ExposedThings, + remove href from + form. +
      8. +
      +
    2. +
    +
  10. +
  11. Search for missing required properties in + td accordingly to + + TD JSON Schema. +
    +
    + Editor's note +
    +

    The editors find this step vague. It will + be improved or removed in the next iteration.

    +
    +
  12. +
  13. For each missing property run these + sub-steps: +
      +
    1. If missing is title + generate a runtime unique name and assign to + title.
    2. +
    3. If missing is @context + assign the latest supported Thing Description context + URI.
    4. +
    5. If missing is instance + assign the string 1.0.0.
    6. +
    7. If missing is forms + generate a list of Forms using the available + Protocol + Bindings and content types encoders. Then + assign the obtained list to forms. +
    8. +
    9. If missing is security + assign the label of the first supported SecurityScheme in + securityDefinitions field. If no + SecurityScheme + is found generate a NoSecurityScheme + called nosec and assign the string + nosec to security. +
      +
      + Issue 1 +
      +

      The discussion about how to properly + generate a value for security is + still open. See issue + #299

      +
      +
    10. +
    11. If missing is href define + formStub as the partial Form that does not + have href. Generate a valid + url using the first + Protocol + Binding that satisfy the requirements of + formStub. Assign url to href. If not + Protocol + Binding can be found remove formStub + from td. +
    12. +
    13. Add missing to td with value as + value
    14. +
    +
  14. +
  15. Run validate a TD on + td. If that + fails re-throw + the error and stop +
  16. +
  17. Return td
  18. +
+
+
+
+

+ 6.2.2 Validating an + ExposedThingInit

+
To validate an ExposedThingInit given + init, run the + following steps: +
    +
  1. Parse + TD JSON Schema and load it in object called + exposedThingInitSchema +
  2. +
  3. let optional be a list + containing the following strings: title, + @context, instance, + forms, security, and + href.
  4. +
  5. For each property and sub-property key in + exposedThingInitSchema + equals to required execute the following + steps: +
      +
    1. if key value is an + Array then remove all its elements equal + to the elements in optional
    2. +
    3. if key value is a + string then if value is equal + to one of the elements in optional remove key from + exposedThingInitSchema
    4. +
    +
  6. +
  7. Return the result of validating + an object with JSON Schema given init and exposedThingInitSchema. +
    +
    + Editor's note +
    +

    The validating an object with JSON Schema + steps are still under discussion. Currently this + specification reference to the validation process of + JSONSchema. Please follow this + document when validating init with exposedThingInitSchema. Notice that + the working group is evaluating an alternative formal + approach.

    +
    +
  8. +
+
+
+
+
+

6.3 The discover() method

+
+
WebIDLpartial namespace WOT {
+  Promise<ThingDiscoveryProcess> discover(optional ThingFilter filter = {});
+};
+
+ 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 of type + ThingFilter. + The method MUST run the following + steps: +
    +
  1. Return a + Promise promise and execute the next steps + + in parallel. +
  2. +
  3. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  4. +
  5. If discovery is not supported by the implementation, + reject + promise with + + NotSupportedError and stop. +
  6. +
  7. Let discovery be a new + ThingDiscoveryProcess + object. +
  8. +
  9. Set discovery.[[filter]] to + filter. +
  10. +
  11. Set discovery.[[url]] to + undefined. +
  12. +
  13. If filters in general are not supported by the + implementation and filter is not undefined + or null, reject + promise with + + NotSupportedError and stop. +
  14. +
  15. If discovery cannot be started by the underlying + platform, reject + promise with + OperationError + and stop. +
  16. +
  17. Request the underlying platform to start the + discovery process + by any means supported and provisioned in the WoT Runtime + for which the script has access to, passing + discovery + to it. +
  18. +
  19. + Resolve + promise with + discovery. +
  20. +
+
+
+
+
+

6.4 The exploreDirectory() + method

+
+
WebIDLpartial namespace WOT {
+  Promise<ThingDiscoveryProcess> exploreDirectory(USVString url,
+      optional ThingFilter filter = {});
+};
+
+ Belongs to the WoT Discovery conformance + class. Starts the discovery process that given a TD Directory URL, + will provide ThingDescription + objects for Thing Descriptions + that match an optional filter argument of type + ThingFilter. + The method MUST run the following + steps: +
    +
  1. Return a + Promise promise and execute the next steps + + in parallel. +
  2. +
  3. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  4. +
  5. If directory discovery is not supported by the + implementation, reject + promise with + + NotSupportedError and stop. +
  6. +
  7. Let discovery be a new + ThingDiscoveryProcess + object. +
  8. +
  9. Set discovery.[[url]] to + url. +
  10. +
  11. Set discovery.[[filter]] to + filter. +
  12. +
  13. Request the underlying platform to start the + directory discovery process. +
    +
    + Note +
    +

    This is a placeholder for more details in + the discovery algorithm. Implementations should + follow the procedures described in the + [WOT-DISCOVERY] + and [WOT-PROTOCOL-BINDINGS] specifications. Some + normative steps are indicated below.

    +
    +
      +
    1. If url is not a + + TD Directory or if the underlying + implementation cannot support the Protocol + Binding indicated by url, reject + promise with + + NotSupportedError and terminate + these steps. +
    2. +
    3. If filters in general are not supported by the + implementation and filter is not + undefined or null, + reject + promise with + + NotSupportedError and stop. +
    4. +
    5. Run the discovery + process given discovery. +
      +
      + Note +
      +

      From this point on, errors are + recorded only on + error, but don't affect + promise any + longer.

      +
      +
    6. +
    +
  14. +
  15. + Resolve + promise with + discovery. +
  16. +
+
+
+
+
+

+ 6.5 The requestThingDescription() + method

+
+
WebIDLpartial namespace WOT {
+  Promise<ThingDescription> requestThingDescription(USVString url);
+};
+
+ Belongs to the WoT Discovery conformance + class. Requests a Thing Description + from the given URL. The method MUST run the following steps: +
    +
  1. Return a + Promise promise and execute the next steps + + in parallel. +
  2. +
  3. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  4. +
  5. If fetching a Thing + Description is not supported by the implementation, + reject + promise with + + NotSupportedError and stop. +
  6. +
  7. Let td be the + result of making a request to the underlying platform to + retrieve the Thing Description + using the Protocol Binding + specified by url. If retrieving + td fails, + reject + promise with + NotFoundError + and stop. +
  8. +
  9. + Resolve + promise with + td. +
  10. +
+
+
+
+
+
+

7. Handling interaction data

+
+

As specified in the + Web of Things (WoT) Thing Description 1.1 + specification, WoT interactions extend + DataSchema and include + a number of possible Forms, out of + which one is selected for the interaction. The + Form contains a contentType to describe the + data. For certain content types, a DataSchema is defined, based on + JSON Schema, making + possible to represent these contents as JavaScript types and + eventually set range constraints on the data.

+
+
+

7.1 The InteractionInput + type

+
+
WebIDLtypedef any DataSchemaValue;
+typedef (ReadableStream or DataSchemaValue) InteractionInput;
+

Belongs to the WoT Consumer conformance + class and represents the WoT Interaction data + provided by application scripts to the UA.

+

DataSchemaValue is an + ECMAScript value that is accepted for DataSchema defined in + [WoT-TD]. + The possible values MUST be of type + + null, + boolean, + number, + string, array, + or object.

+

ReadableStream + is meant to be used for WoT Interactions that + don't have a DataSchema in the Thing + Description, only a Form's + contentType that can be represented by a + stream.

+

In practice, any + ECMAScript value may be used for WoT + Interactions that have a DataSchema defined in the + Thing Description, or + which can be mapped by implementations to the + Form's + contentType defined in the Thing + Description.

+

The algorithms in this document specify how exactly input + data is used in WoT Interactions.

+
+
+
+

7.2 The InteractionOutput + interface

+
+

Belongs to the WoT Consumer conformance + class. An InteractionOutput + object is always created by the implementations and exposes + the data returned from WoT Interactions to + application scripts.

+

This interface exposes a convenience function which should + cover the vast majority of IoT use cases: the value() function. Its + implementation will inspect the data, parse it if adheres to + a DataSchema, or otherwise fail + early, leaving the underlying stream undisturbed so that + application scripts could attempt reading the stream + themselves, or handling the data as ArrayBuffer.

+
WebIDL[SecureContext, Exposed=(Window,Worker)]
+interface InteractionOutput {
+  readonly attribute ReadableStream? data;
+  readonly attribute boolean dataUsed;
+  readonly attribute Form? form;
+  readonly attribute DataSchema? schema;
+  Promise<ArrayBuffer> arrayBuffer();
+  Promise<DataSchemaValue> value();
+};
+

The data property represents the raw + payload in WoT + Interactions as a ReadableStream, + initially null.

+

The dataUsed property tells whether + the data stream has been + disturbed. Initially false.

+

The form attribute + represents the Form + selected from the Thing Description for + this WoT + Interaction, initially null.

+

The schema attribute represents the + DataSchema (defined + in [WoT-TD]) + of the payload as a JSON + object, initially null.

+

The [[value]] internal slot represents the parsed + value of the WoT Interaction, + initially undefined (note that null + is a valid value).

+
+
+

7.2.1 The value() + function

+
Parses the data returned by the WoT + Interaction and returns a value with the type described + by the interaction DataSchema if that exists, + or by the contentType of the interaction + Form. The method + MUST run the following steps: +
    +
  1. Return a + Promise promise and execute the next steps + + in parallel. +
  2. +
  3. If this.[[value]] is not + undefined, resolve + promise with that value + and stop. +
  4. +
  5. If this.data is not a + + ReadableStream or if dataUsed is + true, or if form is not an + object + or if schema or its type are + null or undefined, then + reject + promise with + + NotReadableError and stop. +
  6. +
  7. If form.contentType is not + application/json and if a mapping is not + available in the Protocol Bindings + from form.contentType to + [JSON-SCHEMA], + reject + promise with + + NotSupportedError and stop. +
  8. +
  9. Let reader be the result of + getting a reader from data. If that + threw an exception, reject + promise with that + exception and stop. +
  10. +
  11. Let bytes be the result of + reading all bytes from data with + reader. +
  12. +
  13. Set dataUsed to true.
  14. +
  15. If form.contentType is not + application/json and if a mapping is + available in the Protocol + Bindings from + form.contentType to + [JSON-SCHEMA], + transform bytes with that mapping. +
  16. +
  17. Let json be the result of running + + parse JSON from bytes on bytes. If that + throws, reject + promise with that + exception and stop. +
  18. +
  19. Set [[value]] to the result of + running check data schema + on json and schema. If that throws, + reject + promise with that + exception and stop. +
  20. +
  21. + Resolve + promise with [[value]]. +
  22. +
+
+
+
+

7.2.2 The arrayBuffer() + function

+
When invoked, MUST run the + following steps: +
    +
  1. Return a + Promise promise and execute the next steps + + in parallel. +
  2. +
  3. If data is not + ReadableStream or if dataUsed is + true, reject + promise with + + NotReadableError and stop. +
  4. +
  5. Let reader be the result of + getting a reader from data. If that + threw an exception, reject + promise with that + exception and stop. +
  6. +
  7. Let bytes be the result of + reading all bytes from data with + reader. +
  8. +
  9. Set dataUsed to true.
  10. +
  11. Let arrayBuffer be a new + + ArrayBuffer whose contents are + bytes. If that throws, reject + promise with that + exception and stop. +
  12. +
  13. + Resolve + promise with + arrayBuffer. +
  14. +
+
+
+
+

+ 7.2.3 The check data schema + algorithm

+
To run the check data schema + steps on payload and schema, +
    +
  1. Let type be schema.type.
  2. +
  3. If type is "null" and if + payload is not null, throw + + TypeError and stop, otherwise return + null. +
  4. +
  5. If type is "boolean" and + payload is a falsy value or its byte length is + 0, return false, otherwise return + true.
  6. +
  7. If type is "integer" or + "number", +
      +
    1. If payload is not a number, throw + + TypeError and stop. +
    2. +
    3. If form.minimum is defined + and payload is smaller, or if + form.maximum is defined and + payload is bigger, throw a + + RangeError and stop. +
    4. +
    +
  8. +
  9. If type is "string", return + payload.
  10. +
  11. If type is "array", run these + sub-steps: +
      +
    1. If payload is not an array, throw + + TypeError and stop. +
    2. +
    3. If form.minItems is defined + and payload.length is less than + that, or if form.maxItems is + defined and payload.length is + more than that, throw + RangeError and stop. +
    4. +
    5. Let payload be an array of items + obtained by running the check data + schema steps on each element item of + payload and schema.items. If this + throws at any stage, re-throw that exception and + stop. +
    6. +
    +
  12. +
  13. If type is "object", run + these sub-steps: +
      +
    1. If payload or + schema.properties is not an + object, + throw + TypeError and stop. +
    2. +
    3. For each key in payload: +
        +
      1. Let prop be + payload[key].
      2. +
      3. Let propSchema be + interaction.properties[key].
      4. +
      5. Let prop be the result of running + the check data + schema steps on prop and + propSchema. If this throws, re-throw + that exception and stop. +
      6. +
      +
    4. +
    5. Let required be schema.required if that is + an array or an empty array otherwise.
    6. +
    7. For each key in required, + if key is not present in + payload, throw + SyntaxError and stop. +
    8. +
    +
  14. +
  15. Return payload.
  16. +
+
+
+
+

+ 7.2.4 The create + interaction request algorithm

+
For a given ConsumedThing + object thing, in order + to create + interaction request given a source, form and schema, + run these steps: +
    +
  1. Let idata be a new an InteractionOutput + object. +
  2. +
  3. Set idata.form + to form, set + idata.schema to + schema, set + |idata.data to null and set + idata.[[value]] to + undefined. +
  4. +
  5. If source is + a + ReadableStream object, let + idata.data be source, return + idata and stop. +
  6. +
  7. If schema and its + type are defined and not null, run + these sub-steps: +
      +
    1. If type is "null" and + source is not + "null", throw + TypeError and stop. +
    2. +
    3. If type is "boolean" and + source is a + falsy value, set + idata.[[value]] to + false, otherwise set it to + true. +
    4. +
    5. If type is "integer" or + "number" and source is not a number, or + if form.minimum is defined and + source is + smaller, or if form.maximum is defined and + source is + bigger, throw + RangeError and stop. +
    6. +
    7. If type is "string" and + source is not + a string, let idata.[[value]] be + the result of running + serialize JSON to bytes given source. If that is + failure, throw + SyntaxError and stop. +
    8. +
    9. If type is "array", run + these sub-steps: +
        +
      1. If source is not an array, + throw a + TypeError and stop. +
      2. +
      3. Let length be the length of + source.
      4. +
      5. If form.minItems is defined + and length is less than that, or if + form.maxItems is defined + and length is more than that, throw + + RangeError and stop. +
      6. +
      7. For each item in source, let + itemschema be + schema.items and let + item be the result of running the + create + interaction request steps given + item, form and itemschema. If + this throws, re-throw that exception and stop. +
      8. +
      9. Set data.[[value]] + to source. +
      10. +
      +
    10. +
    11. If type is "object", run + these sub-steps: +
        +
      1. If source is not an object, + throw + TypeError and stop. +
      2. +
      3. If schema.properties is + not an object, throw + TypeError and stop. +
      4. +
      5. For each key in source, +
          +
        1. Let value be source[key].
        2. +
        3. Let propschema be + properties.interactions[key].
        4. +
        5. Let value be the result of + running the create + interaction request steps on + value, form and propschema. + If this throws, re-throw that exception and + stop. +
        6. +
        +
      6. +
      7. If schema.required is an + array, for each item in + required check if item is a + property name in source. If an + item is not found in source, throw + + SyntaxError and stop. +
      8. +
      9. Set data.[[value]] + to source. +
      10. +
      +
    12. +
    +
  8. +
  9. Set idata.data to a new + + ReadableStream created from + idata.[[value]] + + internal slot as its underlying + source. +
  10. +
  11. Return idata.
  12. +
+
+
+
+

+ 7.2.5 The parse + interaction response algorithm

+
For a given ConsumedThing + object thing, in order + to parse + interaction response given response, + form and schema, run these steps: +
    +
  1. Let result be a new InteractionOutput + object. +
  2. +
  3. Let result.schema be schema.
  4. +
  5. Let result.form be form.
  6. +
  7. Let result.data be a new + + ReadableStream with the payload data of + response as its underlying + source. +
  8. +
  9. Let result.dataUsed be + false.
  10. +
  11. Return result.
  12. +
+
+
+
+
+

+ 7.3 Using InteractionInput + and InteractionOutput

+
+

As illustrated in the next pictures, the + InteractionOutput + interface is used every time implementations provide data to + scripts, while InteractionInput + is used when the scripts pass data to the implementation.

+
+ +
+ Figure + 1 Data structures used when reading data +
+
+

When a ConsumedThing + reads data, it receives it from the implementation as an + InteractionOutput + object.

+

An ExposedThing + read handler + provides the read data to the implementation as + InteractionInput.

+
+ +
+ Figure + 2 Data structures used when writing data +
+
+

When a ConsumedThing + writes data, it provides it to the implementation as + InteractionInput.

+

An ExposedThing + write + handler receives data from to implementation as an + InteractionOutput + object.

+
+ +
+ Figure + 3 Data structures used when invoking an + Action +
+
+

When a ConsumedThing + invokes an Action, + it provides the parameters as InteractionInput + and receives the output of the Action as an InteractionOutput + object.

+

An ExposedThing + action handler + receives arguments from the implementation as an + InteractionOutput + object and provides Action output as + InteractionInput + to the implementation.

+
+
+
+

7.4 + Error handling

+
+

The algorithms in this API define the errors to be + reported to application scripts.

+

The errors reported to the other communication end are + mapped and encapsulated by the Protocol + Bindings.

+
+ +
+ Figure 4 Error + handling in WoT interactions +
+
+
+
+ Editor's note +
+

This topic is still being discussed in Issue + #200. A standardized error mapping would be needed in + order to ensure consistency in mapping script errors to + protocol errors and vice versa. In particular, when + algorithms say "error received from the Protocol Bindings", + that will be factored out as an explicit error mapping + algorithm. Currently, that is encapsulated by + implementations.

+
+
+
+
+
+

8. The ConsumedThing + interface

+
+

Represents a client API to operate a Thing. Belongs to the WoT Consumer conformance + class.

+
WebIDL[SecureContext, Exposed=(Window,Worker)]
+interface ConsumedThing {
+  constructor(ThingDescription td);
+  Promise<InteractionOutput> readProperty(DOMString propertyName,
+                              optional InteractionOptions options = {});
+  Promise<PropertyReadMap> readAllProperties(
+                              optional InteractionOptions options = {});
+  Promise<PropertyReadMap> readMultipleProperties(
+                              sequence<DOMString> propertyNames,
+                              optional InteractionOptions options = {});
+  Promise<undefined> writeProperty(DOMString propertyName,
+                              InteractionInput value,
+                              optional InteractionOptions options = {});
+  Promise<undefined> writeMultipleProperties(
+                              PropertyWriteMap valueMap,
+                              optional InteractionOptions options = {});
+  /*Promise<undefined> writeAllProperties(
+                              PropertyWriteMap valueMap,
+                              optional InteractionOptions options = {});*/
+  Promise<InteractionOutput> invokeAction(DOMString actionName,
+                              optional InteractionInput params = {},
+                              optional InteractionOptions options = {});
+  Promise<Subscription> observeProperty(DOMString name,
+                              InteractionListener listener,
+                              optional ErrorListener onerror,
+                              optional InteractionOptions options = {});
+  Promise<Subscription> subscribeEvent(DOMString name,
+                              InteractionListener listener,
+                              optional ErrorListener onerror,
+                              optional InteractionOptions options = {});
+  ThingDescription getThingDescription();
+};
+
+dictionary InteractionOptions {
+  unsigned long formIndex;
+  object uriVariables;
+  any data;
+};
+
+[SecureContext, Exposed=(Window,Worker)]
+interface Subscription {
+  readonly attribute boolean active;
+  Promise<undefined> stop(optional InteractionOptions options = {});
+};
+
+[SecureContext, Exposed=(Window,Worker)]
+interface PropertyReadMap {
+  readonly maplike<DOMString, InteractionOutput>;
+};
+
+[SecureContext, Exposed=(Window,Worker)]
+interface PropertyWriteMap {
+  readonly maplike<DOMString, InteractionInput>;
+};
+
+callback InteractionListener = undefined(InteractionOutput data);
+callback ErrorListener = undefined(Error error);
+
+
+ Editor's note: Where + is the writeAllProperties method? +
+

The writeAllProperties() method is + still under discussion. Meanwhile, use the + writeMultipleProperties() method instead.

+
+
+
+

8.1 Internal slots for ConsumedThing

+
+

A ConsumedThing + object has the following + internal slots:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Internal SlotInitial valueDescription (non-normative)
[[td]]null + The Thing + Description of the ConsumedThing. +
[[activeSubscriptions]]{} + An ordered + map keyed on + a string + name representing the Event and + value is a + Subscription + object. +
[[activeObservations]]{} + An ordered + map keyed on + a string + name representing a Property and + value is a + Subscription + object. +
+
+
+
+

8.2 Constructing + ConsumedThing

+
+

After 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: +
    +
  1. Run the validate a TD steps on + td. If that + fails, throw + SyntaxError + and stop. +
  2. +
  3. Run the expand a TD steps on + td. If that + fails, re-throw + the error and stop. +
  4. +
  5. Let thing be a + new ConsumedThing + object. +
  6. +
  7. Set the + internal slot [[td]] of + thing to + td. +
  8. +
  9. Return thing.
  10. +
+
+
+
+
+

8.3 The getThingDescription() + method

+
+

Returns the [[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.

+
+
+
+

8.4 The readProperty() + method

+
+
+ Reads a Property value. Takes as + arguments propertyName and + optionally options. It returns a + + Promise that resolves with a + Property value represented + as an InteractionOutput + object or rejects on error. The method MUST run the following steps: +
    +
  1. Return a + Promise promise and execute the next steps + + in parallel. +
  2. +
  3. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  4. +
  5. Let interaction be [[td]].properties.propertyName. +
  6. +
  7. If interaction is undefined, + reject + promise with a + NotFoundError + and stop. +
  8. +
  9. If option.formIndex is defined, + let form be the Form associated with + formIndex in + interaction.forms array, otherwise + let form be a Form in + interaction.forms whose + op is readproperty, selected by + the implementation. +
  10. +
  11. If form is failure, reject + promise with a + SyntaxError + and stop. +
  12. +
  13. Make a request to the underlying platform (via the + Protocol Bindings) + to retrieve the value of the propertyName Property using + form and the optional URI templates given in + options.uriVariables. +
  14. +
  15. If the request fails, reject + promise with the error + received from the Protocol + Bindings and stop. +
  16. +
  17. Let response be the response received to + the request.
  18. +
  19. Let data be the result of running + parse + interaction response on response, + form and interaction. If that + fails, reject + promise with a + SyntaxError + and stop. +
  20. +
  21. + Resolve + promise with + data. +
  22. +
+
+
+
+
+

8.5 The readMultipleProperties() + method

+
+
+ Reads multiple Property values with one + request. Takes as arguments propertyNames and optionally + options. It + returns a + Promise that resolves with a + PropertyReadMap + object that maps keys from propertyNames to values returned by + this algorithm. The method MUST + run the following steps: +
    +
  1. Return a + Promise promise and execute the next steps + + in parallel. +
  2. +
  3. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  4. +
  5. If option.formIndex is defined, + let form be the Form associated with + formIndex in the [[td]].forms + array, otherwise let form be the Form in + [[td]].forms + array whose op is + readmultipleproperties, as selected by the + implementation. +
  6. +
  7. If form is failure, reject + promise with a + SyntaxError + and stop. +
  8. +
  9. Let result be an object + and for each string name in + propertyNames add + a property with key name + and the value null.
  10. +
  11. Make a request to the underlying platform (via the + Protocol Bindings) + to retrieve the Property values given by + propertyNames with + form and optional URI templates given in + options' + uriVariables. +
  12. +
  13. If this cannot be done with a single request with the + Protocol Bindings, + reject + promise with a + + NotSupportedError and stop. +
  14. +
  15. Process the response and for each key in + result, run the following + sub-steps: +
      +
    1. Let value be result[key].
    2. +
    3. Let schema be + this.[[td]].properties[key]. +
    4. +
    5. Let property be the result of running + parse + interaction response on value, + form and schema. +
    6. +
    +
  16. +
  17. If the above step throws at any point, + reject + promise with that + exception and stop. +
  18. +
  19. + Resolve + promise with + result. +
  20. +
+
+
+
+
+

8.6 The readAllProperties() + method

+
+
+ Reads all properties of the Thing with one request. Takes + options as + optional argument. It returns a + Promise that resolves with a + PropertyReadMap + object that maps keys from Property names to values + returned by this algorithm. The method MUST run the following steps: +
    +
  1. Return a + Promise promise and execute the next steps + + in parallel. +
  2. +
  3. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  4. +
  5. Let forms be + subscription.[[interaction]].forms. +
  6. +
  7. If forms is undefined, + reject + promise with a + SyntaxError + and stop. +
  8. +
  9. If option.formIndex is not + undefined and is less than + forms.length, set + subscription.[[form]] to + forms.[formIndex]. +
  10. +
  11. Otherwise, set + subscription.[[form]] to a + Form + in forms whose op is + "readallproperties", as selected by the + implementation. +
  12. +
  13. If subscription.[[form]] is + failure, reject + promise with a + SyntaxError + and stop. +
  14. +
  15. Make a request to the underlying platform using the + Protocol Bindings + to retrieve all the Property definitions from + the TD + given form and optional URI templates in + options.uriVariables. +
  16. +
  17. If this cannot be done with a single request with the + Protocol Bindings + of the Thing, then + reject + promise with a + + NotSupportedError and stop. +
  18. +
  19. If the request fails, reject + promise with the error + received from the Protocol + Bindings and stop. +
  20. +
  21. Process the reply and let result be an object with the keys and + values obtained in the reply.
  22. +
  23. Process the response and for each key in + result, run the following + sub-steps: +
      +
    1. Let value be result[key].
    2. +
    3. Let schema be + this.[[td]].properties[key]. +
    4. +
    5. Let property be the result of running + parse + interaction response on value, + form and schema. +
    6. +
    +
  24. +
  25. + Resolve + promise with + result. +
  26. +
+
+
+
+
+

8.7 The writeProperty() + method

+
+
+ Writes a single Property. Takes as + arguments propertyName, + value and + optionally options. It returns a + + Promise that resolves on success + and rejects on failure. The method MUST run the following steps: +
    +
  1. Return a + Promise promise and execute the next steps + + in parallel. +
  2. +
  3. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  4. +
  5. Let interaction be + this.[[td]].properties[propertyName]. +
  6. +
  7. If interaction is undefined, + reject + promise with a + NotFoundError + and stop. +
  8. +
  9. If option.formIndex is not + undefined, let form be the + Form + associated with formIndex in the + interaction.forms array, + otherwise let form be a Form in + interaction.forms whose + op is writeproperty, as + selected by the implementation. +
  10. +
  11. If form is failure, reject + promise with a + SyntaxError + and stop. +
  12. +
  13. Let data be the result of running the + create + interaction request steps given value, form and + interaction. If that throws, reject + + promise with that exception and stop. +
  14. +
  15. Make a request to the underlying platform (via the + Protocol Bindings) + to write the Property given by + propertyName using + data and the + optional URI templates given in options' + uriVariables. +
  16. +
  17. If the request fails, reject + promise with the error + received from the Protocol + Bindings and stop. +
  18. +
  19. Otherwise resolve + promise. +
  20. +
+
+
+
+ Editor's note +
+

As discussed in Issue + #193, the design decision is that write interactions + only return success or error, not the written value + (optionally). TDs should + capture the schema of the Property values, including + precision and alternative formats. When a return value is + expected from the interaction, an Action should be used instead + of a Property.

+
+
+
+
+

+ 8.8 The writeMultipleProperties() + method

+
+
+ Writes a multiple Property values with one + request. Takes as arguments properties - as an object with keys being + Property names and values + as Property values - and + optionally options. It returns a + + Promise that resolves on success + and rejects on failure. The method MUST run the following steps: +
    +
  1. Return a + Promise promise and execute the next steps + + in parallel. +
  2. +
  3. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  4. +
  5. If option.formIndex is defined, + let form be the Form associated with + formIndex in the [[td]].forms + array, otherwise let form be a Form in + [[td]].forms + array whose op is + writemultipleproperties, as selected by + the implementation. +
  6. +
  7. If form is failure, reject + promise with a + SyntaxError + and stop. +
  8. +
  9. Let propertyNames be an array of + string with as elements the keys of the + properties object.
  10. +
  11. For each name in + propertyNames, let property be + this.[[td]].properties[name]. +
  12. +
  13. If property is null or + undefined or is not writeable + reject + promise with + + NotSupportedError and stop. +
  14. +
  15. Let result be an object + and for each string name in + propertyNames add a property with key + name and let its value be + null.
  16. +
  17. Let schemas be an + object and for each name in + propertyNames add a property with key + name and let its value be + this.[[td]].properties[name]. +
  18. +
  19. For each key key in + properties, run the + create + interaction request steps given properties[key], form and the value for + schema[key]. + If that throws for any name, reject + + promise with that exception and stop. +
  20. +
  21. Make a single request to the underlying platform (via + the Protocol + Bindings) to write each Property provided in + properties with optional + URI templates given in options' + uriVariables. +
  22. +
  23. If this cannot be done with a single request with the + Protocol Bindings + of the Thing, then + reject + promise with a + + NotSupportedError and stop. +
  24. +
  25. If the request fails, return the error received from + the Protocol + Bindings and stop. +
  26. +
  27. Otherwise resolve + promise. +
  28. +
+
+
+
+
+

8.9 The observeProperty() + method

+
+
+ Makes a request for Property value change + notifications. Takes as arguments propertyName, listener and optionally + onerror and + options. It + returns a + Promise that resolves on success + and rejects on failure. +
+
+ Editor's note +
+

This algorithm allows for only one active + Subscription + per Property. If a new + Subscription + is made while an existing Subscription + is active the runtime will throw an NotAllowedError.

+
The method MUST run the + following steps: +
    +
  1. Let thing be the reference of this + ConsumedThing + object. +
  2. +
  3. Return a + Promise promise and execute the next steps + + in parallel. +
  4. +
  5. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  6. +
  7. If listener is not a + Function, + reject + promise with a + + TypeError and stop. +
  8. +
  9. If onerror is + not null and is not a Function, + reject + promise with a + + TypeError and stop. +
  10. +
  11. If thing.[[activeObservations]][propertyName] + [=map/exists], reject + promise with a + + NotAllowedError and stop. +
  12. +
  13. Let subscription be a new + Subscription + object with its + internal slots set as follows: + +
  14. +
  15. Make a request to the underlying platform to observe + the Property identified by + propertyName with + form and optional URI templates given in + options' + uriVariables. +
  16. +
  17. If the request fails, reject + promise with the error + received from the Protocol + Bindings and stop. +
  18. +
  19. + Set + thing.[[activeObservations]][|propertyName] + to subscription and resolve + promise. +
  20. +
  21. Whenever the underlying platform detects a + notification for this subscription + keyed + on propertyName with a + new Property value + value, run the following sub-steps: + +
  22. +
  23. Whenever the underlying platform detects an error for + this subscription, run the following sub-steps: +
      +
    • If the error is irrecoverable and stops the + subscription, set + subscription.active to + false and suppress further + notifications.
    • +
    • Let error be a new + + NetworkError and set its + message to reflect the underlying error + condition. +
    • +
    • If onerror + is a Function, + invoke it given error. +
    • +
    +
  24. +
+
+
+
+
+

8.10 The invokeAction() + method

+
+
+ Makes a request for invoking an Action and return the result. + Takes as arguments actionName, optionally params and optionally + options. It + returns a + Promise that resolves with the + result of the Action + represented as an InteractionOutput + object, or rejects with an error. The method MUST run the following steps: +
    +
  1. Return a + Promise promise and execute the next steps + + in parallel. +
  2. +
  3. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  4. +
  5. Let interaction be + this.[[td]].actions[actionName]. +
  6. +
  7. If interaction is not an + object, + reject + promise with a + NotFoundError + and stop. +
  8. +
  9. Let forms be + subscription.[[interaction]].forms. +
  10. +
  11. If forms is undefined, + reject + promise with a + SyntaxError + and stop. +
  12. +
  13. If option.formIndex is not + undefined and is less than + forms.length, set + subscription.[[form]] to + forms.[formIndex]. +
  14. +
  15. Otherwise, set + subscription.[[form]] to a + Form + in forms whose op is + "invokeaction", as selected by the + implementation. +
  16. +
  17. If subscription.[[form]] is + failure, reject + promise with a + SyntaxError + and stop. +
  18. +
  19. Let args be the result of running the + create + interaction request steps on params, form and + interaction. If that throws, + reject + + promise with that exception and stop. +
  20. +
  21. Make a request to the underlying platform (via the + Protocol Bindings) + to invoke the Action identified by + actionName given + args and options.uriVariables. +
  22. +
  23. If the request fails locally or returns an error over + the network, reject + promise with the error + received from the Protocol + Bindings and stop. +
  24. +
  25. Let value be the reply returned in the + reply.
  26. +
  27. Let result be the result of running + parse + interaction response with value, + form and interaction. If that + throws, reject + promise with that + exception and stop. +
  28. +
  29. + Resolve + promise with + result. +
  30. +
+
+
+
+
+

8.11 The subscribeEvent() + method

+
+
+ Makes a request for subscribing to Event notifications. Takes as + arguments eventName, + listener and optionally + onerror and + options. It + returns a + Promise to signal success or + failure. +
+
+ Editor's note +
+

This algorithm allows for only one active + Subscription + per Event. If a new + Subscription + is made while an existing Subscription + is active the runtime will throw an NotAllowedError.

+
The method MUST run the + following steps: +
    +
  1. Let thing be the reference of this + ConsumedThing + object. +
  2. +
  3. Return a + Promise promise and execute the next steps + + in parallel. +
  4. +
  5. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  6. +
  7. If listener is not + a Function, + reject + promise with a + + TypeError and stop. +
  8. +
  9. If onerror is + not null and is not a Function, + reject + promise with a + + TypeError and stop. +
  10. +
  11. If thing.[[activeSubscriptions]][eventName] + does not exist, + reject + promise with a + + NotAllowedError and stop. +
  12. +
  13. Let subscription be a new + Subscription + object with its + internal slots set as follows: + +
  14. +
  15. Make a request to the underlying platform via the + Protocol Bindings + to subscribe to the Event identified by + eventName with + + [[form]], optional URI templates given + in options.uriVariables + and optional subscription data given in options.data. +
  16. +
  17. If the request fails, reject + promise with the error + received from the Protocol + Bindings and stop. +
  18. +
  19. + Set + eventName to + thing.[[activeSubscriptions]][eventName] + to subscription. +
  20. +
  21. + Resolve + promise. +
  22. +
  23. Whenever the underlying platform detects a + notification for the Event subscription + keyed + on eventName, run the + following sub-steps: +
      +
    1. Invoke listener given the result of + running parse + interaction response on the data provided with + the Event, + subscription.[[form]] and + subscription.[[interaction]]. +
    2. +
    +
  24. +
  25. Whenever the underlying platform detects an error for + Event + subscription keyed on + eventName, run the + following sub-steps: +
      +
    • If the error is irrecoverable and stops the + subscription, set + subscription.active to + false and suppress further + notifications.
    • +
    • Let error be a new + + NetworkError and set its + message to reflect the underlying error + condition. +
    • +
    • If onerror + is a Function, + invoke it given error. +
    • +
    +
  26. +
+
+
+
+
+

+ 8.12 The InteractionOptions + dictionary

+
+

Holds the interaction options that need to be exposed for + application scripts according to the Thing + Description.

+

The formIndex property, if defined, + represents an application hint for which Form + definition, identified by this index, of the TD to use for the given WoT + interaction. Implementations SHOULD + use the Form with this index for making the + interaction, but MAY override this + value if the index is not found or not valid. If not defined, + implementations SHOULD attempt to + use the Form definitions in order of appearance + as listed in the TD for the + given Wot Interaction.

+

The uriVariables property if defined, + represents the URI template variables to be used with the WoT + Interaction that are represented as + parsed JSON objects defined in [WOT-TD].

+
+
+ Editor's note +
+

The support for URI variables comes from the + need, exposed by the + Web of Things (WoT) Thing Description 1.1 + specification, to be able to describe existing RESTful + endpoints that use them. However, it should be possible to + write a Thing Description that would use Actions for representing this + kind of interactions and model the URI variables as action + parameters. In that case, implementations can serialize the + parameters as URI variables, and therefore, the + options parameter could be dismissed.

+
+

The data + property if defined, represents additional opaque data that + needs to be passed to the interaction.

+
+
+
+

8.13 The PropertyReadMap + type

+
+

Represents a map of Property names to an InteractionOutput + object that represents the value the Property can take. It is + used as a property bag for interactions that involve multiple + Properties at + once.

+
+
+
+

8.14 The PropertyWriteMap + type

+
+

Represents a map of Property names to an InteractionInput + that represents the value the Property can take. It is used + as a property bag for interactions that involve multiple + Properties at + once.

+
+
+
+

8.15 The InteractionListener + callback

+
+

User provided callback that is given an argument of type + InteractionOutput + and is used for observing Property changes and handling + Event notifications. + Since subscribing to Events are WoT interactions and + might take options or even data, they are not modelled with + software events.

+
+
+
+

8.16 The ErrorListener + callback

+
+

User provided callback that is given an argument of type + Error + and is used for conveying critical and non-critical errors + from the Protocol Bindings to + applications.

+
+
+
+

8.17 The Subscription + interface

+
+

Represents a subscription to Property change and Event interactions.

+

The active + boolean property denotes if the subscription is active, i.e. + it is not stopped because of an error or because of + invocation of the stop() method.

+
+
+

+ 8.17.1 Internal slots for + Subscription

+
A Subscription + object has the following + internal slots: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Internal SlotInitial valueDescription (non-normative)
[[type]]null + Indicates what WoT + Interaction the Subscription + refers to. The value can be either + "property" or "event" or + null. +
[[name]]null + The Property or + Event name. +
[[interaction]]null + The Thing + Description fragment that describes the + WoT + interaction. +
[[form]]null + The Form associated with the + subscription. +
[[thing]]null + The ConsumedThing + associated with the subscription. +
+
+
+
+

8.17.2 The stop() + method

+
+

Stops delivering notifications for the subscription. It + takes an optional parameter options and returns a + + Promise. When invoked, the method + MUST execute the following + steps:

+
    +
  1. Return a + Promise promise and execute the next steps + + in parallel. +
  2. +
  3. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  4. +
  5. If options' + formIndex is defined, let + unsubscribeForm be the Form associated with + formIndex in [[interaction]]'s + forms array. +
  6. +
  7. Otherwise let unsubscribeForm be the + result of running the find a + matching unsubscribe form algorithm given + + [[form]]. +
  8. +
  9. If unsubscribeForm is failure, + reject + promise with a + SyntaxError + and stop. +
  10. +
  11. If [[type]] is + "property", make a request to the underlying + platform via the Protocol + Bindings to stop observing the Property + identified by [[name]] with + unsubscribeForm and optional URI templates + given in options' + uriVariables. +
  12. +
  13. Otherwise, if [[type]] is + "event", make a request to the underlying + platform via the Protocol + Bindings to unsubscribe from the Event identified by + [[name]] with + unsubscribeForm, with optional URI templates + given in options' + uriVariables and optional unsubscribe data + given in options.data. +
  14. +
  15. If the request fails, reject + promise with the error + received from the Protocol + Bindings and stop. +
  16. +
  17. Otherwise: + +
  18. +
  19. If the underlying platform receives further + notifications for this subscription, implementations + SHOULD silently suppress + them.
  20. +
+
+
+
+

8.17.3 Finding an unsubscribe Form

+
+
+
+ Note +
+

This algorithm is under development and is + non-normative. Implementations MAY choose another algorithm to find a + matching unsubscribe Form to a given + subscribe Form.

+
To find a matching unsubscribe form given + subscribeForm in the context of a + Subscription + object, run the following steps: +
    +
  1. Let results be an empty array.
  2. +
  3. For each form in [[interaction]].forms, +
      +
    1. Add an + internal slot [[matchLevel]] on form + and set its value to 0. +
    2. +
    3. If form.op is + "unobserveproperty" if + [[type]] is + "property" or if + form.op is + "unsubscribeevent" if + [[type]] + is"event", +
        +
      1. Set the + internal slot [[matchLevel]] on + form to 1 and add form to + results. +
      2. +
      3. If form.href and + [[subscribeForm]].href are + same origin-domain, increment + form.[[matchLevel]]. +
      4. +
      5. If form.contentType is + equal to [[subscribeForm]]'s + contentType and + form.[[matchLevel]] is greater than 2, + increment form.[[matchLevel]].
      6. +
      +
    4. +
    +
  4. +
  5. If results is empty, return + null and terminate these steps.
  6. +
  7. Return the first form in + results that has the highest [[matchLevel]] + value.
  8. +
+
+
+
+
+

8.18 ConsumedThing Examples

+
+

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.

+
+
+ Example + 2: Thing + Client API example with data value +
+
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", async (data) => {
+    try {
+      console.log("Temperature changed to: " + await data.value());
+    } catch (error) {
+      console.error("Cannot read the observed property temperature");
+      console.error(error);
+    }
+  });
+  // subscribe to the “ready” event defined in the TD
+  await thing.subscribeEvent("ready", async (eventData) => {
+    try {
+      console.log("Ready; index: " + await eventData.value());
+      // run the “startMeasurement” action defined by TD
+      await thing.invokeAction("startMeasurement", { units: "Celsius" });
+      console.log("Measurement started.");
+    } catch (error) {
+      console.error("Cannot read the ready event or startMeasurement failed");
+      console.error(error)
+    }
+  });
+} catch (e) {
+  console.log("Error starting measurement.");
+}
+
+setTimeout(async () => {
+  try {
+    const temperatureData = await thing.readProperty("temperature")
+    const temperature = await temperatureData.value();
+    console.log("Temperature: " + temperature);
+
+    await thing.unsubscribe("ready");
+    console.log("Unsubscribed from the ‘ready’ event.");
+  } catch (error) {
+    console.log("Error in the cleanup function");
+  }
+}, 10000);
+
+

The following shows an advance usage of InteractionOutput + to read a property without a DataSchema.

+
+
+ Example + 3: Thing + Client API example with arrayBuffer +
+
/*
+* takePicture affordance form:
+* "form": {
+*   "op": "invokeaction",
+*   "href" : "http://camera.example.com:5683/takePicture",
+*   "response": {
+*     "contentType": "image/jpeg",
+*     "contentCoding": "gzip"
+*   }
+*}
+* See https://www.w3.org/TR/wot-thing-description/#example-23
+*/
+let response;
+let image;
+try {
+  response = await thing.invokeAction(“takePicture”));
+  image = await response.value() // throws NotReadableError --> schema not defined
+} catch(ex) {
+  image = await response.arrayBuffer();
+  // image: ArrayBuffer [0x1 0x2 0x3 0x5 0x15 0x23 ...]
+}
+
+

Finally, the next two examples shows the usage of a + ReadableStream + from an InteractionOutput.

+
+
+ + Example 4: + Thing Client API example with readable stream (e.g., + video stream) +
+
/*{
+"video": {
+  "description" : "the video stream of this camera",
+  "forms": [
+    {
+      "op": "readproperty",
+      "href": "http://camera.example.com/live",
+      "subprotocol": "hls"
+      "contentType": "video/mp4"
+    }
+  ]
+}}*/
+
+const video = await thing.readProperty("video")
+const reader = video.data.getReader()
+reader.read().then(function processVideo({ done, value }) {
+  if (done) {
+    console.log("live video stoped");
+    return;
+  }
+  const decoded = decode(value)
+  UI.show(decoded)
+  // Read some more, and call this function again
+  return reader.read().then(processText);
+});
+
+

Here consider that the JSON object is too big to be read + wholly in the memory. Therefore, we use streaming processing + to get the total number of the events recorded by the remote + Web Thing.

+
+
+ + Example 5: + Thing Client API example with readable stream (e.g., + counting json objects) +
+
/*
+* "eventHistory":
+* {
+*   "description" : "A long list of the events recorded by this thing",
+*   "type": "array",
+*   "forms": [
+*     {
+*       "op": "readproperty",
+*       "href": "http://recorder.example.com/eventHistory",
+*     }
+*   ]
+* }
+*/
+
+// Example of streaming processing: counting json objects
+let objectCounter = 0
+const parser = new Parser() //User library for json streaming parsing (i.e. https://github.com/uhop/stream-json/wiki/Parser)
+
+parser.on('data', data => data.name === 'startObject' && ++objectCounter);
+parser.on('end', () => console.log(`Found ${objectCounter} objects.`));
+
+const response = await thing.readProperty(“eventHistory”)
+await response.data.pipeTo(parser);
+
+// Found N objects
+
+
+
+
+
+

9. The ExposedThing + interface

+
+

The ExposedThing + interface is the server API to operate the Thing that allows defining request + handlers, Property, Action, and Event interactions.

+
WebIDL[SecureContext, Exposed=(Window,Worker)]
+interface ExposedThing {
+  ExposedThing setPropertyReadHandler(DOMString name,
+          PropertyReadHandler handler);
+  ExposedThing setPropertyWriteHandler(DOMString name,
+          PropertyWriteHandler handler);
+  ExposedThing setPropertyObserveHandler(DOMString name,
+          PropertyReadHandler handler);
+  ExposedThing setPropertyUnobserveHandler(DOMString name,
+          PropertyReadHandler handler);
+  Promise<undefined> emitPropertyChange(DOMString name,
+          optional InteractionInput data);
+
+  ExposedThing setActionHandler(DOMString name, ActionHandler action);
+
+  ExposedThing setEventSubscribeHandler(DOMString name,
+          EventSubscriptionHandler handler);
+  ExposedThing setEventUnsubscribeHandler(DOMString name,
+          EventSubscriptionHandler handler);
+  Promise<undefined> emitEvent(DOMString name,
+          optional InteractionInput data);
+
+  Promise<undefined> expose();
+  Promise<undefined> destroy();
+
+  ThingDescription getThingDescription();
+};
+
+callback PropertyReadHandler = Promise<InteractionInput>(
+        optional InteractionOptions options = {});
+
+callback PropertyWriteHandler = Promise<undefined>(
+        InteractionOutput value,
+        optional InteractionOptions options = {});
+
+callback ActionHandler = Promise<InteractionInput>(
+        InteractionOutput params,
+        optional InteractionOptions options = {});
+
+callback EventSubscriptionHandler = Promise<undefined>(
+        optional InteractionOptions options = {});
+
+
+

9.1 Internal slots for + ExposedThing

+
+

An ExposedThing + object has the following + internal slots:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Internal SlotInitial valueDescription (non-normative)
[[td]]null + The Thing + Description of the ExposedThing. +
[[readHandlers]]{} + A + Map with property names as keys and + PropertyReadHandlers + as values +
[[writeHandlers]]{} + A + Map with property names as keys and + PropertyWriteHandlers + as values +
[[observeHandlers]]{} + A + Map with property names as keys and + PropertyReadHandlers + as values +
[[unobserveHandlers]]{} + A + Map with property names as keys and + Functions + as values +
[[actionHandlers]]{} + A + Map with action names as keys and + ActionHandlers + as values +
[[subscribeHandlers]]{} + A + Map with event names as keys and + EventSubscriptionHandlers + as values +
[[unsubscribeHandlers]]{} + A + Map with event names as keys and + EventSubscriptionHandlers + as values +
[[propertyObservers]]{} + A + Map with property names as keys and + an + Array of listeners as values +
[[eventListeners]]{} + A + Map with event names as keys and + + Array of listeners as values +
+
+
+
+

9.2 Constructing ExposedThing

+
+

The ExposedThing + interface extends ConsumedThing. + It is constructed from a full or partial ThingDescription + object.

+
+
+ Note +
+

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.

+
+
+
+ Note +
+

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 ExposedThingInit + init, run the + following steps: +
    +
  1. If invoking this method is not allowed for the + current scripting context for security reasons, + throw + a SecurityError + and stop. +
  2. +
  3. Run the expand an + ExposedThingInit steps on init. if that fails + re-throw the + error and stop. Otherwise store the obtained + td +
  4. +
  5. Run the expand a TD steps on + td. If that + fails, re-throw + the error and stop. +
  6. +
  7. Let thing be a + new ExposedThing + object. +
  8. +
  9. Set the [[td]] of + thing to + td. +
  10. +
  11. Return thing.
  12. +
+
+
+
+
+

9.3 The getThingDescription() + method

+
+

Returns the [[td]] of the + ExposedThing + object that represents the Thing Description of + the Thing. Applications may + consult the Thing + metadata stored in [[td]] in order to + introspect its capabilities before interacting with it.

+
+
+
+

9.4 The PropertyReadHandler + callback

+
+

A 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 with an + ReadableStream + object or an + ECMAScript value conforming to DataSchema, or rejects with + an error.

+
+
+
+

9.5 The setPropertyReadHandler() + method

+
+

Takes as arguments name and + handler. Sets the + service handler that defines what to do when a request is + received for reading the specified Property matched by + name. Throws on error. Returns + a reference to this object for supporting + chaining.

+
+
+ Editor's note +
+

Note that there is no need to register handlers + for handling requests for reading multiple or all Properties. The request + and reply are transmitted in a single network request, but + the ExposedThing + may implement them using multiple calls to the single read + handler.

+
+

The handler + 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 + provided in the [[td]] + + internal slot.

+
+ When the method is invoked given name and handler, implementations + MUST run the following steps: +
    +
  1. If invoking this method is not allowed for the + current scripting context for security reasons, + throw + a SecurityError + and stop. +
  2. +
  3. If [[td]].properties[name] + does not exist, + throw + NotFoundError + and stop. +
  4. +
  5. Set this.[[readHandlers]][name] + to handler. +
  6. +
+
+
+
+
+

+ 9.6 Handling requests for reading + a Property

+
+
+ When a network request for reading Property name is received by the implementation with + options, run the + following steps: +
    +
  1. If this operation is not supported, send back a + + NotSupportedError according to the Protocol + Bindings and stop. +
  2. +
  3. If this operation is not allowed, send back a + + NotAllowedError according to the Protocol + Bindings and stop. +
  4. +
  5. Let value be the result of running the + read server + property steps with name and options: +
      +
    1. Let interaction be + [[td]].properties.name. +
    2. +
    3. If a Property with + name does not exist, + throw + NotFoundError and stop. +
    4. +
    5. Let handler be + null.
    6. +
    7. If there is a user provided PropertyReadHandler + in + [[readHandlers]] + internal slot for interaction, let + handler be that. +
    8. +
    9. Otherwise, if there is a default read handler + provided by the implementation, let handler be that.
    10. +
    11. If handler is + null, throw + NotSupportedError and stop. +
    12. +
    13. Let value be the result of invoking + handler given + options. If + that fails, throw the error and stop.
    14. +
    15. Return value. +
      +
      + Note +
      +

      The value returned here + SHOULD either conform to + DataSchema or it + SHOULD be an + + ReadableStream object created by + the handler.

      +
      +
    16. +
    +
  6. +
  7. If the previous step has thrown an error, send the + error back with the reply created by following the + Protocol Bindings + and stop. +
  8. +
  9. Serialize and add the returned value to + the reply created by following the Protocol Bindings. +
  10. +
+
+
+
+
+

+ 9.7 Handling requests for reading + multiple Properties

+
+
+ When a network request for reading multiple Properties given in an + object propertyNames is received with + options, run the + following read + multiple properties steps on propertyNames + and options: +
    +
  1. If this operation is not supported, send back a + + NotSupportedError according to the Protocol + Bindings and stop. +
  2. +
  3. If this operation is not allowed, send back a + + NotAllowedError according to the Protocol + Bindings and stop. +
  4. +
  5. For each property with key name defined in + propertyNames, +
      +
    1. Let value be the result of running the + read server + property steps on name and + options. If + that throws, send back the error in the reply created + by following the Protocol + Bindings and stop. +
    2. +
    3. Set propertyNames.name to + value.
    4. +
    +
  6. +
  7. Reply to the request by sending a single reply + created from propertyNames according to the + Protocol Bindings. +
  8. +
+
+
+
+
+

+ 9.8 Handling requests for reading + all Properties

+
+
+ When a network request for reading all Properties is received + with options, run + the following steps: +
    +
  1. If this operation is not supported, send back a + + NotSupportedError according to the Protocol + Bindings and stop. +
  2. +
  3. If this operation is not allowed, send back a + + NotAllowedError according to the Protocol + Bindings and stop. +
  4. +
  5. Let properties be an object created with + all properties defined in the Thing with values set to + null. +
  6. +
  7. Run the read multiple + properties steps on properties and + options. +
  8. +
+
+
+
+
+

+ 9.9 The setPropertyObserveHandler() + method

+
+

Takes as arguments name and + handler. Sets the + service handler that defines what to do when a request is + received for observing the specified Property matched by + name. Throws on error. Returns + a reference to this object for supporting + chaining.

+

The handler + callback function should implement reading a Property and + resolve with an + InteractionOutput + object or reject with + an error.

+

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 the method is invoked given name and handler, implementations + MUST run the following steps: +
    +
  1. If invoking this method is not allowed for the + current scripting context for security reasons, + throw + a SecurityError + and stop. +
  2. +
  3. If this.[[td]].properties[name] + does not exist, + throw + NotFoundError + and stop. +
  4. +
  5. Set this[[observeHandlers]][name] + to handler. +
  6. +
+
+
+
+
+

+ 9.10 Handling Property observe + requests

+
+
+ When a network request for observing a Property name is received by the implementation with + options, run the + following steps: +
    +
  1. If this operation is not supported, send back a + + NotSupportedError according to the Protocol + Bindings and stop. +
  2. +
  3. If this operation is not allowed, send back a + + NotAllowedError according to the Protocol + Bindings and stop. +
  4. +
  5. If this.[[td]].properties[name] + does not exist, + send back a NotFoundError + in the reply and stop. +
  6. +
  7. Internally save the request sender information + together with options and + this.[[propertyObservers]][name], + in order to be able to notify about Property value + changes. +
  8. +
+
+
+
+ Note +
+

Every time the value of property + changes, emitPropertyChange() + needs to be explicitly called by the application + script.

+
+
+
+
+

+ 9.11 The setPropertyUnobserveHandler() + method

+
+

Takes as arguments name and + handler. Sets the + service handler that defines what to do when a request is + received for unobserving the specified Property matched by + name. Throws on error. Returns + a reference to this object for supporting + chaining.

+

The handler + callback function should implement what to do when an + unobserve request is received by the implementation.

+

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 handler + based on the Thing + Description.

+
+ When the method is invoked given name and handler, implementations + MUST run the following steps: +
    +
  1. If invoking this method is not allowed for the + current scripting context for security reasons, + throw + a SecurityError + and stop. +
  2. +
  3. If this.[[td]].properties[name] + does not exist, + throw + NotFoundError + and stop. +
  4. +
  5. Set this.[[unobserveHandlers]][name] + to handler. +
  6. +
+
+
+
+
+

+ 9.12 Handling Property unobserve + requests

+
+
+ When a network request for unobserving a Property name with options is received by the + implementation, run the following steps: +
    +
  1. If this operation is not supported, send back a + + NotSupportedError according to the Protocol + Bindings and stop. +
  2. +
  3. If this operation is not allowed, send back a + + NotAllowedError according to the Protocol + Bindings and stop. +
  4. +
  5. If this.[[td]].properties[name] + does not exist, + send back a NotFoundError + in the reply and stop. +
  6. +
  7. Let handler be + this.[[unobserveHandlers]][name]; +
  8. +
  9. If handler is a Function, + invoke that given options, then send back a + reply following the Protocol Bindings + and stop. +
  10. +
  11. Otherwise, if this.[[propertyObservers]][name] + exists, + remove it from this.[[propertyObservers]], + send back a reply as defined in the Protocol + Bindings and stop. +
  12. +
  13. Otherwise, send back a NotFoundError + in the reply as defined in the Protocol Bindings + and stop. +
  14. +
+
+
+
+
+

9.13 The emitPropertyChange() + method

+
+
+ Takes the name argument, + denoting a Property name, and + optionally the data + argument. Triggers emitting a notification to all observers + of the specified Property with the data + provided by the data argument, or if not provided, + it is obtained by the observe handler or the by read + handler associated with that Property. The method + MUST run the following steps: +
    +
  1. Let promise be a new + + Promise. +
  2. +
  3. Return promise and + execute the next steps + in parallel. +
  4. +
  5. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  6. +
  7. Let name be the first + argument.
  8. +
  9. Let property be + this.[[td]].properties[name]. +
  10. +
  11. If property is undefined, + reject + promise with + NotFoundError + and stop. +
  12. +
  13. Let data be + the second argument.
  14. +
  15. If data is + undefined, run the following sub-steps: +
      +
    1. Let handler be + null.
    2. +
    3. If name does not + exist + in + [[readHandlers]], reject + promise and stop. +
    4. +
    5. Let handler be + + [[readHandlers]][name]. +
    6. +
    7. If handler is + null or undefined, + reject + promise and stop. +
    8. +
    9. Let handled be the result of invoking + handler given + null.
    10. +
    11. If handled is rejected, + then + reject promise + and stop. +
    12. +
    13. Otherwise if handled + resolved + with value, let data be value. +
    14. +
    +
  16. +
  17. For each observer in [[propertyObservers]][name], + run the following sub-steps: +
      +
    1. Let options be the interaction options + saved with observer.
    2. +
    3. Request the underlying platform to create a + reply from data and + options according to the Protocol + Bindings. +
      +
      + Editor's note +
      +

      This clause needs expanding and/or + refer to an algorithm in [WOT-PROTOCOL-BINDINGS].

      +
      +
    4. +
    5. Send reply to + observer.
    6. +
    +
  18. +
  19. + Resolve + promise. +
  20. +
+
+
+
+
+

+ 9.14 The PropertyWriteHandler + callback

+
+

A function that is called when an external request for + writing a Property is received and + defines what to do with such requests. Takes as argument + value and returns a + + Promise, resolved when the value of + the Property - identified by the + name provided when setting the handler has been updated -, or + rejects with an error if the property is not found or the + value cannot be updated.

+
+
+ Editor's note +
+

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.

+
+
+
+ Note +
+

The value is provided by implementations as an + InteractionOutput + object in order to be able to represent values that are not + described by a DataSchema, such as + streams.

+
+
+
+
+

+ 9.15 The setPropertyWriteHandler() + method

+
+

Takes as arguments name and + handler. Sets the + service handler that defines what to do when a request is + received for writing the Property matched by + name given when setting the + handler. Throws on error. Returns a reference to + this object for supporting chaining.

+
+
+ Note +
+

Note that even for readonly Properties it is + possible to specify a write handler, as explained in + Issue + 199. In this case, the write handler may define in an + application-specific way to fail the request.

+
+

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 if the Property is writeable and + notifying observers on change if the Property is observable, + based on the Thing + Description.

+
+ When the method is invoked given name and handler, implementations + MUST run the following steps: +
    +
  1. If invoking this method is not allowed for the + current scripting context for security reasons, + throw + a SecurityError + and stop. +
  2. +
  3. If this.[[td]].properties[name] + does not exist, + throw + NotFoundError + and stop. +
  4. +
  5. Set this.[[writeHandlers]][name] + to handler. +
  6. +
+
+
+
+
+

+ 9.16 Handling requests for writing + a Property

+
+
+ When a network request for writing a Property name with a new value value and options is received, + implementations MUST run the + following update property + steps, given name, + value, + options and + mode set to "single": +
    +
  1. If this operation is not supported, send back a + + NotSupportedError according to the Protocol + Bindings and stop. +
  2. +
  3. If this operation is not allowed, send back a + + NotAllowedError according to the Protocol + Bindings and stop. +
  4. +
  5. Let interaction be + this.[[td]].properties[name]. +
  6. +
  7. If interaction is undefined, + return a NotFoundError + in the reply and stop. +
  8. +
  9. Let handler be + this.[[writeHandlers]][name]. +
  10. +
  11. If handler is + undefined and if there is a default write + handler provided by the implementation, let + handler be that.
  12. +
  13. If handler is + undefined, send back a + NotSupportedError with the reply and stop. +
  14. +
  15. Let promise be the result of invoking + handler given + name and options. If it fails, return + the error in the reply and stop.
  16. +
  17. If mode is "single", reply to + the request reporting success, following the Protocol Bindings + and stop. +
  18. +
+
+
+
+
+

+ 9.17 Handling requests for writing + multiple Properties

+
+
+ When a network request for writing multiple Properties given in an + object propertyNames is received with + options, run the + following steps: +
    +
  1. If this operation is not supported, send back a + + NotSupportedError according to the Protocol + Bindings and stop. +
  2. +
  3. If this operation is not allowed, send back a + + NotAllowedError according to the Protocol + Bindings and stop. +
  4. +
  5. For each property with key name and value + value defined in propertyNames, run + the update property + steps with name, value, + options and + mode set to "multiple". If that + fails, reply to the request with that error and stop. +
  6. +
  7. Reply to the request by sending a single reply + according to the Protocol + Bindings. +
  8. +
+
+
+
+
+

9.18 The ActionHandler + callback

+
+

A 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 given params + and optionally with an options object. It returns a + + Promise that rejects with an error or + resolves with the value returned by the Action as InteractionInput.

+
+
+ Note +
+

Application scripts MAY return a ReadableStream + object from an ActionHandler. + Implementations will then use the stream for constructing + the Action's response.

+
+
+
+
+

9.19 The setActionHandler() + method

+
+

Takes as arguments name and + action. Sets the handler + function that defines what to do when a request is received + to invoke the 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 the method is invoked given name and action, run the following steps: +
    +
  1. If invoking this method is not allowed for the + current scripting context for security reasons, + throw + a SecurityError + and stop. +
  2. +
  3. Let interaction be + this.[[td]].actions[name]. +
  4. +
  5. If interaction is undefined, + throw + a NotFoundError + and stop. +
  6. +
  7. Set this.[[actionHandlers]][name] + to action. +
  8. +
+
+
+
+
+

9.20 Handling Action requests

+
+
+ When a network request for invoking the Action identified by + name is received given + inputs and optionally options, run the following + steps: +
    +
  1. If this operation is not supported, send back a + + NotSupportedError according to the Protocol + Bindings and stop. +
  2. +
  3. If this operation is not allowed, send back a + + NotAllowedError according to the Protocol + Bindings and stop. +
  4. +
  5. Let interaction be + this.[[td]].properties[name]. +
  6. +
  7. If interaction is undefined, + return a NotFoundError + in the reply and stop. +
  8. +
  9. Let handler be + this.[[actionHandlers]][name]. +
  10. +
  11. If handler is + undefined, return a + NotSupportedError with the reply created by + following the Protocol + Bindings and stop. +
  12. +
  13. Let promise be the result of invoking + handler given + name, inputs and + options.
  14. +
  15. If promise rejects, send the error with + the reply and stop.
  16. +
  17. When promise resolves with + data, use + data to create + and send the reply according to the Protocol + Bindings. +
  18. +
+
+
+
+
+

+ 9.21 The EventSubscriptionHandler + callback

+
+

A function that is called when an external request for + subscribing to an Event is + received and defines what to do with such requests. It is + invoked given an options object provided by the + implementation and coming from subscribers. It returns a + + Promise that rejects with an error or + resolves when the subscription is accepted.

+
+
+
+

+ 9.22 The setEventSubscribeHandler() + method

+
+

Takes as arguments name and + handler. Sets + the handler function that defines what to do when a + subscription request is received for the specified Event matched by name. Throws on error. Returns a reference to + this object for supporting chaining.

+

The handler callback function + SHOULD implement what to do when an + subscribe request is received, for instance necessary + initializations. Note that the handler for emitting Events is set separately.

+

There MUST be at most one event + subscribe handler for any given Event, so newly added handlers + MUST replace the previous + handlers.

+
+ When the method is invoked given name and handler, run the following + steps: +
    +
  1. If invoking this method is not allowed for the + current scripting context for security reasons, + throw + a SecurityError + and stop. +
  2. +
  3. Let interaction be + this.[[td]].events[name]. +
  4. +
  5. If interaction is undefined, + throw + a NotFoundError + and stop. +
  6. +
  7. Set this.[[subscribeHandlers]][name] + to handler. +
  8. +
  9. Return this.
  10. +
+
+
+
+
+

+ 9.23 Handling Event subscribe + requests

+
+
+ When an Event + subscription request for name is received by the + underlying platform with optional options, run the following + steps: +
    +
  1. If this operation is not supported, send back a + + NotSupportedError according to the Protocol + Bindings and stop. +
  2. +
  3. If this operation is not allowed, send back a + + NotAllowedError according to the Protocol + Bindings and stop. +
  4. +
  5. Let interaction be + this.[[td]].events[name]. +
  6. +
  7. If interaction is undefined, + send back a NotFoundError + and stop. +
  8. +
  9. If this.[[subscribeHandlers]][name] + is a Function, + invoke it given options and stop. +
  10. +
  11. Otherwise implement the default subscriber mechanism: +
      +
    1. Let subscriber be a tuple formed of + options + (from which uriVariables and + data may be used) and the subscriber + information needed to create an Event notification + response. +
    2. +
    3. Set this.[[eventListeners]][name] + to subscriber. +
    4. +
    +
  12. +
+
+
+
+
+

+ 9.24 The setEventUnsubscribeHandler() + method

+
+

Takes as arguments name and + handler. Sets + the handler function that defines what to do when the + specified Event + matched by name is unsubscribed + from. Throws on error. Returns a reference to this + object for supporting chaining.

+

The handler callback function + SHOULD implement what to do when an + unsubscribe request is received.

+

There MUST be at most one handler + for any given Event, so + newly added handlers MUST replace + the previous handlers.

+
+ When the method is invoked given name and handler, run the following + steps: +
    +
  1. If invoking this method is not allowed for the + current scripting context for security reasons, + throw + a SecurityError + and stop. +
  2. +
  3. Let interaction be + this.[[td]].events[name]. +
  4. +
  5. If interaction is undefined, + throw + a NotFoundError + and stop. +
  6. +
  7. Set this.[[unsubscribeHandlers]][name] + to handler. +
  8. +
  9. Return this.
  10. +
+
+
+
+
+

+ 9.25 Handling Event unsubscribe + requests

+
+
+ When an Event + unsubscribe request for name is received by the + underlying platform optionally with options, run the following + steps: +
    +
  1. If this operation is not supported, send back a + + NotSupportedError according to the Protocol + Bindings and stop. +
  2. +
  3. If this operation is not allowed, send back a + + NotAllowedError according to the Protocol + Bindings and stop. +
  4. +
  5. Let interaction be + this.[[td]].events[name]. +
  6. +
  7. If interaction is undefined, + send back a NotFoundError + and stop. +
  8. +
  9. If this.[[unsubscribeHandlers]][name] + exists + and is a Function, + invoke it given options and stop. +
  10. +
  11. Otherwise name [=map/exists] in + this.[[eventListeners]], + remove + name. +
  12. +
  13. Return this.
  14. +
+
+
+
+
+

9.26 Handling Events

+
+
+ When an Event + with name name is emitted with data by the emitEvent() method, run the + following steps: +
    +
  1. Let listeners be [[eventListeners]].name. +
  2. +
  3. For each subscriber in + listeners, run the following sub-steps: +
      +
    1. Create an Event notification + response according to the Protocol + Bindings from data and + subscriber, including its + options. +
    2. +
    3. If data + is undefined, assume that the + notification response will contain an + empty data payload as specified by Protocol + Bindings. +
    4. +
    5. If the underlying protocol stack permits + conveying event errors and if an error condition has + been detected by the UA, create response + as an error notification according to the Protocol + Bindings, using data, subscriber + and its options. +
      +
      + Note +
      +

      The error reporting is protocol + specific and it is encapsulated by + implementations. On the client end, the error + listener passed with the subscription will be + invoked if the client UA detects the error.

      +
      +
    6. +
    7. Send response to the subscriber + identified by subscriber.
    8. +
    +
  4. +
+
+
+
+
+

9.27 The emitEvent() + method

+
+
+ Takes as arguments name + denoting an Event + name and optionally data. Triggers emitting the + Event with the + optional data. The method MUST run + the following steps: +
    +
  1. Return a + Promise promise and execute the next steps + + in parallel. +
  2. +
  3. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  4. +
  5. Let interaction be [[td]].events.name. +
  6. +
  7. If an Event + with the name name is not + found, reject + promise with + NotFoundError + and stop. +
  8. +
  9. Make a request to the underlying platform to emit an + Event with optional + data. Call the + handling events steps. +
  10. +
+
+
+
+
+

9.28 The expose() method

+
+
+ Start 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: +
    +
  1. Return a + Promise promise and execute the next steps + + in parallel. +
  2. +
  3. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  4. +
  5. Run the expand a TD steps on the + + [[td]]. +
  6. +
  7. Run the validate a TD on + + [[td]]. If that fails, + reject + promise with a + + TypeError and stop. +
  8. +
  9. For each key in [[td]].properties + initialize this.[[propertyObservers]].key + to an empty + Array in order to store observe + request data needed to notify the observers on value + changes. +
  10. +
  11. For each key in + this.[[td]].events + initialize this.[[eventListeners]].key + to an empty + Array in order to store subscribe + request data needed to notify the subscribers on event + emission. +
  12. +
  13. Set up the WoT Interactions + based on introspecting [[td]] as + explained in [WOT-TD] + and [WOT-PROTOCOL-BINDINGS]. + Make a request to the underlying platform to initialize + the Protocol + Bindings and then start serving external requests + for WoT Interactions + (read, write and observe Properties, invoke + Actions and manage + Event subscriptions), based + on the Protocol + Bindings. Implementations MAY reject this step for any reason + (e.g. if they want to enforce further checks and + constraints on interaction forms). +
  14. +
  15. If there was an error during the request, + reject + promise with an + + Error object error with + error.message set to the error + code seen by the Protocol + Bindings and stop. +
  16. +
  17. Otherwise resolve + promise and stop. +
  18. +
+
+
+
+
+

9.29 The destroy() method

+
+
+ Stop 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: +
    +
  1. Return a + Promise promise and execute the next steps + + in parallel. +
  2. +
  3. If invoking this method is not allowed for the + current scripting context for security reasons, + reject + promise with a + SecurityError + and stop. +
  4. +
  5. Make a request to the underlying platform to stop + serving external requests for WoT Interactions, + based on the Protocol + Bindings. +
  6. +
  7. If there was an error during the request, + reject + promise with an + + Error object error with its + message set to the error code seen by the + Protocol + Bindings and stop. +
  8. +
  9. Otherwise resolve + promise and stop. +
  10. +
+
+
+
+
+

9.30 ExposedThing Examples

+
+

The next example illustrates how to create an + ExposedThing + based on a partial TD object + constructed beforehand.

+
+
+ Example + 6: Create + ExposedThing with a simple Property +
+
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 ExposedThing: + take its td property, add or modify it, then + create another ExposedThing + with that.

+
+
+ Example + 7: Add an + object Property +
+
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);
+}
+
+

The following will cover a set of examples for the + generation of a Thing Description + from an ExposedThingInit + using expand an + ExposedThingInit steps. As hypothesis the runtime + supports HTTP and COAP protocol bindings and it is hosted at + 192.168.0.1.

+

The next example shows how to exploit a ExposedThingInit + to create a simple Thing Description + with one Property with the default + values.

+ +
+
+ Editor's note +
+

TODO: add more examples where the ExposedThingInit + contains suggested values that are replaced by the + algorithm.

+
+
+
+
+
+

10. The ThingDiscoveryProcess + interface

+
+

Discovery 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 ThingDiscoveryProcess + object provides the properties and methods controlling the + discovery process and returning the results.

+
WebIDL[SecureContext, Exposed=(Window,Worker)]
+interface ThingDiscoveryProcess {
+  constructor(optional ThingFilter filter = {});
+  readonly attribute boolean done;
+  readonly attribute Error? error;
+  undefined stop();
+  async iterable<ThingDescription>;
+};
+

The ThingDiscoveryProcess + object has the following + internal slots.

+ + + + + + + + + + + + + + + + + + + + +
Internal SlotInitial valueDescription (non-normative)
[[filter]]undefined + The ThingFilter + object used in discovery. +
[[url]]undefined + A URL + representing the TD Directory in a + discovery. +
+

The done property is true + if the discovery has been stopped or completed with no more + results to report.

+

The error property represents the last + error that occurred during the discovery process. Typically + used for critical errors that stop discovery.

+

The ThingDiscoveryProcess + object implements the async + iterator concept.

+
+
+

+ 10.1 Constructing + ThingDiscoveryProcess

+
+
+ To create ThingDiscoveryProcess + with a filter, run the + following steps: +
    +
  1. If filter is not + an object or null, throw a + + TypeError and stop. +
  2. +
  3. Let discovery be a new + ThingDiscoveryProcess + object. +
  4. +
  5. Set discovery.[[filter]] to + filter. +
  6. +
  7. Set discovery.done + to false. +
  8. +
  9. Set discovery.error + to null. +
  10. +
  11. Return discovery.
  12. +
+
+
+
+
+

10.2 The ThingFilter + dictionary

+
+

Represents an object containing the constraints for + discovering Things as + key-value pairs.

+
WebIDLdictionary ThingFilter {
+  object? fragment;
+  
+};
+

The fragment property represents a + template object used for matching property by property + against discovered Things.

+
+
+ Editor's note +
+

The query + property was temporarily removed from ThingFilter, + until it is standardized in the WoT Discovery task force. + It represented a query string accepted by the + implementation, for instance a SPARQL or JSON query. + Support was to be implemented locally in the WoT Runtime or remotely + as a service in a TD Directory.

+
+
+
+ Editor's note +
+

The url + property was removed. It used to represent the target + entity serving the discovery request, for instance the URL + of a TD + Directory, or the URL of a directly targeted Thing, but these are + implemented by dedicated methods now.

+
+
+
+
+

10.3 The discovery process + algorithm

+
+
+ Describes what to do during a discovery process that has + already started. The algorithm, given discovery, runs the following + steps: +
    +
  1. Whenever a new link to a Thing Description + is discovered and provided by the underlying platform, + run the following sub-steps: +
      +
    1. Retrieve td as a JSON object, using + the Protocol + Binding used by the underlying discovery process + (as specified by link). In the case of an + HTTP(S) Binding, this process could use the + Fetch + API for the td's retrieval. +
    2. +
    3. If that fails, set the discovery.error + property to + SyntaxError, discard td and continue the + discovery process. +
    4. +
    +
  2. +
  3. Whenever a Thing Description + td is discovered + and provided by the underlying platform or by the + previous step, run the following sub-steps: +
    +
    + Note +
    +

    At this point implementations MAY control the flow of the discovery + process (depending on memory constraints, for + instance queue the results, or temporarily stop + discovery if the queue is getting too large, or + resume discovery when the queue is emptied + sufficiently). These steps are run for each + discovered/fetched td.

    +
    +
      +
    1. Let fragment be discovery.[[filter]].fragment. +
    2. +
    3. If fragment is an object, + then for each key defined in it: +
        +
      1. Check if that key + exists + in json and + json[key is equal to + fragment.key. +
      2. +
      3. If this is fails in any checks, discard + td and + continue the discovery process.
      4. +
      +
    4. +
    5. Yield td + using + asyncIterator. +
      +
      + Editor's note +
      +

      Improve this step using proper + asyncIterator terminology.

      +
      +
    6. +
    +
  4. +
  5. Whenever an error occurs during the discovery + process, run the following sub-steps: +
    +
    + Note +
    +

    The last error is retained. + Implementations MAY choose + to stop the discovery process if they consider it + should be reported.

    +
    +
      +
    1. Let error be a new + + Error object. Set + error.name to + "DiscoveryError". +
    2. +
    3. If there was an error code or message provided by + the Protocol + Bindings, set error.message + to that value as string. +
    4. +
    5. Set discovery.error + to error. +
    6. +
    7. If the error is irrecoverable and discovery has + been stopped by the underlying platform, or if the + implementation decided to stop the discovery process + and report the error, set discovery.done + to true and terminate these steps. +
    8. +
    +
  6. +
+
+
+
+
+

10.4 The stop() method

+
+
+ Stops 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 as done. The method MUST run the following steps: +
    +
  1. If invoking this method is not allowed for the + current scripting context for security reasons, + throw + a SecurityError + and stop. +
  2. +
  3. Request the underlying platform to stop the discovery + process. If this returns an error, or if it is not + possible, for instance when discovery is based on open + ended multicast requests, the implementation SHOULD discard subsequent discovered + items.
  4. +
  5. Set the done + property to true. +
  6. +
+
+
+
+
+

10.5 Discovery Examples

+
+

The following example finds ThingDescription + objects of Things + that are exposed by local hardware, regardless how many + instances of WoT + Runtime it is running Using the + asyncIterator provided by the Discovery + object, we can iterate asynchronously over the results and + perform operations with the obtained ThingDescription + objects.

+
+
+ Example + 9: Fetch the + Thing Description of a Thing +
+
let url = "https://mythings.com/thing1";
+let td = await WOT.requestThingDescription(url);
+console.log("Found Thing Description for " + td.title);
+
+

The next example finds ThingDescription + objects of Things + listed in a TD + Directory service. We set a timeout for safety.

+
+
+ Example + 10: Discover + Things via directory +
+
let discovery = await WOT.exploreDirectory("http://directory.wotservice.org");
+setTimeout( () => {
+    discovery.stop();
+    console.log("Discovery stopped after timeout.");
+  },
+  3000);
+for await (const td of discovery) {
+  console.log("Found Thing Description for " + td.title);
+  let thing = new ConsumedThing(td);
+  console.log("Thing name: " + thing.getThingDescription().title);
+};
+if (discovery.error) {
+  console.log("Discovery stopped because of an error: " + error.message);
+}
+
+

The next example is for a generic discovery, by any means + provisioned to the WOT runtime, including local Things, if + any is available.

+
+
+ Example + 11: Discover + Things in a network +
+
let discovery = await WOT.discover();
+setTimeout( () => {
+    discovery.stop();
+    console.log("Stopped open-ended discovery");
+  },
+  10000);
+for await (const td of discovery) {
+  console.log("Found Thing Description for " + td.title);
+};
+if (discovery.error) {
+  console.log("Discovery stopped because of an error: " + error.message);
+}
+
+
+
+
+
+

11. Security and + Privacy

+
+

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]. + 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]. + That document may be updated as security measures evolve. + Following these practices does not guarantee security, but it + might help avoid commonly known vulnerabilities.

+
+ The WoT security risks and possible mitigations are + concerning the following groups: +
    +
  • Implementors of WoT Runtimes that do not implement a + Scripting Runtime. The [WOT-ARCHITECTURE] + document provides generic security guidelines for this + group.
  • +
  • Implementors of the WoT Scripting API in a WoT + Scripting Runtime. This is the main scope and is covered in + the Scripting + Runtime Security and Privacy Risks sub-section that + contains normative text regarding security. +
  • +
  • WoT script developers, covered in the Script Security and + Privacy Risks sub-section that contains informative + recommendations concerning security. +
  • +
+
+
+
+

+ 11.1 Scripting Runtime Security + and Privacy Risks

+
+

This section is normative and contains specific risks + relevant for the WoT Scripting Runtime.

+
+
+

+ 11.1.1 Corrupted Input Security + and Privacy Risk

+
+

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.

+
+
Mitigation:
+
+ Implementors of this API SHOULD perform validation on all script + inputs. In addition to input validation, fuzzing + should be used to verify that the input processing is + done correctly. There are many tools and techniques in + existence to do such validation. More details can be + found in [WOT-SECURITY]. +
+
+
+
+
+

+ 11.1.2 Physical Device Direct + Access Security and Privacy Risk

+
+

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).

+
+
Mitigation:
+
The WoT Scripting Runtime SHOULD avoid directly exposing the native + device interfaces to the script developers. Instead, a + WoT Scripting Runtime should provide a hardware + abstraction layer for accessing the native device + interfaces. Such hardware abstraction layer should refuse + to execute commands that might put the device (or + environment) to an unsafe state. Additionally, in order + to reduce the damage to a physical WoT device in cases a + script gets compromised, it is important to minimize the + number of interfaces that are exposed or accessible to a + particular script based on its functionality.
+
+
+
+
+

+ 11.1.3 Provisioning and Update + Security Risk

+
+

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.

+
+
Mitigation:
+
Post-manufacturing provisioning or update of scripts, + WoT Scripting Runtime or any related data should be done + in a secure fashion. A set of recommendations for secure + update and post-manufacturing provisioning can be found + in [WOT-SECURITY].
+
+
+
+
+

+ 11.1.4 Security Credentials + Storage Security and Privacy Risk

+
+

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.

+
+
Mitigation:
+
The WoT Scripting Runtime should securely store the + provisioned security credentials, guaranteeing their + integrity and confidentiality. In case there are more + than one tenant on a single WoT-enabled device, a WoT + Scripting Runtime should guarantee isolation of each + tenant provisioned security credentials. Additionally, in + order to minimize a risk that provisioned security + credentials get compromised, the WoT Scripting Runtime + should not expose any API for scripts to query the + provisioned security credentials.
+
+
+
+
+
+

+ 11.2 Script Security and Privacy + Risks

+
+

This section is non-normative.

+

This section describes specific risks relevant for script + developers.

+
+
+

+ 11.2.1 Corrupted Script Input + Security and Privacy Risk

+
+

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.

+
+
Mitigation:
+
+ Script developers should perform validation on all + application defined script inputs. In addition to input + validation, fuzzing + could be used to verify that the input processing is + done correctly. There are many tools and techniques in + existence to do such validation. More details can be + found in [WOT-SECURITY]. +
+
+
+
+
+

+ 11.2.2 Denial Of Service + Security Risk

+
+

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.

+
+
Mitigation:
+
Scripts should avoid heavy functional processing + without prior successful authentication of requestor. The + set of recommended authentication mechanisms can be found + in [WOT-SECURITY].
+
+
+
+
+
+
+

A. + API design rationale

+
+

API rationale usually belongs to a separate document, but in + the WoT case the complexity of the context justifies including + basic rationale here.

+
+
+

+ A.1 Approaches to WoT application + development

+
+

The WoT Interest Group and Working Group have explored + different approaches to application development for WoT that + have been all implemented and tested.

+
+
+

A.1.1 No Scripting API

+
+

It is possible to develop WoT applications that only use + the WoT network + interface, typically exposed by a WoT gateway that + presents a RESTful API towards clients and implements IoT + protocol plugins that communicate with supported IoT + deployments. One such implementation is the Mozilla WebThings + platform.

+
+
+
+

A.1.2 Simple Scripting API

+
+

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.

+
+
+ Example + 12: Open a + lock with a simple API +
+
let lock = await WoT.consume(‘https://td.my.com/lock-00123’);
+console.log(lock.status);
+lock.open('withThisKey');
+
+
+
+
+

+ A.1.3 This API, aligned with the + + Web of Things (WoT) Thing Description 1.1 + specification

+
+

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 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.

+
+
+ Example + 13: Open a + lock +
+
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 + Web of Things (WoT) Thing Description 1.1 + 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 the + Web of Things (WoT) Thing Description 1.1 + specification.

+

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.

+
+
+
+

A.2 Fetching and validating a + TD

+
+

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 the + Web of Things (WoT) Thing Description 1.1 + specification, that is scoped out, too. This specification + expects a TD as + + parsed JSON object that has been validated according to + the + Web of Things (WoT) Thing Description 1.1 + specification.

+
+
+
+

A.3 Factory vs constructors

+
+

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.

+
+
+
+

A.4 + Observers

+
+

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.

+
+
+
+

A.5 + Using Events

+
+
+ This API ended up not using software events at all, for the + following reasons: +
    +
  • Subscription to WoT Events may be different from + handling software events (subscription might need + parameters, might involve security tokens etc). +
  • +
  • Most implementations are for Node.js and browser + implementations will likely be libraries (because + possible dependency management issues in native + implementations), using Events has been challenging.
  • +
  • Observing Property changes and + handling WoT Events is done with the + solution above. +
  • +
+
+
+
+
+

A.6 Polymorphic functions

+
+

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 the + Web of Things (WoT) Thing Description 1.1 + specification.

+
+
+
+
+

B. + Changes

+
+
+ The following is a list of major changes to the document. + Major versions of this specification are the following: +
    +
  • First Public Working Draft + September 2017. +
  • +
  • Working Draft + April 2018. +
  • +
  • Working Draft + November 2018. +
  • +
  • Working draft + October 2019. +
  • +
  • This version, introducing the following major changes: +
      +
    • Added support for formIndex, + InteractionData including streams.
    • +
    • Cleaned up the specification prose, aligned + terminology and used modern ReSpec.
    • +
    • Improved algorithm descriptions, included + illustrative figures.
    • +
    +
  • +
+
+

For a complete list of changes, see the github + change log. You can also view the + recently closed issues.

+
+
+
+

C. Full Web + IDL

+
+
WebIDLtypedef object ThingDescription;
+
+[SecureContext, Exposed=(Window,Worker)]
+namespace WOT {
+  // methods defined in UA conformance classes
+};
+
+partial namespace WOT {
+  Promise<ConsumedThing> consume(ThingDescription td);
+};
+
+typedef object ExposedThingInit;
+
+partial namespace WOT {
+  Promise<ExposedThing> produce(ExposedThingInit init);
+};
+
+partial namespace WOT {
+  Promise<ThingDiscoveryProcess> discover(optional ThingFilter filter = {});
+};
+
+partial namespace WOT {
+  Promise<ThingDiscoveryProcess> exploreDirectory(USVString url,
+      optional ThingFilter filter = {});
+};
+
+partial namespace WOT {
+  Promise<ThingDescription> requestThingDescription(USVString url);
+};
+
+typedef any DataSchemaValue;
+typedef (ReadableStream or DataSchemaValue) InteractionInput;
+
+[SecureContext, Exposed=(Window,Worker)]
+interface InteractionOutput {
+  readonly attribute ReadableStream? data;
+  readonly attribute boolean dataUsed;
+  readonly attribute Form? form;
+  readonly attribute DataSchema? schema;
+  Promise<ArrayBuffer> arrayBuffer();
+  Promise<DataSchemaValue> value();
+};
+
+[SecureContext, Exposed=(Window,Worker)]
+interface ConsumedThing {
+  constructor(ThingDescription td);
+  Promise<InteractionOutput> readProperty(DOMString propertyName,
+                              optional InteractionOptions options = {});
+  Promise<PropertyReadMap> readAllProperties(
+                              optional InteractionOptions options = {});
+  Promise<PropertyReadMap> readMultipleProperties(
+                              sequence<DOMString> propertyNames,
+                              optional InteractionOptions options = {});
+  Promise<undefined> writeProperty(DOMString propertyName,
+                              InteractionInput value,
+                              optional InteractionOptions options = {});
+  Promise<undefined> writeMultipleProperties(
+                              PropertyWriteMap valueMap,
+                              optional InteractionOptions options = {});
+  /*Promise<undefined> writeAllProperties(
+                              PropertyWriteMap valueMap,
+                              optional InteractionOptions options = {});*/
+  Promise<InteractionOutput> invokeAction(DOMString actionName,
+                              optional InteractionInput params = {},
+                              optional InteractionOptions options = {});
+  Promise<Subscription> observeProperty(DOMString name,
+                              InteractionListener listener,
+                              optional ErrorListener onerror,
+                              optional InteractionOptions options = {});
+  Promise<Subscription> subscribeEvent(DOMString name,
+                              InteractionListener listener,
+                              optional ErrorListener onerror,
+                              optional InteractionOptions options = {});
+  ThingDescription getThingDescription();
+};
+
+dictionary InteractionOptions {
+  unsigned long formIndex;
+  object uriVariables;
+  any data;
+};
+
+[SecureContext, Exposed=(Window,Worker)]
+interface Subscription {
+  readonly attribute boolean active;
+  Promise<undefined> stop(optional InteractionOptions options = {});
+};
+
+[SecureContext, Exposed=(Window,Worker)]
+interface PropertyReadMap {
+  readonly maplike<DOMString, InteractionOutput>;
+};
+
+[SecureContext, Exposed=(Window,Worker)]
+interface PropertyWriteMap {
+  readonly maplike<DOMString, InteractionInput>;
+};
+
+callback InteractionListener = undefined(InteractionOutput data);
+callback ErrorListener = undefined(Error error);
+
+[SecureContext, Exposed=(Window,Worker)]
+interface ExposedThing {
+  ExposedThing setPropertyReadHandler(DOMString name,
+          PropertyReadHandler handler);
+  ExposedThing setPropertyWriteHandler(DOMString name,
+          PropertyWriteHandler handler);
+  ExposedThing setPropertyObserveHandler(DOMString name,
+          PropertyReadHandler handler);
+  ExposedThing setPropertyUnobserveHandler(DOMString name,
+          PropertyReadHandler handler);
+  Promise<undefined> emitPropertyChange(DOMString name,
+          optional InteractionInput data);
+
+  ExposedThing setActionHandler(DOMString name, ActionHandler action);
+
+  ExposedThing setEventSubscribeHandler(DOMString name,
+          EventSubscriptionHandler handler);
+  ExposedThing setEventUnsubscribeHandler(DOMString name,
+          EventSubscriptionHandler handler);
+  Promise<undefined> emitEvent(DOMString name,
+          optional InteractionInput data);
+
+  Promise<undefined> expose();
+  Promise<undefined> destroy();
+
+  ThingDescription getThingDescription();
+};
+
+callback PropertyReadHandler = Promise<InteractionInput>(
+        optional InteractionOptions options = {});
+
+callback PropertyWriteHandler = Promise<undefined>(
+        InteractionOutput value,
+        optional InteractionOptions options = {});
+
+callback ActionHandler = Promise<InteractionInput>(
+        InteractionOutput params,
+        optional InteractionOptions options = {});
+
+callback EventSubscriptionHandler = Promise<undefined>(
+        optional InteractionOptions options = {});
+
+[SecureContext, Exposed=(Window,Worker)]
+interface ThingDiscoveryProcess {
+  constructor(optional ThingFilter filter = {});
+  readonly attribute boolean done;
+  readonly attribute Error? error;
+  undefined stop();
+  async iterable<ThingDescription>;
+};
+
+dictionary ThingFilter {
+  object? fragment;
+  
+};
+
+
+
+

D. + Acknowledgements

+
+

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.

+
+
+
+

E. + References

+
+
+
+

E.1 Normative references

+
+
+
[ECMASCRIPT]
+
+ ECMAScript + Language Specification. Ecma International. + URL: https://tc39.es/ecma262/multipage/ +
+
[fetch]
+
+ Fetch + Standard. Anne van Kesteren. WHATWG. Living + Standard. URL: https://fetch.spec.whatwg.org/ +
+
[html]
+
+ HTML + Standard. Anne van Kesteren; Domenic Denicola; + Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. + Living Standard. URL: https://html.spec.whatwg.org/multipage/ +
+
[infra]
+
+ Infra + Standard. Anne van Kesteren; Domenic Denicola. + WHATWG. Living Standard. URL: https://infra.spec.whatwg.org/ +
+
[JSON-SCHEMA]
+
+ + JSON Schema: A Media Type for Describing JSON + Documents. Austin Wright; Henry Andrews; Ben + Hutton; Greg Dennis. Internet Engineering Task Force + (IETF). 8 December 2020. Internet-Draft. URL: + https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema +
+
[RFC2119]
+
+ Key words + for use in RFCs to Indicate Requirement + Levels. S. Bradner. IETF. March 1997. Best + Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119 +
+
[RFC8174]
+
+ Ambiguity + of Uppercase vs Lowercase in RFC 2119 Key + Words. B. Leiba. IETF. May 2017. Best Current + Practice. URL: https://www.rfc-editor.org/rfc/rfc8174 +
+
[streams]
+
+ Streams + Standard. Adam Rice; Domenic Denicola; Mattias + Buelens; 吉野剛史 (Takeshi Yoshino). WHATWG. Living Standard. + URL: https://streams.spec.whatwg.org/ +
+
[TYPESCRIPT]
+
+ + TypeScript Language Specification. + Microsoft. 1 October 2012. URL: + https://www.typescriptlang.org/docs/handbook/intro.html +
+
[url]
+
+ URL + Standard. Anne van Kesteren. WHATWG. Living + Standard. URL: https://url.spec.whatwg.org/ +
+
[WEBIDL]
+
+ Web IDL + Standard. Edgar Chen; Timothy Gu. WHATWG. + Living Standard. URL: https://webidl.spec.whatwg.org/ +
+
[WOT-ARCHITECTURE]
+
+ + Web of Things (WoT) Architecture 1.1. + W3C. 19 January 2023. URL: + https://www.w3.org/TR/2023/CR-wot-architecture11-20230119/ +
+
+ [WOT-PROTOCOL-BINDINGS]
+
+ + Web of Things (WoT) Binding Templates. + W3C. 30 January 2020. URL: + https://www.w3.org/TR/2020/NOTE-wot-binding-templates-20200130/ +
+
[WOT-SECURITY]
+
+ + Web of Things (WoT) Security and Privacy + Guidelines. W3C. 6 November 2019. URL: + + https://www.w3.org/TR/2019/NOTE-wot-security-20191106/ +
+
[WOT-TD]
+
+ + Web of Things (WoT) Thing Description + 1.1. W3C. 19 January 2023. URL: + https://www.w3.org/TR/2023/CR-wot-thing-description11-20230119/ +
+
+
+
+
+

E.2 Informative references

+
+
+
[WOT-DISCOVERY]
+
+ + Web of Things (WoT) Discovery. W3C. 19 January + 2023. URL: https://www.w3.org/TR/2023/CR-wot-discovery-20230119/ +
+
[WOT-USE-CASES]
+
+ + Web of Things (WoT): Use Cases and + Requirements. W3C. 7 March 2022. URL: https://www.w3.org/TR/2022/NOTE-wot-usecases-20220307/ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/releases/note2/fix-id.pl b/releases/note2/fix-id.pl new file mode 100755 index 0000000..bcbcf70 --- /dev/null +++ b/releases/note2/fix-id.pl @@ -0,0 +1,11 @@ +#!/usr/bin/perl + +while (<>) { + chomp; + if (/id="webidl-([0-9]+)"/) { + $num = $1; + } elsif (/id="idl-def-wot"/) { + s/id="idl-def-wot"/id="idl-def-wot-${num}"/; + } + print "$_\n"; +}