The WoT Profile Specification defines a Profiling Mechanism and a WoT Core Profile, which enables out of the box interoperability among things and devices. Out of the box interoperability implies, that devices can be integrated into various application scenarios without deep level adaptations. Typically only minor configuration operations are necessary (such as entering a network key, or IP address) to use the device in a certain scenario. These actions can be done by anyone without specific training.
The WoT Core Profile defines a set of constraints and rules, which compliant thing descriptions have to adopt to guarantee interoperability.These rules are prescriptive, to ensure that compliant implementations satisfy the semantic guarantees implied by them. We call this set of rules a Profile.
The WoT Profile Specification as defined in this document serves two purposes:
This document incudes a binding of the core data model to HTTP(S) and selected notification sub-protocols. The core data model can be bound to other protocols - it is expected that bindings to other protocols (e.g. MQTT, CoAP) will be defined in the near future.
A TD that is compliant to the core profile MUST adhere to both the constraints on the data model and the protocol binding.
Devices that constrain their use of the Thing Description to the WoT Core Profile can interoperate with each other out-of-the-box.
Note that the core profile is not exclusive. Device implementers are free to adopt
other features of the thing description that go beyond the constraints
of the core profile, however the interoperability guarantees of the core profile
hold only for the WoT Core Profile subset.
Motivation for a Profile
The W3C WoT Thing Architecture [[wot-architecture11]] and WoT Thing Description [[wot-thing-description11]] define a powerful description mechanism and a format to describe myriads of very different devices, which may be connected over various protocols. The format is very flexible and open and puts very few normative requirements on devices that implement it.
However, this flexibility de-facto prevents interoperability, since, without additional rules, it allows implementers to make many choices that do not provide guarantees of common behavior between implementations.
The W3C WoT Architecture [[wot-architecture11]] and the WoT Thing Description [[wot-thing-description11]] have been developed as a versatile format, that allows describing the interactions between multiple devices and protocols.
This flexibility permits an easy integration of new device types and protocols, however it risks interoperability, since there are no guarantees that two devices which are formally spec-compliant, will be able to communicate.
To increase adoption of the WoT specifications, interoperability between on premise devices, edge devices and the cloud is essential. Even if every manufacturer is implementing the current Thing Description specification in full flexibility, there is no interoperability guarantee; many choices are still left to the implementations and there are very few normative requirements that a device has to fulfill.
A Thing Description can be used in two fundamentally different deployment scenarios:
For green field deployments, where the implementations are being carried out and corresponding thing descriptions are being created, it is easier to achieve full interoperability by using a small, extensible Core Profile.
In the brown field area, due to the nature of existing deployments and protocols, a broad spectrum of variations and potentially high complexity of thing descriptions inhibits interoperability and will most likely lead to additional profiles of the WoT Thing Description and domain-specific thing consumer implementations.
The WoT Core Profile can be used by green field deployments and gives guidance to new implementers of the WoT specifications. It has already proved in brown-field scenarios in the PlugFests, where existing devices, that already existed as products, prototypes or demonstrators, were described with Thing Descriptions that are constrained to the Core Profile.
A set of over 30 use cases for the Web of Things were contributed by stakeholders from multiple industries for various application domains. These have been published in the WoT Use Cases and Requirements document [[wot-usecases]].
Based on these use cases a set of requirements have been derived which drive the development of the W3C Web of Things specification family. Several of these domains require easy integration of devices from multiple vendors, in other words, out-of-the-box interoperability. However, the descriptive approach taken by the WoT specifications generally leads to a large variety of different protocols and data formats, which can work against out-of-the box interoperability.
For example, a WoT Thing Description (TD) can in theory include a description based on a networking protocol unknown to a device that wishes to connect to it. To ensure interoperability without additional customization (e.g. by writing software or performing complex setup or configuration steps), the range of such choices needs to be limited to a finite set so that a consumer of a Thing Description can be sure it will be able to interact with any possible Thing. A finite set of customization choices is also important for implementing devices with a fixed code base. Defining such constraints leads to the profile mechanism and the WoT Core profile.
In addition to multiple vertical use cases that will use HTTP(S) for their implementations, there are horizontal use cases that are addressed by this profile specification. The primary focus is to enable Multi-Vendor system integration with out of the box interoperability:
During the recent WoT PlugFests there were many de-facto agreements on the use of a small constrained subset of interaction patterns and protocol choices. These de-facto agreements select a common subset of the WoT Thing Description, based on proven interoperability among manufacturers.
The aim of this specification is to formalize these agreements by defining a WoT Core Profile based on the choices that were made by the implementers of PlugFest devices.
The WoT Core Profile contains additional normative requirements that MUST be satisfied by devices to be compliant to the profile.
Adoption of the WoT Core Profile will significantly limit the implementation burden of device and cloud implementors.
The WoT Core Profile was defined with the following main goals:
It makes choices on the required metadata fields as well as the supported interactions and protocol endpoints. It introduces some constraints on data schemas for properties and actions which are required for resource constrained devices in real-world deployments. The format does not forbid the use of additional elements of the WoT Thing Description for vendor specific extensions, however this will impact interoperability.
Devices, which implement the Core Profile, are out-of-the-box interoperable with other Core Profile compliant devices. Furthermore, the Core Profile simplifies device validation and compliance testing since a corresponding conformance test suite can be defined.
The following classification adopts the terminology as described in the "H2020 – CREATE-IoT Project - Recommendations for commonalities and interoperability profiles of IoT platforms" [[?H2020-CREATE-IoT]] report. The definitions below have been adapted to reflect the scope of the WoT profile.
Technical Interoperability is usually associated with communication protocols and the infrastructure needed for those protocols to operate. This implies agreeing on a common protocol (e.g. HTTP / TCP/IP) and providing additional clarifications, where required.
Syntactic Interoperability is usually associated with data formats and encodings along with techniques for compressing them. Examples for these formats and encodings in the WoT are JSON, XML, JSON-LD, UTF-8 payloads.
Semantic Interoperability is associated with a common understanding of the behavior of communication partners. In the profile context, it includes a common interpretation of (synchronous and asynchronous) action semantics, a common event model, how to set/get multiple properties, writable properties, a common error model and error messages.
Domain specific ontologies, e.g. semantic interop of automotive and medical devices exceed the scope of the profile.
Organisational Interoperability in the profile context implies that any consumer, which conforms with a given profile can interact with any Thing which conforms with the same profile, without additional customization.
Organisational Interoperability also requires commonly agreed approaches to security, trust and privacy, i.e. a consumer is provided access to Things only, when these common terms and conditions are applied.
Devices created by various engineers, vendors and SDOs that satisfy the requirements of the profile spec can be integrated with compliant consumers without additional customization. This works across infrastructures, regions and cultures.
A device or consumer implementation complies with this specification if it follows the normative statements in the present document.
A JSON Schema [[?JSON-SCHEMA]] to validate the compliance of a Thing Description with the core profile is provided in Appendix .
The fundamental WoT terminology such as Thing, Consumer, Thing Description (TD), WoT Thing Description, Partial TD, Thing Model (TM), Interaction Model, Interaction Affordance, Property, Action, Event, Protocol Binding, Servient, Vocabulary, Term, Vocabulary Term, WoT Interface, and WoT Runtime are defined in Section 3 of the WoT Architecture specification [[?WOT-ARCHITECTURE]].
For convenience of the reader, we use the terms keyword and field for the linguistic notion vocabulary term as defined in the Thing Description Specification.
We use the terms device and thing in an interchangeable manner.
The following requirements were identified by the WoT profile task force and are addressed by the Profile 1.0 specification.
This is the most important objective of the profile. A TD Consumer satisfying the requirements of a profile should be able to process any TD also satisfying the profile and should be able to correctly interact with all affordances of the Thing such a TD describes.
This implies that a profile has three parts:Complexity addresses at least the follwing two things to simplify the development and reduce the implementation effort:
Get rid of ambiguities, i.e. clarify specifications to define interpretation of a TD and behavior of the thing and consumer.
Examples are the choice of properties vs. actions, use of PUT or POST for HTTP, observe protocols.
A profile should help define what needs to be implemented. This requirement also includes behavioral goals and recommendations about best practice for the implementation of Consumers and Things.
The mechanism used to indicate that a TD satisfies a profile should be general enough to indicate the TD satisfies the requirements for multiple profiles.
It should be possible to combine multiple profiles both for production and consumption:
It should be possible to indicate that a consumer can ingest TDs that satisfy one or more profiles, even if each TDs individually only satisfies one profile. For example, a Smart Building may need to use both "constrained" devices and "unconstrained" devices. A gateway consuming TDs should be able to ingest TDs designed for both the constrained and unconstrained contexts.
A Thing that satisfies all the requirements for multiple TDs (for example, a device using protocols common to two different usage contexts) should be able to indicate that.
Whether or not a TD satisfies the requirements of a given profile should be verifiable with automated tools. We can use the existing TD JSDON Schema as a basis and reuse the existing tooling (TD-playground)
There should be a mechanism to identify which profiles a TD satisfies. This mechanism should be intrinsic to a TD, i.e. be in-band.
A profile should limit the number of options, for example the set of possible protocols, to a finite set, so that a consumer can consume any TD in a given profile with a finite and static code base.
In order to conform with a profile, a Web Thing MUST conform with all the normative statements in the profile's specification.
In order to denote that a given
Web Thing
conforms to one or more profiles, its Thing Description MUST include a
profile
member [[wot-thing-description11]]. The value of
the profile
member MUST be set to either a valid URI
[[RFC3986]] identifying a single profile, or an array of valid URIs
identifying multiple profiles.
{ "@context": "https://www.w3.org/2019/wot/td/v1", "id": "urn:dev:ops:32473-WoTLamp-1234", "profile": "https://www.w3.org/2022/wot/profile/http-baseline/v1", "title": "My Lamp", "description": "A web connected lamp", ... }
{ "@context": "https://www.w3.org/2019/wot/td/v1", "id": "urn:dev:ops:32473-WoTLamp-1234", "profile": [ "https://www.w3.org/2022/wot/profile/http-baseline/v1", "https://www.w3.org/2022/wot/profile/http-sse/v1" ], "title": "My Lamp", "description": "A web connected lamp", ... }
The HTTP Profile specificiation defines several profiles that can be used for dedicated use cases.
The following sections are applicable for all profiles defined by this document.
Below is a list of security schemes [[wot-thing-description]] which conformant Web Things MAY use:
Conformant Consumers MUST support all of these security schemes.
The list of security schemes to include in the HTTP Baseline Profile is still under discussion.
A Thing MAY implement multiple security schemes.
Security schemes MUST be applied using the top level security
member of a
Thing.
This section defines the HTTP Baseline Profile, which includes a Data Model and a Protocol Binding for reading and writing properties and invoking, querying and cancelling actions.
This profile may be used in conjunction with the HTTP SSE Profile or the HTTP Webhook Profile in order to provide operations for observing properties and listening for events.
In order to denote that a given
Web Thing
conforms to the HTTP Baseline Profile, its Thing Description MUST have a
profile
member [[wot-thing-description]] with a value
of https://www.w3.org/2022/wot/profile/http-baseline/v1
.
The information model of the HTTP Profile incorporates the data model defined by chapter 5 of the Thing Description specification. The normative rules defined by that model are the baseline for the definition of the HTTP Profile data model and are normative for the information model of the profiles defined in this document. A compliant implementation MUST additionally satisfy the requirements of this chapter.
The following rules are applicable to multiple classes of the WoT Thing Description Specification, as they provide clearer semantics, improved readability and simplified processing on resource constrained devices.
One of the benefits of the WoT Thing Description over a typical IoT format is the possibility to automatically generate user interfaces that can be used by humans without reading out of band documentation.
Therefore it is strongly RECOMMENDED to use
descriptive values for the fields
title
titles
description
desvriptions
for Things, Property Affordances,
Action Affordances, Event Affordances and Data
Schemas.
It is possible to have empty values for these fields, if, for specific purposes it is not desired to provide documentation, however this is NOT RECOMMENDED and the conscious decision is obvious from the TD.
All date and time values MUST use the canonical dateTime representation format defined in section 3.2.7 of [xmlschema-2]. As described by this section the following constraints must be observed: All dateTime values MUST use UTC as the time zone and use the 'Z' identifier. A time value of 24:00 is not permitted; the value 00:00 MUST be used instead.
The length of
id
,
description
and
descriptions
values is limited to 512 characters.
The length of
title
and
titles
values is limited to 64 characters.
Where a type permits using an
TODO: decide if multiple types and contexts are required.
In this case the following section could be added:
The only exception to this rule are array of string
or a
string
, an
array of string
MUST be used.
@context
and @type
annotations,
where both string
or array of string
MAY be used.
Where a type permits using an
array of DataSchema
or a
DataSchema
, an
array of DataSchema
MUST be used.
All elements of an
enum
MUST be either
string
or
number
.
Different types in a single
enum
MUST NOT be used.
To provide minimum interoperability, the following metadata fields of a Thing MUST be contained in a compliant Thing Description:
keyword | type | remarks |
---|---|---|
title | string | human readable documentation |
id | urn_type | a globally unique urn of the thing |
description | string | human readable documentation |
created | date | human readable documentation |
modified | date | human readable documentation |
support | urn_type | human readable documentation |
security | array of string | simplified handling |
version | VersionInfo | clear versioning, easy to compare different TDs |
It is RECOMMENDED to use the value "" for strings, where the value cannot be determined.
If a Thing Description is used solely within a company, the email address of the developer SHOULD be used in the support field, if the Thing Description is provided externally, a support email address SHOULD be used.
It will be evaluated whether the profile also recommends some new TD terms that may be introduced in TD 1.1. Currently the following terms are discussed: serialNumber, hardwareRevision, softwareRevision, loc_latitude, loc_longitude loc_altitude, loc_height, and loc_depth. If these, or some of them, are defined in the TD 1.1 model, they may be recommended here in one of the next draft updates.
Data Schemas are used for the values of Properties, Action input and output parameters and Event message payloads. The value of a Data Schema can be a simple type (boolean, integer, number, string) or an instance of a structured type (array and object).
The Core Data Model applies the following constraints and rules to theDataSchema
class of section 5.3.2.1 of the WoT Thing Description
Specification.
This section defines a subset of the class
DataSchema
that can be processed on resource-constrained
devices.
The Core Data Model restricts the use of
arrays and objects to the top level
of Data Schemas, i.e. only a one-level hierarchy is
permitted.
The members of a top level
object
or
array
MUST NOT be array or object types.
This may appear as a severe limitation, however it is motivated by integrating with multiple cloud services. Many enterprise services and applications are based on (relational) databases, where individual property values are stored. Of course databases can also store objects (e.g. encoded as a JSON string), however this will prevent processing by other enterprise applications.
If a property conceptually has a deeper structure, such as grid of lamps with RGB colors, the structure can be represented in the keyword of the property, i.e. lamp1_color_r, lamp1_color_g and lamp1_color_b. A similar mapping can be done for arrays and hierarchical objects. This constraint leads to simpler Thing Descriptions that can be handled by very limited devices.
The following fields MUST be contained in a DataSchema:
keyword | type | constraints |
---|---|---|
description | human readable description | |
type | string | one of boolean, integer, number, string, array or object |
The values
object
,
array
MAY only be used at the top level of a Data Schema.
The type value MUST NOT be null
.
PropertyAffordance
class of section 5.3.1.3 of the WoT Thing Description Specification.
The following property fields MUST be contained
in the
properties
element of a Profile compliant TD:
keyword | type | constraints |
---|---|---|
title | string | unique name among all properties |
description | string | human readable description |
type | string |
one of boolean ,
string ,
number , integer
, object or
array . The type value
null MUST NOT be used.
|
The Thing Description permits arbitrary
object depths for properties. Parsing of a
deeply nested structure is not possible on
resource constrained devices.
Therefore each
property MUST NOT exceed a maximum depth
of 5 levels of nested
array
or
object
elements.
It is RECOMMENDED to keep the nesting
of
array
or
object
elements below 4.
The following additional constraints MUST be applied to the Property Affordances of a Thing Description conforming to the Core Profile:
keyword | type | constraint |
---|---|---|
const | anyType | MUST NOT be used |
enum | array of simple type | Values of enums MAY only be simple types. Handling of any type is too complex to implement on resource constrained devices |
forms | array of Forms |
The Array of Form
of each property MUST contain only a
single endpoint for each
operation readproperty
, writeproperty , observeproperty
, unobserveproperty .
|
format | string |
If the field format
is used, formats defined in
section 7.3.1-7.3.6 of
[[JSON-SCHEMA]] MUST be used.
|
oneOf | string |
The DataSchema field oneOf
does not make sense for properties
and MUST NOT be used.
|
uriVariables | Map of DataSchema |
uriVariables MUST
NOT be used.
|
It is highly RECOMMENDED to always specify a
unit
if a value has a metric.
Authors of Thing Descriptions should be aware that units
that are common in their geographic region are
not globally applicable and may lead to
misinterpretation with drastic consequences.
The field
unit
could be used for non-decimal numeric types as
well, e.g. a string value with binary or hex
data (
0xCAFEBABE
,
0b01000010
), where the unit is
hex
or
bin
,
to indicate how the value should be
interpreted.
It is strongly RECOMMENDED to use the values
hex
,
oct
or
bin
in the unit
for string values with binary or hex data to achieve interoperability.
ActionAffordance
class of section 5.3.1.4 of the WoT Thing Description Specification.
The following fields MUST be contained in an action element of an Core Profile compliant TD:
keyword | type | constraints |
---|---|---|
title | string | unique name among all actions |
input | array of DataSchema | schemas describing the input to an action |
output | array of DataSchema | schemas describing the output of an action |
input
field,
all elements of the subclasses
objectSchema and dataSchema MUST
only contain simple types.
In the output
field,
all elements of the subclasses
objectSchema and dataSchema MUST
only contain simple types.
The elements of the DataSchema subclasses
ArraySchema and ObjectSchema for the fields
input
and
output
are restricted to simple types in a Thing
Description conforming to the Core
Data Model. Without this limitation a higher
implementation burden would be put on resource
constrained devices (arbitrary cascaded arrays
and multi-level objects) which cannot be
satisfied by all consuming devices.
The following additional constraints MUST be applied to the indicated fields of the Interaction Affordances of a Thing Description conforming to the Core Data Model:
forms
field,
the Array of Form
of each action MUST contain only a single
endpoint.
format
is used, formats defined in
section 7.3.1-7.3.6 of
[[JSON-SCHEMA]] MUST be used.
oneOf
does not make sense for properties
and MUST NOT be used.
uriVariables
field MUST NOT be used.
TODO:
- no optional parameters
- timeout
EventAffordance
class of section 5.3.1.5 of the WoT Thing Description Specification.
A Thing MAY provide more than one event mechanism to enable a variety of consumers.
TODO:
The events section needs to be signifcantly extended and define addtional constraints to ensure OOTBI. LongPoll, WebSockets and WebHooks can be considered as initial candidates for supported protocols for the event mechanism to identify appropriate data model constraints.
The individual protocol constraints need to be defined in a respective protocol binding chapter after they have been identified/evaluated in plugfests.
The following fields MUST be present in an event element of a Core TD:
keyword | type | constraints |
---|---|---|
title | string | unique name among all events |
description | string | human readable description |
data | set of DataSchema instances in a JSON object | only the DataSchema subclasses booleanSchema, IntegerSchema, NumberSchema, StringSchema are permitted |
The following additional constraints MUST be applied to the Event Affordances of a WoT Thing Description conforming to the profile:
keyword | type | constraint |
---|---|---|
forms | array of Forms |
The Array of Form
of each event MUST contain only a single
endpoint.
|
uriVariables | Map of DataSchema |
uriVariables MUST
NOT be used.
|
A Thing MAY provide more than one event mechanism to enable a variety of consumers.
The following fields MUST be present in a form element of a Core TD:
keyword | type | constraints |
---|---|---|
title | string | unique name among all events |
description | string | human readable description |
data | set of DataSchema instances in a JSON object | only the DataSchema subclasses BooleanSchema, IntegerSchema, NumberSchema, StringSchema are permitted |
The following additional constraints MUST be applied to the Form elements of a WoT Thing Description conforming to the Core profile:
keyword | type | constraint |
---|---|---|
security | string or Array of string |
A security field at the form
level MUST NOT be used.
|
scopes | string or Array of string |
A scopes field MUST NOT be
used.
|
The "type" relationship as defined in chapter 6 of [[RFC6903]] is reserved for indicating an instance relationship between a thing and a thing template. The Core Data Model does not put additional constraints or requirements on links. The interpretation of a link is out of scope.
This section defines a protocol binding which describes how a Consumer communicates with a Web Thing [[wot-architecture11]] using JSON [[JSON]] payloads over the HTTP [[HTTP11]] protocol.
A Consumer or Web Thing conforming to the HTTP Baseline Profile MUST implement this protocol binding.
The examples provided throughout this section describe how a Consumer would communicate with a Web Thing which produces the following Thing Description:
{ "@context": "https://www.w3.org/2019/wot/td/v1", "id": "https://mywebthingserver.com/things/lamp", "profile": "https://www.w3.org/2022/wot/profile/http-baseline/v1", "base": "https://mywebthingserver.com/things/lamp/", "title": "My Lamp", "description": "A web connected lamp", "securityDefinitions": { "oauth2": { "scheme": "oauth2", "flow": "code", "authorization": "https://mywebthingserver.com/oauth/authorize", "token": "https://mywebthingserver.com/oauth/token" } }, "security": "oauth2", "properties": { "on": { "type": "boolean", "title": "On/Off", "description": "Whether the lamp is turned on", "forms": [{"href": "properties/on"}] }, "level" : { "type": "integer", "title": "Brightness", "description": "The level of light from 0-100", "unit": "percent", "minimum" : 0, "maximum" : 100, "forms": [{"href": "properties/level"}] } }, "actions": { "fade": { "title": "Fade", "description": "Fade the lamp to a given level", "input": { "type": "object", "properties": { "level": { "type": "integer", "minimum": 0, "maximum": 100, "unit": "percent" }, "duration": { "type": "integer", "minimum": 0, "unit": "milliseconds" } } }, "forms": [{"href": "actions/fade"}] } }, "forms": [ { "op": ["readallproperties", "writemultipleproperties"], "href": "properties" }, { "op": "queryallactions", "href": "actions" } ] }
readproperty
The URL of a Property
resource to be used when reading
the value of a property MUST be obtained from a Thing Description by
locating a
Form
inside the corresponding
PropertyAffordance
for which:
op
member
contains the value readproperty
.
href
member is http
or https
The resolved value of the href
member MUST then be used
as the URL of the Property
resource.
In order to read the value of a property, a Consumer MUST send an HTTP request to a Web Thing with:
GET
Property
resourceAccept
header set to application/json
GET /things/lamp/properties/on HTTP/1.1 Host: mythingserver.com Accept: application/json
If a Web Thing receives an HTTP request following the format above and the Consumer has permission to read the corresponding property, then upon successfully reading the value of the property it MUST send an HTTP response with:
200
Content-Type
header set to application/json
HTTP/1.1 200 OK Content-Type: application/json false
writeproperty
The URL of a Property
resource to be used when writing
the value of a property MUST be obtained from a Thing Description by
locating a
Form
inside the corresponding
PropertyAffordance
for which:
op
member
contains the value writeproperty
href
member is http
or https
The resolved value of the href
member MUST then be used
as the URL of the Property
resource.
In order to write the value of a property, a Consumer MUST send an HTTP request to a Web Thing with:
PUT
Property
resourceContent-Type
header set to application/json
PUT /things/lamp/properties/on HTTP/1.1 Host: mythingserver.com Content-Type: application/json true
If a Web Thing receives an HTTP request following the format above and the Consumer has permission to write the corresponding property, then upon successfully writing the value of the property it MUST send an HTTP response with:
204
HTTP/1.1 204 No Content
readallproperties
The URL of a Properties
resource to be used when
reading the value of all properties at once MUST be obtained from a
Thing Description by locating a
Form
inside the top level
forms
member
for which:
op
member contains the value
readallproperties
href
member is http
or https
The resolved value of the href
member MUST then be used
as the URL of the Properties
resource.
In order to read the value of all properties, a Consumer MUST send an HTTP request to a Web Thing with:
GET
Properties
resource
Accept
header set to application/json
GET /things/lamp/properties HTTP/1.1 Host: mythingserver.com Accept: application/json
If a Web Thing receives an HTTP request following the format above, then upon successfully reading the values of all the readable properties to which the Consumer has permission to access, it MUST send an HTTP response with:
200
Content-Type
header set to application/json
HTTP/1.1 200 OK Content-Type: application/json { "on": false, "level": 100 }
writemultipleproperties
The URL of a Properties
resource to be used when
writing the value of multiple properties at once MUST be obtained
from a Thing Description by locating a
Form
inside the top level
forms
member
for which:
op
member contains the value
writemultipleproperties
href
member is http
or https
The resolved value of the href
member MUST then be used
as the URL of the Properties
resource.
In order to write the value of multiple properties at once, a Consumer MUST send an HTTP request to a Web Thing with:
PUT
Properties
resource
Content-Type
header set to application/json
PUT /things/lamp/properties HTTP/1.1 Host: mythingserver.com Content-Type: application/json { "on": true, "level": 50 }
If a Web Thing receives an HTTP request following the format above, then upon successfully writing the values of the requested writable properties it MUST send an HTTP response with:
204
HTTP/1.1 204 No Content
The readmultipleproperties
operation is currently excluded due to
the complexities of the request payload format and because it
doesn't add much functionality over readproperty
and
readallproperties
.
writeallproperties
is currently excluded because it
is just a special case of writemultipleproperties
.
invokeaction
The URL of an Action
resource to be used when invoking
an action MUST be obtained from a Thing Description by locating a
Form
inside the corresponding
ActionAffordance
for which:
op
member is invokeaction
href
member is http
or https
The resolved value of the href
member MUST then be used
as the URL of the Action
resource.
In order to invoke an action on a Web Thing, a Consumer MUST send an HTTP request to the Web Thing with:
POST
Action
resourceAccept
header set to application/json
Content-Type
header set to application/json
POST /things/lamp/actions/fade HTTP/1.1 Host: mythingserver.com Content-Type: application/json Accept: application/json { "level": 100, "duration": 5 }
If a Web Thing receives an HTTP request following the format above then it MUST respond with one of three response formats:
For long-running actions which are not expected to finish executing
within the timeout period of an HTTP request (e.g. 30 to 120
seconds), it is RECOMMENDED that a Web Thing respond with an
Asynchronous Action Response so that a Consumer may continue to
monitor the status of an action request with a
queryaction
operation on a dynamically created
ActionStatus
resource, after the initial
invokeaction
response.
For short-lived actions which are expected to finish executing within the timeout period of an HTTP request, a Web Thing MAY wait until the action has completed to send a Synchronous Action Response.
If a Web Thing encounters an error in attempting to execute an
action before responding to the invokeaction
request,
then it MUST send an Error Response.
Conforming Consumers MUST support all three types of response
to the initial invokeaction
request.
After the initial request,
support for subsequent operations on an ActionStatus
resource is OPTIONAL.
ActionStatus
object
The status of an action invocation request is represented by an
ActionStatus
object which includes the following
members:
Member | Description | Assignment | Type |
---|---|---|---|
status |
The status of the action request. | mandatory |
string (one of pending ,
running , completed or
failed )
|
output |
The output data, if any, of a completed action which
MUST conform with the output data schema of the
corresponding
ActionAffordance .
|
optional | any type |
error |
An error message, if any, associated with a failed action
which MUST use the JSON serialization of the Problem Details
format [[RFC7807]] (only needed in response to a
queryaction
operation).
|
optional | object |
href |
The [[URL]] of an ActionStatus resource which can
be polled with a queryaction operation to get
the latest status of the action, the
URI
scheme [[RFC3986]] of which MUST resolve to
http or https (only needed for an
Asynchronous Action
Response).
|
optional | string |
timeRequested |
A timestamp indicating the time at which the Thing received the request to execute the action, in ISO format [[ISO8601-1]] (see Date format for additional constraints). | optional | string |
timeEnded |
A timestamp indicating the time at which the Thing successfully completed executing the action, or failed to execute the action, in ISO format [[ISO8601-1]] (see Date format for additional constraints). | optional | string |
It is possible that a Thing's clock may not be set to the correct
time. If timings are important then a Consumer may therefore choose
to treat the timeEnded
member of an
ActionStatus
object as being relative to the
timeRequested
member, but not necessarily as relative
to its own internal clock, or the clocks of other Things.
If providing a Synchronous Action Response, a Web Thing MUST send an HTTP response with:
200
Content-Type
header set to
application/json
ActionStatus
object serialized
in JSONHTTP/1.1 200 OK Content-Type: application/json { "status": "completed", "timeRequested": "2021-11-10T11:43:20.135Z", "timeEnded": "2021-11-10T11:43:25.135Z" }
If providing an Asynchronous Action Response, a Web Thing MUST send
an HTTP response containing the URL of an ActionStatus
resource, the
URI
scheme [[RFC3986]] of which MUST resolve to http
or https
. The response MUST have:
201
Content-Type
header set to
application/json
Location
header set to the URL of the
ActionStatus
resource
ActionStatus
object serialized
in JSON, with its href
member set to the URL
of the ActionStatus
resource
HTTP/1.1 201 CREATED Content-Type: application/json Location: /things/lamp/actions/fade/123e4567-e89b-12d3-a456-426655 { "status": "pending", "href": "/things/lamp/actions/fade/123e4567-e89b-12d3-a456-426655", "timeRequested": "2021-11-10T11:43:19.135Z" }
queryaction
A queryaction
operation is used to query the current
state of an ongoing action request.
A Web Thing which provides
Asynchronous Action Responses to an invokeaction
operation on an Action
MUST also support
queryaction
operations on
that same Action
.
A Web Thing which only provides
Synchronous Action Responses to an invokeaction
operation on an Action
SHOULD NOT support
queryaction
operations on that same
Action
.
The URL of an ActionStatus
resource to be used in a
queryaction
operation MUST be obtained from the
Location
header of an
Asynchronous Action Response, or the href
member of
the ActionStatus
object in its body.
In order to query the status of an action request, a Consumer MUST send an HTTP request to a Web Thing with:
GET
ActionStatus
resource
Accept
header set to application/json
GET /things/lamp/actions/fade/123e4567-e89b-12d3-a456-426655 Host: mythingserver.com Accept: application/json
If a Web Thing receives an HTTP request following the format
above and the Consumer has permission to query the corresponding
ActionStatus
resource, then upon successfully reading
the status of the action request it MUST send an HTTP response
with:
200
Content-Type
header set to application/json
ActionStatus
object
representing the current status of the action request, serialized
in JSONHTTP/1.1 200 OK Content-Type: application/json { "status": "running", "timeRequested": "2021-11-10T11:43:19.135Z" }
If the queried action failed to execute, then
the status
member of the ActionStatus
object
MUST be set to "failed"
.
If the queried action failed to execute, then
the error
member MAY provide additional error information
conforming to the Problem Details format [[RFC7807]].
HTTP/1.1 200 OK Content-Type: application/json { "status": "failed", "error": { "type": "https://mythingserver.com/docs/errors/invalid-level", "title": "Invalid value for level provided", "invalid-params": [ { "name": "level", "reason": "Must be a valid number between 0 and 100" } ] }, "timeRequested": "2021-11-10T11:43:19.135Z", "timeEnded": "2021-11-10T11:43:20.513Z" }
cancelaction
A cancelaction
operation is used to cancel an ongoing
Action
request.
A Web Thing which provides
Asynchronous Action Responses to an invokeaction
operation on an Action
MAY also support
cancelaction
operations on
that same Action
.
A Web Thing which only provides
Synchronous Action Responses to an invokeaction
operation on an Action
SHOULD NOT support
cancelaction
operations on that same
Action
.
class="rfc2119-assertion"
id="http-baseline-profile-protocol-binding-cancelaction-2">
The URL of an ActionStatus
resource to be used in a
cancelaction
operation MUST be obtained from the
Location
header of an
Asynchronous Action Response, or the href
member of
the ActionStatus
object in its body.
In order to cancel an action request, a Consumer MUST send an HTTP request to a Web Thing with:
DELETE
ActionStatus
resource
DELETE /things/lamp/actions/fade/123e4567-e89b-12d3-a456-426655 HTTP/1.1 Host: mythingserver.com
If a Web Thing receives an HTTP request following the format
above and the Consumer has permission to cancel the corresponding
Action
request, then upon successfully cancelling
Action
it MUST send an HTTP response with:
204
HTTP/1.1 204 No Content
queryallactions
The URL of an Actions
resource to be used when
querying the status of all ongoing action requests MUST be obtained
from a Thing Description by locating a
Form
inside the top level
forms
member
for which:
op
member contains the value
queryallactions
href
member is http
or https
The resolved value of the href
member MUST then be used
as the URL of the Actions
resource.
In order to query the status of all ongoing action requests, a Consumer MUST send an HTTP request to a Web Thing with:
GET
Actions
resource
Accept
header set to application/json
GET /things/lamp/actions HTTP/1.1 Host: mythingserver.com Accept: application/json
If a Web Thing receives an HTTP request following the format above, then upon successfully retreiving the status of all ongoing action requests to which the Consumer has permission to access, it MUST send an HTTP response with:
200
Content-Type
header set to application/json
Action
name,
with the value of each object member being an array of
ActionStatus
objects
representing the action requests, serialized in JSON.
Each array in the result object MUST be sorted in reverse chronological order such that the most recent action request appears first.
HTTP/1.1 200 OK Content-Type: application/json { "fade": [ { "status": "completed", "href": "/things/lamp/actions/fade/123e4567-e89b-12d3-a456-426655", "timeRequested": "2021-11-10T11:43:19.135Z", "timeEnded": "2021-11-10T11:43:20.513Z" }, { "status": "failed", "href": "/things/lamp/actions/fade/123e4567-e89b-12d3-a456-558329", "timeRequested": "2021-11-10T11:42:15.133Z", "timeEnded": "2021-11-10T11:42:22.524Z" }, { "status": "running", "href": "/things/lamp/actions/fade/123e4567-e89b-12d3-a457-434656", "timeRequested": "2021-11-10T11:41:53.351Z" }, { "status": "pending", "href": "/things/lamp/actions/fade/123e4567-e89b-12d3-a457-ea9519", "timeRequested": "2021-11-10T11:39:53.651Z" } ] }
Action
request is cancelled with a
cancelaction
operation, its ActionStatus
object is deleted and need not be retained. For all other
Action
requests it is assumed that a Web Thing will
store the ActionStatus
object so that its status may
later be queried with a queryaction
or
queryallactions
operation. It is not expected that
ActionStatus
objects should be retained indefinitely,
they may be stored in volatile memory and/or periodically pruned.
The length of time for which to retain ActionStatus
objects is expected to be implementation-specific and may depend on
application-specific requirements or resource constraints.
If any of the operations defined above are unsuccessful then the Web Thing MUST send an HTTP response with an HTTP error code which describes the reason for the failure.
It is RECOMMENDED that error responses use one of the following HTTP error codes:
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
500 Internal Server Error
A Web Thing MUST NOT issue any 3xx status codes. A Consumer MAY treat all 3xx codes as errors that do not change the status or behavior of the consumer.
Web Things MAY respond with other valid HTTP error codes
(e.g. 418 I'm a teapot
).
Consumers MAY interpret other valid HTTP error codes as a generic 4xx
or 5xx
error with no special defined behaviour.
TODO: If we define the finite set of error responses as above then we should also define what a Consumer should do if it receives a 3xx redirect type response.
If an HTTP error response contains a body, the content of that body MUST conform with with the Problem Details format [[RFC7807]].
This assertion has two keywords and needs to be broken into two statements. The question is how to do it without repeating the list.
Below is a list of security schemes [[wot-thing-description]] which conformant Web Things MAY use and conformant Consumers MUST support:
The list of security schemes to include in the HTTP Baseline Profile is still under discussion.
A Thing MAY implement multiple security schemes.
Security schemes MUST be applied using the top level security
member of a
Thing.
Security schemes
MUST NOT be applied to individual
Form
s.
Please see WoT Security Best Practices for implementation advice.
This section defines the HTTP SSE Profile, including a Protocol Binding for observing properties and listening for events using Server-Sent Events [[EVENTSOURCE]].
This profile may be used in conjunction with the HTTP Baseline Profile in order to provide operations to read and write properties and invoke, query and cancel actions.
In order to denote that a given
Web Thing
conforms to the HTTP SSE Profile, its Thing Description MUST have a
profile
member [[wot-thing-description]] with a value
of https://www.w3.org/2022/wot/profile/http-sse/v1
.
This section defines a protocol binding which describes how a Consumer communicates with a Web Thing [[wot-architecture11]] using Server-Sent Events [[EVENTSOURCE]].
A Consumer or Web Thing conforming to the HTTP SSE Profile MUST implement this protocol binding.
The examples provided throughout this section describe how a Consumer would communicate with a Web Thing which produces the following Thing Description:
{ "@context": "https://www.w3.org/2019/wot/td/v1", "id": "https://mywebthingserver.com/things/lamp", "profile": [ "https://www.w3.org/2022/wot/profile/http-baseline/v1", "https://www.w3.org/2022/wot/profile/http-sse/v1", ], "base": "https://mywebthingserver.com/things/lamp/", "title": "My Lamp", "description": "A web connected lamp", "securityDefinitions": { "oauth2": { "scheme": "oauth2", "flow": "code", "authorization": "https://mywebthingserver.com/oauth/authorize", "token": "https://mywebthingserver.com/oauth/token" } }, "security": "oauth2", "properties": { "on": { "type": "boolean", "title": "On/Off", "description": "Whether the lamp is turned on", "forms": [{"href": "properties/on"}] }, "level" : { "type": "integer", "title": "Brightness", "description": "The level of light from 0-100", "unit": "percent", "minimum" : 0, "maximum" : 100, "forms": [{"href": "properties/level"}] } }, "actions": { "fade": { "title": "Fade", "description": "Fade the lamp to a given level", "input": { "type": "object", "properties": { "level": { "type": "integer", "minimum": 0, "maximum": 100, "unit": "percent" }, "duration": { "type": "integer", "minimum": 0, "unit": "milliseconds" } } }, "forms": [{"href": "actions/fade"}] } }, "events": { "overheated": { "title": "Overheated", "data": { "type": "number", "unit": "degree celsius" }, "description": "The lamp has exceeded its safe operating temperature", "forms": [{ "href": "events/overheated", "subprotocol": "sse" }] } }, "forms": [ { "op": ["readallproperties", "writemultipleproperties"], "href": "properties" }, { "op": "queryallactions", "href": "actions" }, { "op": ["subscribeallevents", "unsubscribeallevents"], "href": "events", "subprotocol": "sse" } ] }
observeproperty
The URL of a Property
resource to be used when
observing the value of a property MUST be obtained from a Thing
Description by locating a
Form
inside the corresponding
PropertyAffordance
for which:
op
member contains the value
observeproperty
href
member is http
or https
subprotocol
member has a value of
sse
The resolved value of the href
member MUST then be
used as the URL of the Property
resource.
In order to observe a property, a Consumer MUST follow the
Server-Sent Events [[EVENTSOURCE]] specification to open a
connection with the Web Thing at the URL of the
Property
resource.
This involves the Consumer sending an HTTP request to the Web Thing with:
GET
Property
resourceAccept
header set to text/event-stream
Connection
header set to keep-alive
GET /things/lamp/properties/level HTTP/1.1 Host: mythingserver.com Accept: text/event-stream Connection: keep-alive
For Consumers implemented in JavaScript [[ECMASCRIPT]] and executed
in a runtime which exposes the
EventSource
interface, a Server-Sent Events
connection can be initiated using the EventSource
constructor.
const levelSource = new EventSource('/things/lamp/properties/level');
If a Web Thing receives an HTTP request following the format above and the Consumer has permission to observe the corresponding property, then it MUST follow the Server-Sent Events [[EVENTSOURCE]] specification to maintain an open connection with the Consumer and push a property value to the Consumer each time the value of the specified property changes.
This involves the Web Thing initially sending an HTTP response to the Consumer with:
200
Content-Type
header set to
text/event-stream
HTTP/1.1 200 OK Content-Type: text/event-stream
Whenever the value of the specified property changes while the Web
Thing has an open connection with a Consumer, the Web Thing MUST
send a property value to the Consumer using the event stream
format in the Server-Sent Events [[EVENTSOURCE]] specification.
For each message sent, the Web Thing MUST set the
event
field to the name of the
PropertyAffordance
and populate the data
field with the property value, serialized in JSON and following
the data schema specified in the PropertyAffordance
.
The id
field SHOULD be set to a unique
identifier for the property change, for use when re-establishing a
dropped connection (see below). It is RECOMMENDED that the
identifier is a timestamp representing the time at which the
property changed, in the "date-time" format specified by
[[RFC3339]].
event: level\n data: 42\n id: 2021-11-17T15:33:20.827Z\n\n
If the connection between the Consumer and Web Thing drops
(except as a result of the unobserve
operation
defined below), the Consumer MUST re-establish the connection
following the steps outlined in the Server-Sent Events
specification [[EVENTSOURCE]]. Once the connection is
re-established the Web Thing SHOULD, if possible, send any missed
property changes which occured since the last change specified by
the Consumer in a Last-Event-ID
header.
unobserveproperty
In order to stop observing a property, a Consumer MUST terminate the corresponding Server-Sent Events connection with the Web Thing as specified in the Server-Sent Events specification [[EVENTSOURCE]].
For Consumers implemented in JavaScript [[ECMASCRIPT]] and executed
in a runtime which exposes the
EventSource
interface, a Server-Sent Events
connection can be terminated using the close()
method
on an EventSource
[[EVENTSOURCE]] object.
levelSource.close();
observeallproperties
The URL of a properties resource to be used when observing
changes to all properties of a Web Thing MUST be obtained from a
Thing Description by locating a
Form
inside the top level
forms
member of a Thing Description for which:
op
member contains the value
observeallproperties
href
member is http
or https
subprotocol
member has a value of
sse
The resolved value of the href
member MUST then be
used as the URL of the properties resource.
In order to observe changes to all properties of a Web Thing, a Consumer MUST follow the Server-Sent Events [[EVENTSOURCE]] specification to open a connection with the Web Thing at the URL of the properties resource.
This involves the Consumer sending an HTTP request to the Web Thing with:
GET
Accept
header set to text/event-stream
Connection
header set to keep-alive
GET /things/lamp/properties HTTP/1.1 Host: mythingserver.com Accept: text/event-stream Connection: keep-alive
For Consumers implemented in JavaScript [[ECMASCRIPT]] and executed
in a runtime which exposes the
EventSource
interface, a Server-Sent Events
connection can be initiated using the EventSource
constructor.
const lampPropertiesSource = new EventSource('/things/lamp/properties');
If a Web Thing receives an HTTP request following the format above then it MUST follow the Server-Sent Events [[EVENTSOURCE]] specification to maintain an open connection with the Consumer and push new property values to the Consumer for all properties for which it has permission to observe.
This involves the Web Thing initially sending an HTTP response to the Consumer with:
200
Content-Type
header set to
text/event-stream
HTTP/1.1 200 OK Content-Type: text/event-stream
Whenever a property changes while the Web Thing has an open
connection with a Consumer, the Web Thing MUST send the new
property value to the Consumer using the event stream format in
the Server-Sent Events [[EVENTSOURCE]] specification. For each
message sent, the Web Thing MUST set the event
field
to the name of the PropertyAffordance
and populate
the data
field with the new property value. The
property data MUST follow the data schema specified in the
PropertyAffordance
and MUST be serialized in JSON.
The id
field SHOULD be set to a unique identifier for
the event, for use when re-establishing a dropped connection (see
below). It is RECOMMENDED that the identifier is a timestamp
representing the time at which the property changed, in the
"date-time" format specified by [[RFC3339]].
event: level\n data: 42\n id: 2021-11-17T15:33:20.827Z\n\n
If the connection between the Consumer and Web Thing drops
(except as a result of the unobserveallproperties
operation defined below), the Consumer MUST re-establish the
connection following the steps outlined in the Server-Sent Events
specification [[EVENTSOURCE]]. Once the connection is
re-established the Web Thing SHOULD, if possible, send any missed
property changes which occured since the last change specified by
the Consumer in a Last-Event-ID
header.
unobserveallproperties
In order to unobserve all properties, a Consumer MUST terminate the corresponding Server-Sent Events connection with the properties endpoint of the Web Thing, following the steps specified in the Server-Sent Events specification [[EVENTSOURCE]].
For Consumers implemented in JavaScript [[ECMASCRIPT]] and executed
in a runtime which exposes the
EventSource
interface, a Server-Sent Events
connection can be terminated using the close()
method
on an EventSource
[[EVENTSOURCE]] object.
lampPropertiesSource.close();
The HTTP SSE Profile uses Server-Sent Events [[EVENTSOURCE]] as a mechanism for Consumers to subscribe to events emitted by a Web Thing.
Consumers are not required to implement the
EventSource
JavaScript API from the Server-Sent Events
specification in order to conform with this profile. Any programming
language may be used to consume an event stream.
subscribeevent
The URL of an Event
resource to be used when
subscribing to an event MUST be obtained from a Thing Description
by locating a
Form
inside the corresponding
EventAffordance
for which:
op
member
contains the value subscribeevent
href
member is http
or https
subprotocol
member has a value of
sse
The resolved value of the href
member MUST then be used
as the URL of the Event
resource.
In order to subscribe to an event, a Consumer MUST follow the
Server-Sent Events [[EVENTSOURCE]] specification to open a
connection with the Web Thing at the URL of the Event
resource.
This involves the Consumer sending an HTTP request to the Web Thing with:
GET
Event
resourceAccept
header set to text/event-stream
Connection
header set to keep-alive
GET /things/lamp/events/overheated HTTP/1.1 Host: mythingserver.com Accept: text/event-stream Connection: keep-alive
For Consumers implemented in JavaScript [[ECMASCRIPT]] and executed
in a runtime which exposes the
EventSource
interface, a Server-Sent Events
connection can be initiated using the EventSource
constructor.
const overheatedEventSource = new EventSource('/things/lamp/events/overheated');
If a Web Thing receives an HTTP request following the format above and the Consumer has permission to subscribe to the corresponding event, then it MUST follow the Server-Sent Events [[EVENTSOURCE]] specification to maintain an open connection with the Consumer and push event data to the Consumer as events of the specified type are emitted.
This involves the Web Thing initially sending an HTTP response to the Consumer with:
200
Content-Type
header set to
text/event-stream
HTTP/1.1 200 OK Content-Type: text/event-stream
Whenever an event of the specified type occurs while the Web Thing
has an open connection with a Consumer, the Web Thing MUST
send event data to the Consumer using the event stream format in the
Server-Sent Events [[EVENTSOURCE]] specification.
For each message
sent, the Web Thing MUST set the event
field to the
name of the EventAffordance
and populate the
data
field with event data, if any.
The
event data MUST follow the data schema specified in the
EventAffordance
and be serialized in JSON.
The id
field SHOULD be set to a unique identifier for
the event, for use when re-establishing a dropped connection (see
below).
event: overheated\n data: 90\n id: 12345\n\n
If the connection between the Consumer and Web Thing drops
(except as a result of the unsubscribe
operation
defined below), the Consumer MUST re-establish the connection
following the steps outlined in the Server-Sent Events specification
[[EVENTSOURCE]].
Once the connection is re-established the Web Thing
SHOULD, if possible, send any missed events which occured since
the last event specified by the Consumer in a
Last-Event-ID
header.
unsubscribeevent
In order to unsubscribe from an event, a Consumer MUST terminate the corresponding Server-Sent Events connection with the Web Thing as specified in the Server-Sent Events specification [[EVENTSOURCE]].
For Consumers implemented in JavaScript [[ECMASCRIPT]] and executed
in a runtime which exposes the
EventSource
interface, a Server-Sent Events
connection can be terminated using the close()
method
on an EventSource
[[EVENTSOURCE]] object.
overheatedEventSource.close();
subscribeallevents
The URL of an events resource to be used when subscribing to all
events emitted by a Web Thing MUST be obtained from a Thing
Description by locating a
Form
inside the top level
forms
member of a Thing Description for which:
op
member contains the value
subscribeallevents
href
member is http
or https
subprotocol
member has a value of
sse
The resolved value of the href
member MUST then be used
as the URL of the events resource.
In order to subscribe to all events emitted by a Web Thing, a Consumer MUST follow the Server-Sent Events [[EVENTSOURCE]] specification to open a connection with the Web Thing at the URL of the events resource.
This involves the Consumer sending an HTTP request to the Web Thing with:
GET
Accept
header set to text/event-stream
Connection
header set to keep-alive
GET /things/lamp/events HTTP/1.1 Host: mythingserver.com Accept: text/event-stream Connection: keep-alive
For Consumers implemented in JavaScript [[ECMASCRIPT]] and executed
in a runtime which exposes the
EventSource
interface, a Server-Sent Events
connection can be initiated using the EventSource
constructor.
const lampEventsSource = new EventSource('/things/lamp/events');
If a Web Thing receives an HTTP request following the format above then it MUST follow the Server-Sent Events [[EVENTSOURCE]] specification to maintain an open connection with the Consumer and push event data to the Consumer for all event types for which it has permission to subscribe.
This involves the Web Thing initially sending an HTTP response to the Consumer with:
200
Content-Type
header set to
text/event-stream
HTTP/1.1 200 OK Content-Type: text/event-stream
Whenever an event occurs while the Web Thing has an open connection
with a Consumer, the Web Thing MUST send event data to the Consumer
using the event stream format in the Server-Sent Events
[[EVENTSOURCE]] specification.
For each message sent, the Web Thing
MUST set the event
field to the
name of the EventAffordance
and populate the
data
field with event data, if any.
The
event data MUST follow the data schema specified in the
EventAffordance
and be serialized in JSON.
The id
field SHOULD be set to a unique identifier for
the event, for use when re-establishing a dropped connection (see
below).
event: overheated\n data: 90\n id: 12345\n\n
If the connection between the Consumer and Web Thing drops
(except as a result of the unsubscribeallevents
operation defined below), the Consumer MUST re-establish the
connection following the steps outlined in the Server-Sent Events
specification [[EVENTSOURCE]].
Once the connection is re-established
the Web Thing SHOULD, if possible, send any missed events which
occured since the last event specified by the Consumer in a
Last-Event-ID
header.
unsubscribeallevents
In order to unsubscribe from all events, a Consumer MUST terminate the corresponding Server-Sent Events connection with the events endpoint of the Web Thing, following the steps specified in the Server-Sent Events specification [[EVENTSOURCE]].
For Consumers implemented in JavaScript [[ECMASCRIPT]] and executed
in a runtime which exposes the
EventSource
interface, a Server-Sent Events
connection can be terminated using the close()
method
on an EventSource
[[EVENTSOURCE]] object.
lampEventsSource.close();
This section defines the HTTP Webhook Profile, including a Protocol Binding for observing properties and listening for events using WebHooks.
The HTTP Webhook profile MAY be used in conjunction with the HTTP Baseline Profile in order to provide operations to read and write properties and invoke, query and cancel actions.
The HTTP Webhook profile MAY be used as an alternative event mechanism to the HTTP SEE Profile.
Webhooks are a mechanism for Consumers to subscribe to events emitted by a Web Thing.
The mechanism is scalable and supports Consumers that receive events from multiple Things. Consumers implement a Webhook listener that is handling the event streams generated by Things.
Describe how Webhooks are used in the WoT context. Explain listeners, data payloads, dataResponse payloads. Create normative language on multiple subscriptions to the same event by the same consumer.
Depending on the use case, a single listener for multiple things and multiple event types MAY be used.
The following sequence diagram illustrates the flow of messages between a Consumer and a Web Thing when subscribing to, unsubscribing from and receiving events.
TODO: The example needs more work on the data and dataResponse schemas.
The following example contains a snippet from a TD that illustrates how a Webhook event can be described.
... { "events": { "fireAlarm": { "title": "Fire Alarm" "description": "Critical Condition - Fire!", "subscription": { "type": "object", "properties": { "callbackURL": { "type": "string", "format": "uri", "description": "Callback URL provided by subscriber for Webhook notifications.", "writeOnly": true }, ... } "data": { "type": "boolean", "description": "true, if the alert button has been pushed, false, if the button was armed again." }, "dataResponse": { "type": "string", "description": "sprinkler status" }, "cancellation": { "type": "object", "properties": { "subscriptionID": { "type": "integer", "description": "subscription ID to cancel", "writeOnly": true } } }, "forms": [ { "op": "subscribeevent", "href": "http://192.168.0.124:8080/events/fireAlarm/subscribe", "contentType": "application/json", "htv:methodName": "POST" }, { "op": "unsubscribeevent", "href": "http://192.168.0.124:8080/events/fireAlarm/unsubscribe", "htv:methodName": "POST" } ] }, "batteryLow": { ... } } }
In order to denote that a given
Web Thing
conforms to the HTTP Webhook Profile, its Thing Description MUST have a
profile
member [[wot-thing-description]] with a value
of https://www.w3.org/2022/wot/profile/http-webhook/v1
.
Note that the profile
member is an array that may contain multiple
profile entries, which indicates that a Web Thing conforms to all of the profiles in that array.
Event notification messages MUST comply with the following data schema.
TODO: Describe data and dataResponse schemas.
For each notification message, the Web Thing MUST set the event
field to the
name of the EventAffordance
.
The event data
MUST follow the data schema specified in the
EventAffordance
and MUST be serialized in JSON.
The id
field MUST be set to a unique identifier for
the event.
This section defines a protocol binding which describes how a Consumer and a Web Thing communicate using Webhook Events.
A Consumer or Web Thing conforming to the HTTP Webhook Profile MUST implement this protocol binding.
subscribeevent
The URL of an Event
resource to be used when
subscribing to an event MUST be obtained from a Thing Description
by locating a
Form
inside the corresponding
EventAffordance
for which:
op
member
contains the value subscribeevent
href
member is http
or https
subprotocol
member has a value of
"webhook"
The resolved value of the href
member MUST then be used
as the URL of the Event
resource.
In order to subscribe to an event, a Consumer MUST
provide the listener URL in the data payload of the
subscribe
operation of the Event
resource.
This involves the Consumer sending an HTTP request to the Web Thing with:
POST
Event
resourceAccept
header set to application/json
TODO: Define subscription payload.
POST /things/lamp/events/overheated HTTP/1.1 Host: mythingserver.com Accept: application/json { "properties": { ... listener: uri, ... } }
If a Web Thing receives an HTTP request following the format above and the Consumer has permission to subscribe to the corresponding event, then it MUST send event messages to the Consumer as events of the specified type are emitted.
This involves the Web Thing initially sending an HTTP response to the Consumer with:
200
Content-Type
header set to
application/json
HTTP/1.1 200 OK Content-Type: application/json { subscriptionId: 1234-4544-1211 }
Whenever an event of the specified type occurs, the Web Thing MUST send event data to the Consumer using the event payload format defined in section ["#sec-http-webhook-profile-protocol-binding-events-notification"].
unsubscribeevent
In order to unsubscribe to an event, a Consumer MUST
provide the subscriptionID
in the data payload of the
unsubscribe
operation of the Event
resource.
This involves the Consumer sending an HTTP request to the Web Thing with:
POST
Event
resourceAccept
header set to application/json
Request Payload
contains a JSON
object with a valid subscriptionId
.
TODO: define subscriptionId format - urn?
subscribeallevents
The URL of an Events
resource to be used when subscribing to all
events emitted by a Web Thing MUST be obtained from a Thing
Description by locating a
Form
inside the top level
forms
member of a Thing Description for which:
op
member contains the value
subscribeallevents
href
member is http
or https
subprotocol
member has a value of
"sse"
The resolved value of the href
member MUST then be used
as the URL of the events resource.
In order to subscribe to all events emitted by a Web Thing, a Consumer MUST send an HTTP request to the Web Thing with:
POST
Accept
header set to text/event-stream
Request Payload
contains a JSON
object with a subscription payload.
POST /things/lamp/events/overheated HTTP/1.1 Host: mythingserver.com Accept: application/json { "properties": { ... listener: uri, ... } }
If a Web Thing receives an HTTP request following the format above, then it MUST send event messages to the Consumer for all event types for which it has permission to subscribe.
This involves the Web Thing initially sending an HTTP response to the Consumer with:
200
Content-Type
header set to
application/json
subscriptionId
.HTTP/1.1 200 OK Content-Type: text/event-stream { subscriptionId: 1234-4544-1211 }
Whenever an event occurs, the Web Thing MUST send event data to the Consumer using the event payload format defined in section Message Format.
If the connection between the Web Thing and the Consumer drops, the Web Thing MUST re-establish the connection.
Once the connection is re-established the Web Thing SHOULD, if possible, send any missed events which occured since the last succesful event notification.
unsubscribeallevents
In order to unsubscribe from all events, a Consumer MUST invoke the
unsubscribe
operation of the Event
resource.
This involves the Consumer sending an HTTP request to the Web Thing with:
POST
Event
resourceAccept
header set to application/json
Request Payload
contains a JSON
object with a valid subscriptionId
.
TODO: define subscription id format - urn?
A HTTP(s) connection to the event listener of a Consumer MUST be initiated and managed by the Web Thing.
A Consumer MUST terminate the subscription with the events endpoint of the Web Thing.
If a Consumer becomes unavailable and the Web Thing cannot successfully transmit event messages to the consumer, it SHOULD attempt several retries at increasing intervals.
After the maximum number of retries was reached, the Web Thing MAY terminate the subscription
without having received an unsubscribe
or unsubscribeall
operation.
If the connection between the Web Thing and the Consumer drops, the Web Thing MUST attempt to re-establish the connection.
Once the connection is re-established, the Web Thing SHOULD, if possible, send any missed events which occured since the last succesful event notification.
The Web Thing MAY reuse an existing connection to a listener for subsequent message traffic, or it MAY establish a new connection for each message.
When an event message has been received, the Consumer MUST respond with a "HTTP 200 OK" message.
An (optional) JSON payload MAY be provided to return a response back to the Web Thing via the same communication channel.
A Thing Description can be syntactically validated with the JSON Schema [[?JSON-SCHEMA]] for compliance with the HTTP profiles.
TODO: Define a JSON-SCHEMA.