The Web of Things is made of entities (Things) that can describe their
+ capabilities in a machine-interpretable Thing
+ Description (TD) and expose these capabilities through the
+ WoT
+ Interface, that is, network interactions modeled as
+ Properties (for reading
+ and writing values), Actions (to
+ execute remote procedures with or without return values) and
+ Events (for signaling
+ notifications).
Scripting is an optional 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
+
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.
then instantiating a software stack that implements the
+ WoT
+ Interface specified by the TD in order to serve requests for
+ accessing the exposed Properties, Actions and Events,
+
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.
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:
+
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.
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.
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.
+ To expand a TD given
+ td, run the
+ following steps:
+
+
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].
+
+
+
+
+
+
+
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:
+
+
+ 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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
+
Let thing be a
+ new ConsumedThing
+ object constructed from td.
+
Implementations encapsulate the
+ complexity of how to use the Protocol
+ Bindings for implementing WoT interactions.
+ In the future elements of that could be
+ standardized.
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.
+ 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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
+
Let thing be a
+ new ExposedThing
+ object constructed with init.
+
Let td be the
+ result of running clone
+ given init.
+
+
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.
+
+
If td.["security"]
+ does not exist in
+ td.["securityDefinitions"],
+ then remove security from td.
+
+
For each affordance in td.properties, td.actions and td.events, run the following
+ sub-steps:
+
+
For each form in
+ affordance.forms:
+
+
If form.contentType is not recognized by
+ the runtime as valid remove contentType from form.
+
If form.href
+ has an unknown schema, remove href from form.
+
If form.href
+ is absolute and its authority it is
+ not recognized by the runtime as valid, remove
+ href from
+ form.
+
If form.href
+ is already in use by other ExposedThings,
+ remove href from
+ form.
+
+
+
+
+
+
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.
+
+
+
For each missing property run these
+ sub-steps:
+
+
If missing is title
+ generate a runtime unique name and assign to
+ title.
+
If missing is @context
+ assign the latest supported Thing Description context
+ URI.
+
If missing is instance
+ assign the string 1.0.0.
+
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.
+
+
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
+
+
+
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.
+
+
Add missing to td with value as
+ value
+
+
+
Run validate a TD on
+ td. If that
+ fails re-throw
+ the error and stop
+
+
Return td
+
+
+
+
+
+ 6.2.2 Validating an
+ ExposedThingInit
+
To validate an ExposedThingInit given
+ init, run the
+ following steps:
+
+
Parse
+ TD JSON Schema and load it in object called
+ exposedThingInitSchema
+
+
let optional be a list
+ containing the following strings: title,
+ @context, instance,
+ forms, security, and
+ href.
+
For each property and sub-property key in
+ exposedThingInitSchema
+ equals to required execute the following
+ steps:
+
+
if keyvalue is an
+ Array then remove all its elements equal
+ to the elements in optional
+
if keyvalue is a
+ string then if value is equal
+ to one of the elements in optional remove key from
+ exposedThingInitSchema
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.
+ 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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
+
If discovery is not supported by the implementation,
+ reject
+ promise with
+
+ NotSupportedError and stop.
+
If filters in general are not supported by the
+ implementation and filter is not undefined
+ or null, reject
+ promise with
+
+ NotSupportedError and stop.
+
+
If discovery cannot be started by the underlying
+ platform, reject
+ promise with
+ OperationError
+ and stop.
+
+
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.
+
+ 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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
+
If directory discovery is not supported by the
+ implementation, reject
+ promise with
+
+ NotSupportedError and stop.
+
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.
If filters in general are not supported by the
+ implementation and filter is not
+ undefined or null,
+ reject
+ promise with
+
+ NotSupportedError and stop.
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
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.
+
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.
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.
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:
+
+
If this.[[value]] is not
+ undefined, resolve
+ promise with that value
+ and stop.
+
+
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.
+
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.
+
+
Let json be the result of running
+
+ parse JSON from bytes on bytes. If that
+ throws, reject
+ promise with that
+ exception and stop.
+
+
Set [[value]] to the result of
+ running check data schema
+ on json and schema. If that throws,
+ reject
+ promise with that
+ exception and stop.
+
If type is "null" and if
+ payload is not null, throw
+
+ TypeError and stop, otherwise return
+ null.
+
+
If type is "boolean" and
+ payload is a falsy value or its byte length is
+ 0, return false, otherwise return
+ true.
+
If type is "integer" or
+ "number",
+
+
If payload is not a number, throw
+
+ TypeError and stop.
+
+
If form.minimum is defined
+ and payload is smaller, or if
+ form.maximum is defined and
+ payload is bigger, throw a
+
+ RangeError and stop.
+
+
+
+
If type is "string", return
+ payload.
+
If type is "array", run these
+ sub-steps:
+
+
If payload is not an array, throw
+
+ TypeError and stop.
+
+
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.
+
+
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.
+
+
+
+
If type is "object", run
+ these sub-steps:
+
+
If payload or
+ schema.properties is not an
+ object,
+ throw
+ TypeError and stop.
+
+
For each key in payload:
+
+
Let prop be
+ payload[key].
+
Let propSchema be
+ interaction.properties[key].
+
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.
+
+
+
+
Let required be schema.required if that is
+ an array or an empty array otherwise.
+
For each key in required,
+ if key is not present in
+ payload, throw
+ SyntaxError and stop.
+
+
+
+
Return payload.
+
+
+
+
+
+ 7.2.4 The create
+ interaction request algorithm
Set idata.form
+ to form, set
+ idata.schema to
+ schema, set
+ |idata.data to null and set
+ idata.[[value]] to
+ undefined.
+
+
If source is
+ a
+ ReadableStream object, let
+ idata.data be source, return
+ idata and stop.
+
+
If schema and its
+ type are defined and not null, run
+ these sub-steps:
+
+
If type is "null" and
+ source is not
+ "null", throw
+ TypeError and stop.
+
+
If type is "boolean" and
+ source is a
+ falsy value, set
+ idata.[[value]] to
+ false, otherwise set it to
+ true.
+
+
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.
+
If source is not an array,
+ throw a
+ TypeError and stop.
+
+
Let length be the length of
+ source.
+
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.
+
+
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.
+
If source is not an object,
+ throw
+ TypeError and stop.
+
+
If schema.properties is
+ not an object, throw
+ TypeError and stop.
+
+
For each key in source,
+
+
Let value be source[key].
+
Let propschema be
+ properties.interactions[key].
+
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.
+
+
+
+
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.
+
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
+ 1Data structures used when reading data
+
+
+
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.
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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
+
Let interaction be [[td]].properties.propertyName.
+
+
If interaction is undefined,
+ reject
+ promise with a
+ NotFoundError
+ and stop.
+
+
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.
+
+
If form is failure, reject
+ promise with a
+ SyntaxError
+ and stop.
+
+
Make a request to the underlying platform (via the
+ Protocol Bindings)
+ to retrieve the value of the propertyNameProperty using
+ form and the optional URI templates given in
+ options.uriVariables.
+
+
If the request fails, reject
+ promise with the error
+ received from the Protocol
+ Bindings and stop.
+
+
Let response be the response received to
+ the request.
+ 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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
+
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.
+
+
If form is failure, reject
+ promise with a
+ SyntaxError
+ and stop.
+
+
Let result be an object
+ and for each string name in
+ propertyNames add
+ a property with key name
+ and the value null.
+
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.
+
+ 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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
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.
+
+ 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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
+
Let interaction be
+ this.[[td]].properties[propertyName].
+
+
If interaction is undefined,
+ reject
+ promise with a
+ NotFoundError
+ and stop.
+
+
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.
+
+
If form is failure, reject
+ promise with a
+ SyntaxError
+ and stop.
+
+
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.
+
+
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.
+
+
If the request fails, reject
+ promise with the error
+ received from the Protocol
+ Bindings and stop.
+
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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
+
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.
+
+
If form is failure, reject
+ promise with a
+ SyntaxError
+ and stop.
+
+
Let propertyNames be an array of
+ string with as elements the keys of the
+ properties object.
+
For each name in
+ propertyNames, let property be
+ this.[[td]].properties[name].
+
+
If property is null or
+ undefined or is not writeable
+ reject
+ promise with
+
+ NotSupportedError and stop.
+
+
Let result be an object
+ and for each string name in
+ propertyNames add a property with key
+ name and let its value be
+ null.
+
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].
+
+
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.
+
+
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.
+
+ 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.
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
Make a request to the underlying platform to observe
+ the Property identified by
+ propertyName with
+ form and optional URI templates given in
+ options'
+ uriVariables.
+
+
If the request fails, reject
+ promise with the error
+ received from the Protocol
+ Bindings and stop.
+
Whenever the underlying platform detects a
+ notification for this subscription
+ keyed
+ on propertyName with a
+ new Property value
+ value, run the following sub-steps:
+
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.
+
+
+
+
+
+
+
+
+
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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
+
Let interaction be
+ this.[[td]].actions[actionName].
+
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.
+
+
Make a request to the underlying platform (via the
+ Protocol Bindings)
+ to invoke the Action identified by
+ actionName given
+ args and options.uriVariables.
+
+
If the request fails locally or returns an error over
+ the network, reject
+ promise with the error
+ received from the Protocol
+ Bindings and stop.
+
+
Let value be the reply returned in the
+ reply.
+
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.
+
+ 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.
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
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.
+
+
If the request fails, reject
+ promise with the error
+ received from the Protocol
+ Bindings and stop.
+
Whenever the underlying platform detects an error for
+ Event
+ subscriptionkeyed 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.
+
+
+
+
+
+
+
+
+
+ 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.
+ 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:
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
+
If options'
+ formIndex is defined, let
+ unsubscribeForm be the Form associated with
+ formIndex in [[interaction]]'s
+ forms array.
+
If unsubscribeForm is failure,
+ reject
+ promise with a
+ SyntaxError
+ and stop.
+
+
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.
+
+
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.
+
+
If the request fails, reject
+ promise with the error
+ received from the Protocol
+ Bindings and stop.
+
This algorithm is under development and is
+ non-normative. Implementations MAY choose another algorithm to find a
+ matching unsubscribeForm to a given
+ subscribeForm.
+
To find a matching unsubscribe form given
+ subscribeForm in the context of a
+ Subscription
+ object, run the following steps:
+
+
Add an
+ internal slot [[matchLevel]] on form
+ and set its value to 0.
+
+
If form.op is
+ "unobserveproperty" if
+ [[type]] is
+ "property" or if
+ form.op is
+ "unsubscribeevent" if
+ [[type]]
+ is"event",
+
+
Set the
+ internal slot [[matchLevel]] on
+ form to 1 and add form to
+ results.
+
+
If form.href and
+ [[subscribeForm]].href are
+ same origin-domain, increment
+ form.[[matchLevel]].
+
+
If form.contentType is
+ equal to [[subscribeForm]]'s
+ contentType and
+ form.[[matchLevel]] is greater than 2,
+ increment form.[[matchLevel]].
+
+
+
+
+
If results is empty, return
+ null and terminate these steps.
+
Return the first form in
+ results that has the highest [[matchLevel]]
+ value.
+
+
+
+
+
+
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);
+
+ 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(functionprocessVideo({ 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.
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.
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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ throw
+ a SecurityError
+ and stop.
+
Let value be the result of invoking
+ handler given
+ options. If
+ that fails, throw the error and stop.
+
Return value.
+
+
+ Note
+
+
The value returned here
+ SHOULD either conform to
+ DataSchema or it
+ SHOULD be an
+
+ ReadableStream object created by
+ the handler.
+
+
+
+
+
If the previous step has thrown an error, send the
+ error back with the reply created by following the
+ Protocol Bindings
+ and stop.
+
+
Serialize and add the returned value to
+ the reply created by following the Protocol Bindings.
+
+
+
+
+
+
+
+ 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:
+
+
For each property with key name defined in
+ propertyNames,
+
+
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.
+
+
Set propertyNames.name to
+ value.
+
+
+
Reply to the request by sending a single reply
+ created from propertyNames according to the
+ Protocol Bindings.
+
+
+
+
+
+
+
+ 9.8 Handling requests for reading
+ all Properties
+
+
+ When a network request for reading all Properties is received
+ with options, run
+ the following steps:
+
+
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.
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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ throw
+ a SecurityError
+ and stop.
+
If this.[[td]].properties[name]
+ does not exist,
+ send back a NotFoundError
+ in the reply and stop.
+
+
Internally save the request sender information
+ together with options and
+ this.[[propertyObservers]][name],
+ in order to be able to notify about Property value
+ changes.
+
+
+
+
+
+ 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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ throw
+ a SecurityError
+ and stop.
+
+ 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:
+
+
Return promise and
+ execute the next steps
+ in parallel.
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ throw
+ a SecurityError
+ and stop.
+
+ When a network request for writing a Propertyname 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":
+
+
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.
+
+
Reply to the request by sending a single reply
+ according to the Protocol
+ Bindings.
+
+
+
+
+
+
+
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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ throw
+ a SecurityError
+ and stop.
+
+ When a network request for invoking the Action identified by
+ name is received given
+ inputs and optionally options, run the following
+ steps:
+
+
Let promise be the result of invoking
+ handler given
+ name, inputs and
+ options.
+
If promise rejects, send the error with
+ the reply and stop.
+
When promise resolves with
+ data, use
+ data to create
+ and send the reply according to the Protocol
+ Bindings.
+
+
+
+
+
+
+
+ 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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ throw
+ a SecurityError
+ and stop.
+
Otherwise implement the default subscriber mechanism:
+
+
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.
+
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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ throw
+ a SecurityError
+ and stop.
+
For each subscriber in
+ listeners, run the following sub-steps:
+
+
Create an Event notification
+ response according to the Protocol
+ Bindings from data and
+ subscriber, including its
+ options.
+
+
If data
+ is undefined, assume that the
+ notification response will contain an
+ empty data payload as specified by Protocol
+ Bindings.
+
+
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.
+
+
+
Send response to the subscriber
+ identified by subscriber.
+
+
+
+
+
+
+
+
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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
If an Event
+ with the name name is not
+ found, reject
+ promise with
+ NotFoundError
+ and stop.
+
+
Make a request to the underlying platform to emit an
+ Event with optional
+ data. Call the
+ handling events steps.
+
+
+
+
+
+
+
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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
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.
+
+
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.
+
+
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).
+
+
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.
+
+ 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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ reject
+ promise with a
+ SecurityError
+ and stop.
+
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.
+
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.
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.
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.
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:
+
+
Whenever a new link to a Thing Description
+ is discovered and provided by the underlying platform,
+ run the following sub-steps:
+
+
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.
+
+
If that fails, set the discovery.error
+ property to
+ SyntaxError, discard td and continue the
+ discovery process.
+
+
+
+
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.
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.
+
+
+
+
+
+
+
+
+
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:
+
+
If invoking this method is not allowed for the
+ current scripting context for security reasons,
+ throw
+ a SecurityError
+ and stop.
+
+
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.
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);
let discovery = await WOT.discover();
+setTimeout( () => {
+ discovery.stop();
+ console.log("Stopped open-ended discovery");
+ },
+ 10000);
+forawait (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.
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.
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.
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:
+
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.