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