The overall Web of Things (WoT) concepts are described in the WoT Architecture document. The Web of Things is made of entities (Things) that can describe their capabilities in a machine-interpretable format, the 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.
This specification describes a programming interface representing the WoT Interface that allows scripts run on a Thing to discover and consume (retrieve) other Thing Descriptions and to expose Things characterized by WoT Interactions specified by a script.
Scripting is an optional "convenience" building block in WoT and it is typically used in gateways that are able to run a WoT Runtime and script management, providing a convenient way to extend WoT support to new types of endpoints and implement WoT applications like Thing Directory.
Implementers need to be aware that this specification is considered unstable. Vendors interested in implementing this specification before it eventually reaches the Candidate Recommendation phase should subscribe to the repository and take part in the discussions.
Please contribute to this draft using the GitHub Issue feature of the WoT Scripting API repository. For feedback on security and privacy considerations, please use the WoT Security and Privacy Issues.
WoT provides layered interoperability based on how Things are modeled: as being "consumed" and "exposed".
By consuming a TD, a client Thing creates a runtime resource model that allows accessing the Properties, Actions and Events exposed by the server Thing exposed on a remote device.
Exposing a Thing requires defining a Thing Description (TD) and instantiating a software stack to serve requests for accessing the exposed Properties, Actions and Events. This specification describes how to expose and consume Things by a script.
Typically scripts are meant to be used on devices able to provide resources (with a WoT Interface) for managing (installing, updating, running) scripts, such as bridges or gateways that expose and control simpler devices as WoT Things.
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.
For an introduction on how scripts could be used in Web of Things, check the Primer document. For some background on API design decisions check the Rationale document.
The following scripting use cases are supported in this specification:
The WoT object is the API entry point and it is exposed by an implementation of the WoT Runtime. The WoT object does not expose properties, only methods for discovering, consuming and exposing a Thing.
Browser implementations SHOULD use a namespace object such as navigator.wot
. Node.js-like runtimes MAY provide the API object through the require() or import mechanism.
// [SecureContext] // [NamespaceObject] interface WoT { Observable discover(optional ThingFilter filter); Promise<ThingDescription> fetch(USVString url); ConsumedThing consume(ThingDescription td); ExposedThing produce(ThingModel model); Promise<void> register(USVString directory, ExposedThing thing); Promise<void> unregister(USVString directory, ExposedThing thing); }; typedef object ThingFragment; typedef object PropertyFragment; typedef object ActionFragment; typedef object EventFragment; typedef object DataSchema; typedef object SecurityScheme; typedef object Link; typedef object Form; typedef USVString ThingDescription; typedef (ThingFragment or ThingDescription) ThingModel;
The algorithms for the WoT methods will be specified later, including error handling and security considerations.
The ThingModel type represents either a ThingFragment, or a ThingDescription.
Starts the discovery process that will provide ThingDescriptions that match the optional argument filter of type ThingFilter
. Returns an [Observable
](https://github.com/tc39/proposal-observable) object that can be subscribed to and unsubscribed from. The handler function provided to the Observable during subscription will receive an argument of type USVString
representing a ThingDescription.
typedef DOMString DiscoveryMethod;
DiscoveryMethod represents the discovery type to be used:
The ThingFilter dictionary that represents the constraints for discovering Things as key-value pairs.
dictionary ThingFilter { (DiscoveryMethod or DOMString) method = "any"; USVString? url; USVString? query; ThingFragment? fragment; };
The method property represents the discovery type that should be used in the discovery process. The possible values are defined by the DiscoveryMethod
enumeration that MAY be extended by string values defined by solutions (with no guarantee of interoperability).
The url property represents additional information for the discovery method, such as the URL of the target entity serving the discovery request, for instance a Thing Directory (if method
is "directory"
) or a Thing (otherwise).
The query property represents a query string accepted by the implementation, for instance a SPARQL or JSON query. Support may be implemented locally in the WoT Runtime or remotely as a service in a Thing Directory.
The fragment property represents a ThingFragment dictionary used for matching property by property against discovered Things.
The discover(filter)
method MUST run the following steps:
discover()
is not allowed for the current scripting context for security reasons, throw SecurityError
and terminate these steps.
Observable
obs and execute the next steps in parallel.
obs.subscribe(handler, errorHandler, complete)
is called, execute the following sub-steps:
TypeError
and terminate the algorithm. Otherwise configure handler to be invoked when a discovery hit happens.
TypeError
and terminate these steps. Otherwise if defined, save it to be invoked in error conditions.
TypeError
and terminate these steps. Otherwise if defined, save it to be invoked when the discovery process finished for other reasons than having been canceled.
NotSupported
error and terminate these steps.
TypeError
and terminate these steps. Otherwise save the object for matching the discovered items against it.
"any"
, use the widest discovery method supported by the underlying platform.
"local"
, use the local Thing Directory for discovery. Usually that defines Things deployed in the same device, or connected to the device in slave mode (e.g. sensors connected via Bluetooth or a serial connection).
"directory"
, use the remote Thing Directory specified in filter.url.
"multicast"
, use all the multicast discovery protocols supported by the underlying platform.
false
, discard td and continue the discovery process.
false
in any checks, discard td and continue the discovery process.
Error
whose message
property is set to UnknownError
unless there was an error code provided by the Protocol Bindings, in which case set it to that value.
obs.unsubscribe()
method is called, run the following cancel discovery steps:
false
.
Accepts an url
argument of type USVString
that represents a URL (e.g. "file://..."
or "https://..."
) and returns a Promise
that resolves with a ThingDescription (a serialized JSON-LD document of type USVString
).
The fetch(url)
method MUST run the following steps:
Promise
promise and execute the next steps in parallel.
fetch()
is not allowed for the current scripting context for security reasons, reject promise with SecurityError
and terminate these steps.
TypeError
and terminate these steps.
application/td+json
), as far as a valid Thing Description can be obtained as defined in [[!WOT-TD]]. Let td be the Thing Description string-serialized from the returned content, as specified in the Thing Description serialization.
Error
object error with error.message
set to the error code seen by the Protocol Bindings and terminate these steps.
Accepts an td
argument of type ThingDescription
and returns a ConsumedThing object instantiated based on parsing that description.
The consume(td)
method must run the following steps:
TypeError
and terminate these steps.
read()
and write()
methods to the ThingProperty elements so that they make requests to access the remote Things and wait for the reply, as defined by the Protocol Bindings. Also, all ThingProperty elements SHOULD implement Observable, i.e. define a subscribe()
method that should make request to observe the given Properties as defined by the Protocol Bindings.
invoke()
methods to the ThingAction elements so that they make requests to the remote Thing to invoke its actions, as defined by the Protocol Bindings.
subscribe()
method to all ThingEvent elements so that they make requests to subscribe to the events defined by the remote Thing, as defined by the Protocol Bindings.
Accepts a model
argument of type ThingModel
and returns an ExposedThing object.
The produce(model)
method MUST run the following steps:
produce()
is not allowed for the current scripting context for security reasons, throw SecurityError
and terminate these steps.
TypeError
and terminate these steps.
id
property to be the final unique identifier of the Thing,
security
object of type SecurityScheme to represent the actual security scheme and its properties as set up by the implementation,
properties
property to be an object with all properties being ThingProperty objects in which the read()
and write()
methods are provided to define local methods to get and set the Property values,
actions
property to be an object with all properties being ThingAction objects in which the invoke()
method is provided to define a local method to run the defined Actions,
events
property to be an object with all properties being ExposedEvent objects in which the emit()
method is provided to define a local way to trigger sending notifications to all subscribed clients,
The TD parsing algorithm takes a string td as argument and runs the following steps:
Takes two mandatory arguments:
directory
denoting a Thing Directory, and thing
denoting an ExposedThing object.Generate the Thing Description as td, given the Properties, Actions and Events defined for this ExposedThing object. Then make a request to register td to the given WoT Thing Directory.
Takes two mandatory arguments:
directory
denoting a Thing Directory, and thing
denoting an ExposedThing object.
Makes a request to unregister the thing
from the given WoT Thing Directory.
let discoveryFilter = { method: "directory", url: "http://directory.wotservice.org" }; let subscription = wot.discover(discoveryFilter).subscribe( td => { console.log("Found Thing " + td.name); // fetch the TD and create a ConsumedThing let thing = wot.consume(td); }, error => { console.log("Discovery finished because an error: " + error.message); }, () => { console.log("Discovery finished successfully");} ); setTimeout( () => { subscription.unsubscribe(); console.log("Discovery timeout"); }, 5000);
let subscription = wot.discover({ method: "local" }).subscribe( td => { console.log("Found local Thing " + td.name); }, error => { console.log("Discovery error: " + error.message); }, () => { console.log("Discovery finished successfully");} );
let subscription = wot.discover({ method: "local" }).subscribe({ td => { console.log("Found local Thing " + td.name); }, error: err => { console.log("Discovery error: " + err.message); }, complete: () => { console.log("Discovery finished successfully");} });
Represents an object that extends a ThingFragment with methods for client interactions (send request for reading and writing Properties), invoke Actions, subscribe and unsubscribe for Property changes and Events.
interface ConsumedThing: ThingFragment { readonly attribute DOMString id; readonly attribute DOMString name; readonly attribute DOMString? base; readonly attribute PropertyMap properties; readonly attribute ActionMap actions; readonly attribute EventMap events; // getter for ThingFragment properties getter any(DOMString name); }; [NoInterfaceObject] interface PropertyMap { readonly maplike<DOMString, ThingProperty>; }; [NoInterfaceObject] interface ActionMap { readonly maplike<DOMString, ThingAction>; }; [NoInterfaceObject] interface EventMap { readonly maplike<DOMString, ThingEvent>; }; ConsumedThing includes Observable; // for TD changes
The id attribute represents the unique identifier of the Thing instance, typically a URI, IRI, or URN as USVString
.
The name attribute represents the name of the Thing as DOMString
.
The base attribute represents the base URI that is valid for all defined local interaction resources.
The properties attribute represents a dictionary of ThingProperty items. The PropertyMap interface represents a maplike dictionary where all values are ThingProperty objects. The read()
and write()
methods make a request to access the Properties on the remote Thing represented by this ConsumedThing proxy object.
The actions attribute represents a dictionary of ThingAction items. The ActionMap interface represents a maplike dictionary where all values are ThingAction objects. The invoke()
method represents a request to invoke the Action on the remote Thing.
The events attribute represents a dictionary of ThingEvent items. The EventMap interface represents a maplike dictionary where all values are ThingEvent objects. Subscribing to the events involves setting up an observation (subscription) mechanism on the remote object.
Below a ConsumedThing
interface example is given.
try { let subscription = wot.discover({ method: "local" }).subscribe( td => { let thing = wot.consume(td); console.log("Thing " + thing.name + " has been consumed."); let subscription = thing["temperature"].subscribe(function(value) { console.log("Temperature: " + value); }); thing.actions["startMeasurement"].invoke({ units: "Celsius" }) .then(() => { console.log("Temperature measurement started."); }) .catch(e => { console.log("Error starting measurement."); subscription.unsubscribe(); }) }, error => { console.log("Discovery error: " + error.message); }, () => { console.log("Discovery finished successfully");} ); } catch(error) { console.log("Error: " + error.message); };
The ExposedThing interface is the server API that allows defining request handlers, properties, Actions, and Events to a Thing. It also implements the Observable interface. An ExposedThing is created by the produce() method.
interface ExposedThing: ThingFragment { readonly attribute PropertyMap properties; readonly attribute ActionMap actions; readonly attribute ExposedEvents events; // getter for ThingFragment properties getter any(DOMString name); // setter for ThingFragment properties setter void(DOMString name, any value); // methods to expose and destroy the Thing Promise<void> expose(); Promise<void> destroy(); // define Properties ExposedThing addProperty(DOMString name, PropertyFragment property, optional any initValue); ExposedThing setPropertyReadHandler(DOMString name, PropertyReadHandler readHandler); ExposedThing setPropertyWriteHandler(DOMString name, PropertyWriteHandler writeHandler); ExposedThing removeProperty(DOMString name); // define Actions ExposedThing addAction(DOMString name, ActionFragment init, ActionHandler action); ExposedThing removeAction(DOMString name); ExposedThing setActionHandler(DOMString name, ActionHandler action); // define Events ExposedThing addEvent(DOMString name, EventFragment event); ExposedThing removeEvent(DOMString name); }; [NoInterfaceObject] interface ExposedEvents { maplike<DOMString, ExposedEvent>; }; callback PropertyReadHandler = Promise<any>(); callback PropertyWriteHandler = Promise<void>(any value); callback ActionHandler = Promise<any>(any parameters);
The properties attribute represents a dictionary of ThingProperty items in which the read()
and write()
methods define local methods that access the physical representations of the Properties.
The actions attribute represents a dictionary of ThingAction items in which the invoke()
method represents a local method to invoke the Action.
The events attribute represents a dictionary of ExposedEvent items that add the emit()
method to the ThingEvent definition. The ExposedEvents interface represents a maplike dictionary where all values are ExposedEvent objects.
Start serving external requests for the Thing, so that WoT Interactions using Properties, Actions and Events will be possible.
The expose()
method MUST run the following steps:
Promise
promise and execute the next steps in parallel.
expose()
is not allowed for the current scripting context for security reasons, reject promise with SecurityError
and terminate these steps.
Error
object error with error.message
set to the error code seen by the Protocol Bindings and terminate these steps.
Stop serving external requests for the Thing and destroy the object. Note that eventual unregistering should be done before invoking this method.
The destroy()
method MUST run the following steps:
Promise
promise and execute the next steps in parallel.
destroy()
is not allowed for the current scripting context for security reasons, reject promise with SecurityError
and terminate these steps.
Error
object error with error.message
set to the error code seen by the Protocol Bindings and terminate these steps.
Adds a Property with name defined by the name argument, the data schema provided by the property argument of type PropertyFragment, and optionally an initial value provided in the argument initValue whose type should match the one defined in the type
property according to the value-matching algorithm. If initValue is not provided, it SHOULD be initialized as undefined
. Implementations SHOULD update the Thing Description. Throws on error. Returns a reference to the same object for supporting chaining.
Removes the Property specified by the name
argument and updates the Thing Description. Throws on error. Returns a reference to the same object for supporting chaining.
Adds to the actions
property of a Thing object an Action with name defined by the name argument, defines input and output data format by the init argument of type ActionFragment, and adds the function provided in the action argument as a handler, then updates the Thing Description. Throws on error. Returns a reference to the same object for supporting chaining.
The provided action callback function will implement invoking an Action and SHOULD be called by implementations when a request for invoking the Action is received from the underlying platform. The callback will receive a parameters
dictionary argument according to the definition in the init.input argument and will return a value of type defined by the init.output argument according to the value-matching algorithm.
There SHOULD be exactly one handler for any given Action. If no handler is initialized for any given Action, implementations SHOULD throw a TypeError
.
Removes the Action specified by the name
argument and updates the Thing Description. Throws on error. Returns a reference to the same object for supporting chaining.
Adds an event with name defined by the name argument and qualifiers and initialization value provided by the event argument of type EventFragmentto the Thing object and updates the Thing Description. Throws on error. Returns a reference to the same object for supporting chaining.
Removes the event specified by the name
argument and updates the Thing Description. Returns a reference to the same object for supporting chaining.
A function that is called when an external request for reading a Property is received. It should return a Promise and resolves it with the value of the Property matching the name
argument to the setPropertyReadHandler
function, or rejects with an error if the property is not found or the value cannot be retrieved.
A function that is called when an external request for writing a Property is received. It is given the requested new value
as argument and should return a Promise which is resolved when the value of the Property that matches the name
argument has been updated with value
, or rejects with an error if the property is not found or the value cannot be updated.
Note that this function is invoked by implementations before the property is updated and it actually defines what to do when a write request is received. The code in this callback function can invoke the read()
method to find out the old value of the property, if needed. Therefore the old value is not provided to this function.
A function called with a parameters
dictionary argument assembled by the WoT runtime based on the Thing Description and the external client request. It returns a Promise that rejects with an error or resolves if the action is successful or ongoing (may also resolve with a control object such as an Observable for actions that need progress notifications or that can be canceled).
Takes name
as string argument and readHandler
as argument of type PropertyReadHandler. Sets the handler function for reading the specified Property matched by name
. Throws on error. Returns a reference to the same object for supporting chaining.
The readHandler
callback function will implement reading a Property and SHOULD be called by implementations when a request for reading a Property is received from the underlying platform.
There SHOULD be at most one handler for any given Property and newly added handlers replace the old handlers. If no handler is initialized for any given Property, implementations SHOULD implement a default property read handler.
When an external request for reading Property propertyName is received, the runtime SHOULD execute the following steps:
Promise
promise and execute the next steps in parallel.
ReferenceError
and terminate these steps.
Takes name
as string argument and writeHandler
as argument of type PropertyWriteHandler. Sets the handler function for writing the specified Property matched by name
. Throws on error. Returns a reference to the same object for supporting chaining.
There SHOULD be at most one write handler for any given Property and newly added handlers replace the old handlers. If no write handler is initialized for any given Property, implementations SHOULD implement default property update and notifying observers on change.
When an external request for writing a Property propertyName with a new value value is received, the runtime SHOULD execute the following steps:
Promise
promise and execute the next steps in parallel.
ReferenceError
and terminate these steps.
Takes name
as string argument and action
as argument of type ActionHandler. Sets the handler function for the specified Action matched by name
. Throws on error. Returns a reference to the same 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 SHOULD be at most one handler for any given Action and newly added handlers replace the old handlers.
When an external request for invoking the Action identified by name is received, the runtime SHOULD execute the following steps:
Promise
promise and execute the next steps in parallel.
ReferenceError
and terminate these steps.
ReferenceError
and terminate these steps.
Below some ExposedThing
interface examples are given.
try { var temperatureValueDefinition = { type: "number", minimum: -50, maximum: 10000 }; var temperaturePropertyDefinition = temperatureValueDefinition; // add the 'forms' property temperaturePropertyDefinition.forms = [ ... ]; var thing = WoT.produce({ name: "tempSensor", properties: { temperature: temperaturePropertyDefinition }, actions: { reset: { description: "Reset the temperature sensor", input: { temperature: temperatureValueDefinition }, output: null, forms: [] }, }, events: { onchange: temperatureValueDefinition }, links: [] }); await thing.expose(); await wot.register("https://mydirectory.org", thing); // define Thing business logic setInterval( async () => { let mock = Math.random()*100; let old = await thing["temperature"].read(); if (old < mock) { await thing["temperature"].write(mock); thing.emitEvent("onchange", mock); } }, 1000); } catch (err) { console.log("Error creating ExposedThing: " + err); }
try { var 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 } } }; var statusPropertyDefinition = statusValueDefinition; // add the 'forms' property statusPropertyDefinition["forms"] = []; var thing = WoT.produce({ name: "mySensor", properties: { brightness: { type: "number", minimum: 0.0, maximum: 100.0, required: true, }, status: statusPropertyDefinition }, actions: { status: { description: "Get status object", input: null, output: { status : statusValueDefinition; }, forms: [] }, }, events: { onstatuschange: statusValueDefinition; }, links: [] }); thing.expose().then(() => { thing.register(); }); } catch (err) { console.log("Error creating ExposedThing: " + err); }
let thingDescription = '{ \ "name": "mySensor", \ "@context": [ "http://www.w3.org/ns/td",\ "https://w3c.github.io/wot/w3c-wot-common-context.jsonld" ],\ "@type": [ "Thing", "Sensor" ], \ "geo:location": "testspace", \ "properties": { \ "prop1": { \ "type": "number",\ "@type": [ "Property", "Temperature" ], \ "saref:TemperatureUnit": "degree_Celsius" \ } } }'; try { // note that produce() fails if thingDescription contains error let thing = WoT.produce(thingDescription); // Interactions were added from TD // WoT adds generic handler for reading any property // define a specific handler for one property let name = "examplePropertyName"; thing.setPropertyReadHandler(name, () => { console.log("Handling read request for " + name); return new Promise((resolve, reject) => { let examplePropertyValue = 5; resolve(examplePropertyValue); }, e => { console.log("Error"); }); }); thing.expose(); } catch(err) { console.log("Error creating ExposedThing: " + err); }
// fetch an external TD, e.g., to set up a proxy for that Thing WoT.fetch("http://myservice.org/mySensor/description").then(td => { // WoT.produce() ignores instance-specific metadata (security, form) let thing = WoT.produce(td); // Interactions were added from TD // add server functionality // ... });
The [[!WOT-TD]] specification defines the WoT information model, i.e. the data types and data structures used in WoT Interactions. In this API these definitions translate to dictionary objects that are extended with methods by the interfaces defined in this specification.
In order to avoid duplication of definitions, references to these data types and structures is defined in this section, but for their full description please refer to the Thing Description specification.
Value types basically represent types that may be used in JSON object definitions and are used in ThingFragment to define Properties, Events and Action parameters. Value types are represented as dictionary objects whose properties and possible sub-classes are defined in the DataSchema section of [[!WOT-TD]].
One property of all DataSchema dictionary is the type
property whose value is from a set of enumerated strings defined in the DataSchema section of [[!WOT-TD]] and is referred as DataType in this specification.
Based on type
, the following sub-classes of DataSchema are defined in [[!WOT-TD]]: BooleanSchema, NumberSchema, IntegerSchema, StringSchema, ObjectSchema, ArraySchema.
Security metadata is represented as dictionary objects whose properties and sub-classes are defined in the SecurityScheme section of [[!WOT-TD]].
One property of the SecurityScheme dictionary is the scheme
property whose value is from a set of enumerated strings defined in the SecurityScheme section of [[!WOT-TD]]. Based on type
, multiple subclasses of SecurityScheme are defined.
Represents a Web Link with properties defined in the Link section of [[!WOT-TD]].
Represents metadata describing service details, with properties defined in the Form section of [[!WOT-TD]].
InteractionFragment
dictionaryRepresents the common properties of WoT Interactions, one of Property, Action or Event, as defined in the InteractionPattern section of [[!WOT-TD]]. Its subclasses are referred as PropertyFragment, ActionFragment and EventFragment.
Represents the Property interaction data that initializes a ThingProperty object. Its properties are defined in the Property and InteractionPattern sections of [[!WOT-TD]].
Represents the Action interaction data that initializes a ThingAction object. Its properties are defined in the Action and InteractionPattern sections of [[!WOT-TD]].
Represents the Event interaction data that initializes a ThingEvent object. Its properties are defined in the Event section of [[!WOT-TD]].
The ThingFragment dictionary is defined as Thing in [[!WOT-TD]]. It is a dictionary that contains properties representing semantic metadata and interactions (Properties, Actions and Events). It is used for initializing an internal representation of a Thing Description and its properties may be used in ThingFilter.
Serialized representation of the Thing Description (a JSON-LD document).
In this version of the API, Thing Descriptions are represented as an opaque USVString
that can be transmitted between devices.
The data types and structures imported from [[!WOT-TD]] are extended by this specification in order to provide the interfaces for WoT Interactions.
Every Thing describes its metadata as defined in ThingFragment, and basic interactions defined as Properties, Actions and Events. The following interfaces are used for representing these interactions.
The Interaction interface is an abstract class to represent Thing interactions: Properties, Actions and Events.
The InteractionFragment dictionary holds the common properties of PropertyFragment, ActionFragment and EventFragment dictionaries used for initializing ThingProperty, ThingAction and ThingEvent objects in a ThingFragment dictionary used for creating an ExposedThing object.
interface Interaction { readonly attribute (Form or FrozenArray<Form>) forms; }; Interaction includes InteractionFragment;
The forms read-only property represents the protocol bindings initialization data and is initialized by the WoT Runtime.
The ThingProperty interface is used in ConsumedThing and ExposedThing objects to represent Thing Property interactions.
The PropertyFragment dictionary is used for initializing Property objects in a ThingFragment dictionary used for creating an ExposedThing object. It MUST implement one of the DataSchema dictionaries.
interface ThingProperty: Interaction { // getter for PropertyFragment properties getter any(DOMString name); // get and set interface for the Property Promise<any> read(); Promise<void> write(any value); }; ThingProperty includes PropertyFragment; ThingProperty includes Observable;
The ThingProperty interface contains all the properties defined on PropertyFragment as read-only properties.
The type read-only property represents the type definition for the Property as a DataSchema dictionary object.
The writable read-only property tells whether the Property value can be updated. If it is false
, then the set(value)
method SHOULD always reject.
The observable read-only property tells whether the Property supports subscribing to value change notifications. If it is false
, then the subscribe()
method SHOULD always fail.
The constant read-only property - defined in DataSchema - tells whether the Property value is a constant. If true
, the set()
and subscribe()
methods SHOULD always fail.
The required read-only property - defined in DataSchema - tells whether the Property should be always present on the ExposedThing object.
The read() method will fetch the value of the Property. Returns a Promise that resolves with the value, or rejects with an error.
The write() method will attempt to set the value of the Propertyspecified in the value
argument whose type SHOULD match the one specified by the type
property. Returns a Promise that resolves on success, or rejects on an error.
interface ThingAction: Interaction { Promise<any> invoke(optional any inputValue); }; ThingAction includes ActionFragment;
The invoke() method when invoked, starts the Action interaction with the input value provided by the inputValue argument. If inputValue is null
, the action does not take any arguments and rejects if any arguments are provided. If the value is undefined
, the action will ignore any arguments provided. Otherwise the type of inputValue SHOULD match the DataSchema definition in the input
property. Returns a Promise that will reject with an error or will resolve with a value of type defined by the output
property.
interface ThingEvent: Interaction { }; ThingEvent includes EventFragment; ThingEvent includes ThingProperty;
Since ThingEvent implements Observable through the ThingProperty interface, event subscription is done by invoking the subscribe()
method on the event object that returns a cancelable Subscription.
interface ExposedEvent: ThingEvent { void emit(any payload); };
Emits an event that carries data specified by the payload
argument.
The value-matching algorithm is applied to a value input in relation to a valueType property of type DataSchema, for instance the value
and type
properties of a PropertyFragment object, or the inputValue parameter to the invoke()
method of a ThingAction object in relation to the same object. It executes the following steps:
false
.
"null"
: if value is null
, return true
, otherwise return false
.
"boolean"
: if value is either true
or false
, then return true
, otherwise return false
.
"integer"
: if value is not an integer type defined by the underlying platform (such as long
or long long
), then return false
, otherwise execute the following sub-steps:
true
.
"number"
, if value is not an integer or floating point type defined by the underlying platform (such as long
or long long
or double
), then return false
, otherwise otherwise execute the following sub-steps:
true
.
"string"
: if value is not a string type defined by the underlying platform, then return false
, otherwise return true
. In this case the algorithm expects a third parameter valueType.enum and runs the following sub-steps:
"array"
, execute the following sub-steps:
false
.
undefined
, return false
.
null
, return true
(i.e. any type is accepted as array element, including heterogenous arrays).
false
, then return false
.
true
.
"object"
, execute the following sub-steps:
Object
, return false
.
false
.
null
, return true
(i.e. accept any object value).
false
.
true
.
Observables are proposed to be included in ECMAScript and are used for handling pushed data associated with various possible sources, for instance events, timers, streams, etc. A minimal required implementation is described here.
This section is informal and contains rather laconic information for implementations on what to support for interoperability.
interface Observable { Subscription subscribe(EventHandler handler, optional ErrorHandler errorHandler, optional OnComplete onComplete); }; interface Subscription { void unsubscribe(); readonly attribute boolean closed; }; callback EventHandler = void (any value); callback ErrorHandler = void (Error error); callback OnComplete = void ();
The following callbacks can be provided when subscribing to an Observable:
value
argument.
value
argument. It is called when an error occured in producing the data the client should know about.
Contains the closed property of type boolean
that tells if the subscription is closed or active.
Also, contains the unsubscribe() method that cancels the subscription, i.e. makes a request to the underlying platform to stop receiving data from the source, and sets the closed
property to false
.
The Observable interface enabled subscribing to pushed data notifications by the subscribe() method:
In general the security measures taken to protect a WoT system will depend on the threats and attackers that system may face and the value of the assets needs to protect. 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-CONSIDERATIONS]]. This section discusses only security and privacy risks and possible mitigations directly relevant to the scripts and WoT Scripting API.
When designing new devices and services for use with the WoT, we have documented a suggested set of best practices in [[!WOT-SECURITY-BEST-PRACTICES]] to improve security. This best-practices document may be updated as security measures evolve. Following these practices does not guarantee security, but it at least will help to avoid common known vulnerabilities and pitfalls.
This section contains specific risks relevant for script developers.
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.
If a script performs a heavy functional processing on received requests before the request is authenticated, it presents a great risk for Denial-Of-Service (DOS) attacks.
During the lifetime of a WoT network, a content of a TD can change. This includes its identifier, id, which may not be an immutable one and updated periodically.
While stale TDs can present a potential problem for WoT network operation, it might not be a security risk.
This section contains specific risks relevant for the WoT Runtime itself. While the following risks are strictly speaking out of the scope for the WoT Scripting API, a script needs a secure runtime to execute and therefore we present these risks and recommended mitigations here.
In basic WoT setups, all scripts running inside the WoT runtime are considered trusted, and therefore there is no strong need to perform strict isolation between each running script instance. However, depending on device capabilities and deployment use case scenario risk level it might be desirable to do so. For example, if one script handles sensitive privacy-related data and well-audited, it might be desirable to separate it from the rest of the script instances to minimize the risk of data exposure in case some other script inside WoT gets compromised during runtime. Another example is mutual co-existence of different tenants on a single WoT device. In this case each WoT runtime instance will be hosting a different tenant, and isolation between them is required.
In case a script is compromised or misbehaving, the underlying physical device (and potentially surrounded environment) can be damaged if a script can use directly exposed native device interfaces. If such interfaces lack safety checks on their inputs, they might bring the underlying physical device (or environment) to an unsafe state (i.e. device overheats and explodes).
If the WoT runtime supports post-manufacturing provisioning or updates of scripts, WoT runtime or any related data (including security credentials), it can be a major attack vector. An attacker can try to modify any above described element during the update or provisioning process or simply provision attacker's code and data directly.
Typically the WoT 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.
The generic WoT terminology is defined in [[!WOT-ARCHITECTURE]]: Thing, Thing Description (in short TD), Web of Things (in short WoT), WoT Interface, Protocol Bindings, WoT Runtime, Consuming a Thing Description, Thing Directory, WoT Interactions, Property, Action, Event etc.
JSON-LD is defined in [[!JSON-LD]] as a JSON document that is augmented with support for Linked Data.
The terms URL and URL path are defined in [[!URL]].
The following terms are defined in [[!HTML5]] and are used in the context of browser implementations: browsing context, top-level browsing context, global object, incumbent settings object, Document, document base URL, Window, WindowProxy, origin, ASCII serialized origin, executing algorithms in parallel, queue a task, task source, iframe, valid MIME type.
A browsing context refers to the environment in which
Document objects are presented to the user. A given
browsing context has a single WindowProxy
object,
but it can have many Document
objects, with their associated
Window
objects. The script execution context
associated with the browsing context identifies the entity which
invokes this API, which can be a web app, a web page, or an
iframe.
The term secure context is defined in [[!WEBAPPSEC]].
Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError , script execution context, Promise, JSON, JSON.stringify and JSON.parse are defined in [[!ECMASCRIPT]].
DOMString, USVString, ArrayBuffer, BufferSource and any are defined in [[!WEBIDL]].
The algorithms utf-8 encode, and utf-8 decode are defined in [[!ENCODING]].
IANA media types (formerly known as MIME types) are defined in RFC2046.
The terms hyperlink reference and relation type are defined in [[!HTML5]] and RFC8288.
This document defines conformance criteria that apply to a single product: the UA (user agent) that implements the interfaces it contains.
This specification can be used for implementing the WoT Scripting API in multiple programming languages. The interface definitions are specified in [[!WEBIDL]].
The user agent (UA) may be implemented in the browser, or in a separate runtime environment, such as Node.js or 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]].
This document serves a general description of the WoT Scripting API. Language and runtime specific issues are discussed in separate extensions of this document.
The following is a list of major changes to the document. For a complete list of changes, see the github change log. You can also view the recently closed issues.
The following problems are being discussed and need most attention:
Special thanks to former editor Johannes Hund (until August 2017, when at Siemens AG) for developing this specification. Also, the editors would like to thank Dave Raggett, Matthias Kovatsch, Michael Koster and Michael McCool for their comments and guidance.