diff --git a/images/scripting-action-data.png b/images/scripting-action-data.png new file mode 100644 index 00000000..7fa19f8b Binary files /dev/null and b/images/scripting-action-data.png differ diff --git a/images/scripting-action-data.svg b/images/scripting-action-data.svg new file mode 100644 index 00000000..213aa916 --- /dev/null +++ b/images/scripting-action-data.svg @@ -0,0 +1,3 @@ + + +
 invokeAction(
  string action,
  InteractionInput args,
  object options)
invokeAction(...
ConsumedThing
ConsumedThing
 Request to invoke name with params and options via Protocol Binding 
 Request to invoke name with params and options via Protocol Binding 
 Promise<InteractionOutput
 Promise<InteractionOutput> 
WoT
implementation
WoT...
 ActionHandler(
    string action,
    InteractionOutput args,
    object options)
ActionHandler(...
 Response (Body) 
 Response (Body) 
WoT
implementation
WoT...
extract parameters
from args
extract parameters...
 ExposedThing
 ExposedThing
return
value
return...
HAL
(sensors, actuators)
HAL...
Promise<InteractionInput>
Promise<InteractionInput>
Invoking an Action
Invoking an Action
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/images/scripting-read-data.png b/images/scripting-read-data.png new file mode 100644 index 00000000..ed81c820 Binary files /dev/null and b/images/scripting-read-data.png differ diff --git a/images/scripting-read-data.svg b/images/scripting-read-data.svg new file mode 100644 index 00000000..31f871bf --- /dev/null +++ b/images/scripting-read-data.svg @@ -0,0 +1,3 @@ + + +
readProperty(
    string name,
    object options)
readProperty(...
ConsumedThing
ConsumedThing
 Request to read name with options via Protocol Binding 
 Request to read name with options via Protocol Binding 
WoT
implementation
WoT...
 Response (Body) 
 Response (Body) 
WoT
implementation
WoT...
read
value
read...
Promise<InteractionInput>
Promise<InteractionInput>
ExposedThing
ExposedThing
 return
value
return...
HAL
(sensors)
HAL...
PropertyReadHandler(
    string name,
    object options)
PropertyRe...
 Promise<InteractionOutput
Promise<I...
 Reading a Property
 Reading a Property
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/images/scripting-write-data.png b/images/scripting-write-data.png new file mode 100644 index 00000000..2456d2f6 Binary files /dev/null and b/images/scripting-write-data.png differ diff --git a/images/scripting-write-data.svg b/images/scripting-write-data.svg new file mode 100644 index 00000000..0299ccf4 --- /dev/null +++ b/images/scripting-write-data.svg @@ -0,0 +1,3 @@ + + +
writeProperty(
  string name,
  InteractionInput value,
  object options)
writeProperty(...
ConsumedThing
ConsumedThing
Request to write name with value and options via Protocol Binding 
Request to write name with value and options via Protocol Binding 
Promise<void>
Promise<void>
WoT
implementation
WoT...
  PropertyWriteHandler(
    string name,
    InteractionOutput value,
    object options)
  PropertyWriteHandler(    string name,    InteractionOutput value,    object options)
 Response 
 Response 
WoT
implementation
WoT...
write
value
write...
ExposedThing
ExposedThing
return
status
return...
HAL
(actuators)
HAL...
Promise<void>
Promise<void>
 Writing a Property
 Writing a Property
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/index.html b/index.html index dd0ba089..0b9a4eec 100644 --- a/index.html +++ b/index.html @@ -470,346 +470,618 @@

The WoT API object

- + -
-

The ConsumedThing interface

+

Handling interaction data

+

+ As specified in [[WOT-TD]], WoT interactions extend DataSchema + and include a number of possible Forms, out of which one is selected + for the interaction. The + + Form contains a `contentType` to describe the data. + For certain content types, a DataSchema is defined, based on + JSON schema, making possible to represent these contents as + JavaScript types and eventually set range constraints on the data. +

+ +
+

The InteractionInput type

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

- Represents a client API to operate a Thing. Belongs to the - WoT Consumer conformance class. + Belongs to the WoT Consumer conformance class and represents the + WoT Interaction data provided by application scripts to the UA. +

+

+ DataSchemaValue is an + ECMAScript value that is accepted for DataSchema defined in [[WoT-TD]] + (i.e. null, boolean, number, string, array, or object). +

+

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

+

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

+

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

+
+ +
+

The InteractionOutput interface

+

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

+

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

       [SecureContext, Exposed=(Window,Worker)]
-      interface ConsumedThing {
-        constructor(ThingDescription td);
-        Promise<InteractionData> readProperty(DOMString propertyName,
-                                    optional InteractionOptions options = null);
-        Promise<PropertyMap> readAllProperties(optional InteractionOptions options = null);
-        Promise<PropertyMap> readMultipleProperties(
-                                    sequence<DOMString> propertyNames,
-                                    optional InteractionOptions options = null);
-        Promise<void> writeProperty(DOMString propertyName,
-                                    any value,
-                                    optional InteractionOptions options = null);
-        Promise<void> writeMultipleProperties(PropertyMap valueMap,
-                                    optional InteractionOptions options = null);
-        Promise<any> invokeAction(DOMString actionName,
-                                    optional any params = null,
-                                    optional InteractionOptions options = null);
-        Promise<void> observeProperty(DOMString name,
-                                    WotListener listener,
-                                    optional InteractionOptions options = null);
-        Promise<void> unobserveProperty(DOMString name,
-                                    optional InteractionOptions options = null);
-        Promise<void> subscribeEvent(DOMString name,
-                                    WotListener listener,
-                                    optional InteractionOptions options = null);
-        Promise<void> unsubscribeEvent(DOMString name,
-                                    optional InteractionOptions options = null);
-        ThingDescription getThingDescription();
-      };
-      dictionary InteractionOptions {
-        unsigned long formIndex;
-        object uriVariables;
+      interface InteractionOutput {
+        readonly attribute ReadableStream? data;
+        readonly attribute boolean dataUsed;
+        readonly attribute Form? form;
+        readonly attribute DataSchema? schema;
+        Promise<ArrayBuffer> arrayBuffer();
+        Promise<any> value();
       };
-      typedef object PropertyMap;
-      callback WotListener = void(any data);
     
- -
-

Constructing ConsumedThing

-

- After fetching a - Thing Description as a JSON object, one can create a - {{ConsumedThing}} object. -

-
- To create {{ConsumedThing}} with the {{ThingDescription}} - |td:ThingDescription|, run the following steps: -
    -
  1. - Run the expand a TD steps on |td|. If that fails, re-[= exception/throw =] the error and abort these steps. -
  2. -
  3. - Let |thing:ConsumedThing| be a new {{ConsumedThing}} object. -
  4. -
  5. - Let ||td|| be an internal slot of |thing| and let |td| be its value. -
  6. -
  7. - Return |thing|. -
  8. -
-
-
- -
-

The getThingDescription() method

-

- Returns the internal slot ||td|| of the {{ConsumedThing}} object that represents the Thing Description of the {{ConsumedThing}}. - Applications may consult the Thing metadata stored in ||td|| in order to introspect its capabilities before interacting with it. -

-
- -
-

- The InteractionOptions dictionary -

-

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

-

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

-

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

-

- The support for URI variables comes from the need exposed by [[WOT-TD]] to be able to describe existing TDs that use them, but it should be possible to write a Thing Description that would use Actions 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 represents the raw payload in + WoT Interactions as a {{ReadableStream}}, initially `null`. +

+

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

+

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

+

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

+

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

+

The value() function

+ Parses the data returned from the WoT Interaction and returns a + value with the type described by the DataSchema of the interaction + if that exists, or by the `contentType` of the Form used for the + interaction. The method MUST run the following steps: +
    +
  1. + Return a {{Promise}} |promise:Promise| and execute the next steps + in parallel. +
  2. +
  3. + If the value of the [[\value]] internal slot is not + `undefined`, resolve |promise| with that value and abort these steps. +
  4. +
  5. + If the value of the |data| property is not a {{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. +
  6. +
  7. + If |form|'s |contentType| is not `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. +
  8. +
  9. + Let |reader| be the result of + + getting a reader from |data|. If that threw an exception, reject + |promise| with that exception and abort these steps. +
  10. +
  11. + Let |bytes| be the result of + reading all bytes from |data| with |reader|. +
  12. +
  13. + Set |dataUsed| to `true`. +
  14. +
  15. + If |form|'s |contentType| is not `application/json` and if a mapping is + available in the Protocol Bindings from |form|'s |contentType| + to [JSON-SCHEMA], transform |bytes| with that mapping. +
  16. +
  17. + Let |json| be the result of running parse JSON from bytes on + |bytes|. If that throws, reject |promise| with that exception and + abort these steps. +
  18. +
  19. + Set [[\value]] to the result of running check data schema + on |json| and |schema|. If that throws, reject |promise| with that + exception and abort these steps. +
  20. +
  21. + Resolve |promise| with [[\value]]. +
  22. +
-
-

The 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 {{ThingDescription}}) as a maplike interface from string to any. -

+

The arrayBuffer() function

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

The readProperty() method

-
- Reads a Property value. Takes a string argument |propertyName| - and an optional {{InteractionOptions}} |options:InteractionOptions| - argument. It returns a Property value represented as `any` type. - The method MUST run the following steps: -
    -
  1. - Return a {{Promise}} |promise:Promise| and execute the next steps - in parallel. -
  2. -
  3. - If invoking this method is not allowed for the current scripting - context for security reasons, reject |promise| with a - {{SecurityError}} and abort these steps. -
  4. -
  5. - Let |interaction| be the value of ||td||'s |properties|'s - |propertyName|. -
  6. -
  7. - If |option|'s {{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`. -
  8. -
  9. - If |form| is failure, reject |promise| with a {{SyntaxError}} and - abort these steps. -
  10. -
  11. - Make a request to the underlying platform (via the - Protocol Bindings) to retrieve the value of the Property - given by |propertyName| using |form| and the optional URI templates - given in |options|' {{uriVariables}}. -
  12. -
  13. - If the request fails, reject |promise| with the error received from the Protocol Bindings and abort these steps. -
  14. -
  15. - Let |response| be the response received to the request. -
  16. -
  17. - Let |data| be the result of running parse interaction response - on |response|, |form| and |interaction|. - If that fails, reject |promise| with a {{SyntaxError}} and abort these steps. -
  18. -
  19. - Resolve |promise| with |data|. -
  20. -
-
+

The check data schema algorithm

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

The create interaction data algorithm

+ For a given ConsumedThing object |thing:ConsumedThing|, in order to + create interaction data given |source|, |form:Form| and + |schema:object|, run these steps: +
    +
  1. + Let |idata| be a new an {{InteractionOutput}} object whose |form| is + set to |form|, whose |schema| is set to |schema|, whose [[\value]] + internal slot is `undefined` and whose |data| is `null`. +
  2. +
  3. + If |source| is a {{ReadableStream}} object, let |idata|'s |data| be + |source|, return |idata| and abort these steps. +
  4. +
  5. + If |schema| and its |type| are defined and not `null`, run these sub-steps: +
      +
    1. + If |type| is `"null"` and |source| is not, + throw {{TypeError}} and abort these steps. +
    2. +
    3. + If |type| is `"boolean"` and |source| is a falsy value, set + |idata|'s [[\value]] |value| to `false`, otherwise to `true`. +
    4. +
    5. + If |type| is `"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. +
    6. +
    7. + If |type| is `"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. +
    8. +
    9. + If |type| is `"array"`, run these sub-steps: +
        +
      1. + If |source| is not an array, throw a {{TypeError}} and abort these steps. +
      2. +
      3. + Let |length| be the length of |source|. +
      4. +
      5. + If |form|'s |minItems| is defined and |length| is less than + that, or if |form|'s |maxItems| is defined and |length| is + more than that, throw {{RangeError}} and abort these steps. +
      6. +
      7. + For each |item| in |source|, let |itemschema| be |schema|'s + |items| and let |item| be the result of running the + create interaction data steps on |item|, |form| and + |itemschema|. + If this throws, re-throw that exception and abort these steps. +
      8. +
      9. + Set |data|'s [[\value]] to |source|. +
      10. +
      +
    10. +
    11. + If |type| is `"object"`, run these sub-steps: +
        +
      1. + If |source| is not an object, throw {{TypeError}} and abort these steps. +
      2. +
      3. + If |schema|'s |properties| is not an object, + throw {{TypeError}} and abort these steps. +
      4. +
      5. + For each property |key| in |source|, +
          +
        1. Let |value| be the value of |key|.
        2. +
        3. Let |propschema| be the value of |key| in |properties|.
        4. +
        5. + Let |value| be the result of running the + create interaction data steps on |value|, |form| + and |propschema|. + If this throws, re-throw that exception and abort these steps. +
        6. +
        +
      6. +
      7. + If |schema|'s |required| is an array, for each |item| in |required| + check if |item| is a property name in |source|. If an |item| is + not found in |source|, throw {{SyntaxError}} and abort these steps. +
      8. +
      9. + Set |data|'s [[\value]] to |source|. +
      10. +
      +
    12. +
    +
  6. +
  7. + Set |idata|'s |data| to a new {{ReadableStream}} created from |idata|'s + [[\value]] internal slot as its + + underlying source. +
  8. +
  9. + Return |idata|. +
  10. +
+
+ +

The parse interaction response algorithm

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

Using {{InteractionInput}} and {{InteractionOutput}}

+

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

+
+ +
Data structures used when reading data
+
+

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

+

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

+ +
+ +
Data structures used when writing data
+
+

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

+

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

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

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

+

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

+
+
+ +
+

The ConsumedThing interface

+

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

+
+      [SecureContext, Exposed=(Window,Worker)]
+      interface ConsumedThing {
+        constructor(ThingDescription td);
+        Promise<InteractionOutput> readProperty(DOMString propertyName,
+                                    optional InteractionOptions options = null);
+        Promise<PropertyMap> readAllProperties(optional InteractionOptions options = null);
+        Promise<PropertyMap> readMultipleProperties(
+                                    sequence<DOMString> propertyNames,
+                                    optional InteractionOptions options = null);
+        Promise<void> writeProperty(DOMString propertyName,
+                                    InteractionInput value,
+                                    optional InteractionOptions options = null);
+        Promise<void> writeMultipleProperties(PropertyMap valueMap,
+                                    optional InteractionOptions options = null);
+        Promise<InteractionOutput> invokeAction(DOMString actionName,
+                                    optional InteractionInput params = null,
+                                    optional InteractionOptions options = null);
+        Promise<void> observeProperty(DOMString name,
+                                    WotListener listener,
+                                    optional InteractionOptions options = null);
+        Promise<void> unobserveProperty(DOMString name,
+                                    optional InteractionOptions options = null);
+        Promise<void> subscribeEvent(DOMString name,
+                                    WotListener listener,
+                                    optional InteractionOptions options = null);
+        Promise<void> unsubscribeEvent(DOMString name,
+                                    optional InteractionOptions options = null);
+        ThingDescription getThingDescription();
+      };
+
+      dictionary InteractionOptions {
+        unsigned long formIndex;
+        object uriVariables;
+        any data;
+      };
+
+      typedef object PropertyMap;
+
+      callback WotListener = void(InteractionOutput data);
+    
-

The readMultipleProperties() method

+

Constructing ConsumedThing

+

+ After fetching a + Thing Description as a JSON object, one can create a + {{ConsumedThing}} object. +

- Reads multiple Property values with one or multiple requests. Takes the |propertyNames: string sequence| argument as a sequence of strings and an optional {{InteractionOptions}} |options:InteractionOptions| argument. It returns an object that maps keys from |propertyNames| to values returned by this algorithm. The method MUST run the following steps: + To create {{ConsumedThing}} with the {{ThingDescription}} + |td:ThingDescription|, run the following steps:
  1. - Return a {{Promise}} |promise:Promise| and execute the next steps in parallel. -
  2. -
  3. - If invoking this method is not allowed for the current scripting context for security reasons, reject |promise| with a {{SecurityError}} and abort these steps. -
  4. -
  5. - If |option|'s {{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`. -
  6. -
  7. - If |form| is failure, reject |promise| with a {{SyntaxError}} and abort these steps. -
  8. -
  9. - Let |result:object| be an object and for each string |name:string| in |propertyNames| add a property with key |name| and the value `null`. -
  10. -
  11. - Make a request to the underlying platform (via the Protocol Bindings) to retrieve the Property values given by |propertyNames| with |form| and optional URI templates given in |options|' {{uriVariables}}. -
  12. -
  13. - If this cannot be done with a single request with the Protocol Bindings, reject |promise| with a {{NotSupportedError}} and abort these steps. + Run the expand a TD steps on |td|. If that fails, re-[= exception/throw =] the error and abort these steps.
  14. - Process the response and for each |key| in |result|, run the following - sub-steps: -
      -
    1. - Let |value| be the value of |result|'s |key|. -
    2. -
    3. - Let |schema| be the value of ||td||'s |properties|'s |key|. -
    4. -
    5. - Let |property| be the result of running - parse interaction response on |value|, |form| and - |schema|. -
    6. -
    + Let |thing:ConsumedThing| be a new {{ConsumedThing}} object.
  15. - If the above step throws at any point, reject |promise| with that - exception and abort these steps. + Let ||td|| be an internal slot of |thing| and let |td| be its value.
  16. - Resolve |promise| with |result|. + Return |thing|.
-

The readAllProperties() method

-
- Reads all properties of the Thing with one or multiple requests. Takes an optional {{InteractionOptions}} |options:InteractionOptions| argument. It returns an object that maps keys from Property names to values returned by this algorithm. The method MUST run the following steps: -
    -
  1. - Return a {{Promise}} |promise:Promise| and execute the next steps in parallel. -
  2. -
  3. - If invoking this method is not allowed for the current scripting context for security reasons, reject |promise| with a {{SecurityError}} and abort these steps. -
  4. -
  5. - If |option|'s {{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`. -
  6. -
  7. - If |form| is failure, reject |promise| with a {{SyntaxError}} and abort these steps. -
  8. -
  9. - Make a request to the underlying platform (via the Protocol Bindings) to retrieve the value of the all the Property definitions from the TD with |form| and optional URI templates given in |options|' {{uriVariables}}. -
  10. -
  11. - If this cannot be done with a single request with the Protocol Bindings of the Thing, then reject |promise| with a {{NotSupportedError}} and abort these steps. -
  12. -
  13. - If the request fails, reject |promise| with the error received from the Protocol Bindings and abort these steps. -
  14. -
  15. - Process the reply and let |result:object| be an object with the keys and values obtained in the reply. -
  16. -
  17. - Process the response and for each |key| in |result|, run the following - sub-steps: -
      -
    1. - Let |value| be the value of |result|'s |key|. -
    2. -
    3. - Let |schema| be the value of ||td||'s |properties|'s |key|. -
    4. -
    5. - Let |property| be the result of running - parse interaction response on |value|, |form| and - |schema|. -
    6. -
    -
  18. -
  19. - Resolve |promise| with |result|. -
  20. -
-
+

The getThingDescription() method

+

+ Returns the internal slot ||td|| of the {{ConsumedThing}} object that represents the Thing Description of the {{ConsumedThing}}. + Applications may consult the Thing metadata stored in ||td|| in order to introspect its capabilities before interacting with it. +

+
+ +
+

+ The InteractionOptions dictionary +

+

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

+

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

+

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

+

+ The support for URI variables comes from the need exposed by [[WOT-TD]] to be able to describe existing TDs that use them, but it should be possible to write a Thing Description that would use Actions 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. +

-

The writeProperty() method

-
- Writes a single Property. Takes a string argument |propertyName:string|, a value argument |value:any| and an optional {{InteractionOptions}} |options:InteractionOptions| argument. It returns success or failure. The method MUST run the following steps: -
    -
  1. - Return a {{Promise}} |promise:Promise| and execute the next steps in parallel. -
  2. -
  3. - If invoking this method is not allowed for the current scripting context for security reasons, reject |promise| with a {{SecurityError}} and abort these steps. -
  4. -
  5. - Let |interaction| be the value of ||td||'s |properties|'s - |propertyName|. -
  6. -
  7. - If |option|'s {{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`. -
  8. -
  9. - If |form| is failure, reject |promise| with a {{SyntaxError}} and abort these steps. -
  10. -
  11. - Let |data| be the result of running the create interaction datasteps on |value|, |form| and |interaction|. If that throws, reject promise with that exception and abort these steps. -
  12. -
  13. - Make a request to the underlying platform (via the Protocol Bindings) - to write the Property given by |propertyName| using - |data:InteractionData| and the optional URI templates given in - |options|' {{uriVariables}}. -
  14. -
  15. - If the request fails, reject |promise| with the error received from the Protocol Bindings and abort these steps. -
  16. -
  17. - Otherwise resolve |promise|. -
  18. -
-
+

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

- As discussed in - Issue #193, the design decision is that write interactions only - return success or error, not the written value (optionally). - TDs should capture the schema of the Property - values, including precision and alternative formats. When a return - value is expected from the interaction, an Action should be used - instead of a Property. + It could be defined in Web IDL (as well as {{ThingDescription}}) as a maplike interface from string to any.

-

The writeMultipleProperties() method

+

The readProperty() method

- Writes a multiple Property values with one request. Takes a |properties:object| argument as an object with keys being Property names and values as Property values and an optional {{InteractionOptions}} |options:InteractionOptions| argument. It returns success or failure. The method MUST run the following steps: + Reads a Property value. Takes as arguments |propertyName:string| + and optionally |options:InteractionOptions|. + It returns a {{Promise}} that resolves with a Property value + represented as as an {{InteractionOutput}} object or rejects on error. + The method MUST run the following steps:
  1. Return a {{Promise}} |promise:Promise| and execute the next steps @@ -821,66 +1093,51 @@

    The writeMultipleProperties() method

    {{SecurityError}} and abort these steps.
  2. - If |option|'s {{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`. -
  3. -
  4. - If |form| is failure, reject |promise| with a {{SyntaxError}} and - abort these steps. + Let |interaction| be the value of ||td||'s |properties|'s + |propertyName|.
  5. - Let |result:object| be an object and for each string |name:string| in |propertyNames| add a property with key |name| and let its value be - `null`. + If |option|'s |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`.
  6. - Let |schemas:object| be an object and for each string |name:string| - in |propertyNames| add a property with key |name| and let its value - be the value of ||td||'s |properties|'s |name|. + If |form| is failure, reject |promise| with a {{SyntaxError}} and + abort these steps.
  7. - For each key |key:string| in |properties|, take its value as |value| - and run the create interaction data steps on |value|, |form| - and the value for |schema|'s |key|. - If that throws for any |name|, reject promise with that - exception and abort these steps. + Make a request to the underlying platform (via the + Protocol Bindings) to retrieve the value of the Property + given by |propertyName| using |form| and the optional URI templates + given in |options|' |uriVariables|.
  8. - Make a single request to the underlying platform (via the - Protocol Bindings) to write each Property provided in - |properties| with optional URI templates given in |options|' - {{uriVariables}}. + If the request fails, reject |promise| with the error received from the Protocol Bindings and abort these steps.
  9. - If this cannot be done with a single request with the - Protocol Bindings of the Thing, then reject |promise| - with a {{NotSupportedError}} and abort these steps. + Let |response| be the response received to the request.
  10. - If the request fails, return the error received from the - Protocol Bindings and abort these steps. + Let |data| be the result of running parse interaction response + on |response|, |form| and |interaction|. + If that fails, reject |promise| with a {{SyntaxError}} and abort these steps.
  11. - Otherwise resolve |promise|. + Resolve |promise| with |data|.
-

The WotListener callback

-

- User provided callback that takes `any` argument 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. -

-
- -
-

The observeProperty() method

+

The readMultipleProperties() method

- Makes a request for Property value change notifications. Takes a string argument |propertyName:string|, a {{WotListener}} - callback function |listener:WotListener| and an optional {{InteractionOptions}} |options:InteractionOptions| argument. It returns success or failure. + Reads multiple Property values with one or multiple requests. + Takes as arguments |propertyNames: string sequence| and optionally + |options:InteractionOptions|. + It returns a {{Promise}} that resolves with a {{PropertyMap}} object + that maps keys from |propertyNames| to values returned by this algorithm. The method MUST run the following steps:
  1. @@ -890,52 +1147,56 @@

    The observeProperty() method

    If invoking this method is not allowed for the current scripting context for security reasons, reject |promise| with a {{SecurityError}} and abort these steps.
  2. - If |listener| is not a {{Function}}, reject |promise| - with a {{TypeError}} and abort these steps. -
  3. -
  4. - Let |interaction| be the value of ||td||'s |properties|'s - |propertyName|. -
  5. -
  6. - If |option|'s {{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`. + If |option|'s |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`.
  7. If |form| is failure, reject |promise| with a {{SyntaxError}} and abort these steps.
  8. - Make a request to the underlying platform (via the Protocol Bindings) to observe Property identified by |propertyName| with |form| and optional URI templates given in |options|' {{uriVariables}}. + Let |result:object| be an object and for each string |name:string| in |propertyNames| add a property with key |name| and the value `null`.
  9. - If the request fails, reject |promise| with the error received from the Protocol Bindings and abort these steps. + Make a request to the underlying platform (via the Protocol Bindings) to retrieve the Property values given by |propertyNames| with |form| and optional URI templates given in |options|' |uriVariables|.
  10. - Otherwise resolve |promise|. + If this cannot be done with a single request with the Protocol Bindings, reject |promise| with a {{NotSupportedError}} and abort these steps.
  11. - Whenever the underlying platform receives a notification for this subscription with new Property value |value|, run the following sub-steps: -
      + Process the response and for each |key| in |result|, run the following + sub-steps: +
      1. - Let |reply| be the result of running parse interaction response - with |value|, |form| and |interaction|. If that throws, reject |promise| with that exception and abort these steps. + Let |value| be the value of |result|'s |key|.
      2. - Invoke |listener| with |reply|. + Let |schema| be the value of ||td||'s |properties|'s |key|. +
      3. +
      4. + Let |property| be the result of running + parse interaction response on |value|, |form| and + |schema|.
      5. -
    +
+ +
  • + If the above step throws at any point, reject |promise| with that + exception and abort these steps. +
  • +
  • + Resolve |promise| with |result|.
  • -

    The unobserveProperty() method

    +

    The readAllProperties() method

    - Makes a request for unsubscribing from Property value change notifications. Takes a string argument |propertyName:string| and an optional {{InteractionOptions}} |options:InteractionOptions| argument. - It returns success or failure. The method MUST run the following steps: + Reads all properties of the Thing with one or multiple requests. Takes |options:InteractionOptions| as optional argument. + It returns a {{Promise}} that resolves with a {{PropertyMap}} object that + maps keys from Property names to values returned by this algorithm. + The method MUST run the following steps:
    1. Return a {{Promise}} |promise:Promise| and execute the next steps in parallel. @@ -944,35 +1205,55 @@

      The unobserveProperty() method

      If invoking this method is not allowed for the current scripting context for security reasons, reject |promise| with a {{SecurityError}} and abort these steps.
    2. - Let |interaction| be the value of ||td||'s |properties|'s - |propertyName|. + If |option|'s |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`.
    3. - If |option|'s {{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`. + If |form| is failure, reject |promise| with a {{SyntaxError}} and abort these steps.
    4. - If |form| is failure, reject |promise| with a {{SyntaxError}} and abort these steps. + Make a request to the underlying platform (via the Protocol Bindings) to retrieve the value of the all the Property definitions from the TD with |form| and optional URI templates given in |options|' |uriVariables|.
    5. - Make a request to the underlying platform (via the Protocol Bindings) to stop observing the Property identified by |propertyName|, with |form| and optional URI templates given in |options|' {{uriVariables}}. + If this cannot be done with a single request with the Protocol Bindings of the Thing, then reject |promise| with a {{NotSupportedError}} and abort these steps.
    6. If the request fails, reject |promise| with the error received from the Protocol Bindings and abort these steps.
    7. - Otherwise resolve |promise|. + Process the reply and let |result:object| be an object with the keys and values obtained in the reply. +
    8. +
    9. + Process the response and for each |key| in |result|, run the following + sub-steps: +
        +
      1. + Let |value| be the value of |result|'s |key|. +
      2. +
      3. + Let |schema| be the value of ||td||'s |properties|'s |key|. +
      4. +
      5. + Let |property| be the result of running + parse interaction response on |value|, |form| and + |schema|. +
      6. +
      +
    10. +
    11. + Resolve |promise| with |result|.
    -

    The invokeAction() method

    +

    The writeProperty() method

    - Makes a request for invoking an Action and return the result. Takes a string argument |actionName:string|, an optional argument |params:any| and an optional {{InteractionOptions}} |options:InteractionOptions| argument. It returns the result of the Action or an error. The method MUST run the following steps: + Writes a single Property. Takes as arguments |propertyName:string|, + |value:InteractionInput| and optionally |options:InteractionOptions|. + It returns a {{Promise}} that resolves on success and rejects on failure. + The method MUST run the following steps:
    1. Return a {{Promise}} |promise:Promise| and execute the next steps in parallel. @@ -981,87 +1262,131 @@

      The invokeAction() method

      If invoking this method is not allowed for the current scripting context for security reasons, reject |promise| with a {{SecurityError}} and abort these steps.
    2. - Let |interaction| be the value of ||td||'s |actions|'s - |actionName|. + Let |interaction| be the value of ||td||'s |properties|'s + |propertyName|.
    3. - If |option|'s {{formIndex}} is defined, let |form| be the - Form associated with {{formIndex}} in |interaction|'s |forms| + If |option|'s |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`. + |interaction|'s |forms| whose |op| is `writeproperty`.
    4. If |form| is failure, reject |promise| with a {{SyntaxError}} and abort these steps.
    5. - Make a request to the underlying platform (via the Protocol Bindings) to invoke the Action identified by |actionName| with parameters provided in |params| with |form| and optional URI templates given in |options|'s {{uriVariables}}. -
    6. -
    7. - If the request fails locally or returns an error over the network, reject |promise| with the error received from the Protocol Bindings and abort these steps. + Let |data| be the result of running the create interaction datasteps on |value|, |form| and |interaction|. If that throws, reject promise with that exception and abort these steps.
    8. - Let |value| be the reply returned in the reply. + Make a request to the underlying platform (via the Protocol Bindings) + to write the Property given by |propertyName| using + |data:InteractionOutput| and the optional URI templates given in + |options|' |uriVariables|.
    9. - Let |result| be the result of running parse interaction response - with |value|, |form| and |interaction|. If that throws, reject |promise| with that exception and abort these steps. + If the request fails, reject |promise| with the error received from the Protocol Bindings and abort these steps.
    10. - Resolve |promise| with |result|. + Otherwise resolve |promise|.
    +

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

    -

    The subscribeEvent() method

    +

    The writeMultipleProperties() method

    - Makes a request for subscribing to Event notifications. Takes a string argument |eventName|, a {{WotListener}} callback function |listener:WoTListener| and an optional {{InteractionOptions}} |options:InteractionOptions| argument. It returns success or failure. - The method MUST run the following steps: + Writes a multiple Property values with one request. + Takes as arguments |properties:object| - as an object with keys being + Property names and values as Property values - and optionally + |options:InteractionOptions|. + It returns a {{Promise}} that resolves on success and rejects on failure. The method MUST run the following steps:
    1. - Return a {{Promise}} |promise:Promise| and execute the next steps in parallel. + Return a {{Promise}} |promise:Promise| and execute the next steps + in parallel.
    2. - If invoking this method is not allowed for the current scripting context for security reasons, reject |promise| with a {{SecurityError}} and abort these steps. + If invoking this method is not allowed for the current scripting + context for security reasons, reject |promise| with a + {{SecurityError}} and abort these steps.
    3. - If |listener| is not a {{Function}}, reject |promise| - with a {{TypeError}} and abort these steps. + If |option|'s |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`.
    4. - Let |interaction| be the value of ||td||'s |events|'s |eventName|. + If |form| is failure, reject |promise| with a {{SyntaxError}} and + abort these steps.
    5. - If |option|'s {{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`. + Let |result:object| be an object and for each string |name:string| in |propertyNames| add a property with key |name| and let its value be + `null`.
    6. - If |form| is failure, reject |promise| with a {{SyntaxError}} and abort these steps. + Let |schemas:object| be an object and for each string |name:string| + in |propertyNames| add a property with key |name| and let its value + be the value of ||td||'s |properties|'s |name|.
    7. - Make a request to the underlying platform (via the Protocol Bindings) to subscribe to an Event identified by |eventName:string| with |form| and optional URI templates given in |options|' {{uriVariables}}. + For each key |key:string| in |properties|, take its value as |value| + and run the create interaction data steps on |value|, |form| + and the value for |schema|'s |key|. + If that throws for any |name|, reject promise with that + exception and abort these steps.
    8. - If the request fails, reject |promise| with the error received from the Protocol Bindings and abort these steps. + Make a single request to the underlying platform (via the + Protocol Bindings) to write each Property provided in + |properties| with optional URI templates given in |options|' + |uriVariables|.
    9. - Otherwise resolve |promise|. + If this cannot be done with a single request with the + Protocol Bindings of the Thing, then reject |promise| + with a {{NotSupportedError}} and abort these steps.
    10. - Whenever the underlying platform receives a notification for this Event subscription, implementations SHOULD invoke - |listener| with the result of running parse interaction response on the data provided with the Event, |form| and |interaction|. + If the request fails, return the error received from the + Protocol Bindings and abort these steps. +
    11. +
    12. + Otherwise resolve |promise|.
    -

    The unsubscribeEvent() method

    +

    The WotListener callback

    +

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

    +
    + +
    +

    The observeProperty() method

    - Makes a request for unsubscribing from Event notifications. Takes a string argument |eventName:string| and an optional {{InteractionOptions}} |options:InteractionOptions| argument. It returns success or failure. The method MUST run the following steps: + Makes a request for Property value change notifications. + Takes as arguments |propertyName:string|, |listener:WotListener| and + optionally |options:InteractionOptions|. + It returns a {{Promise}} that resolves on success and rejects on failure. + The method MUST run the following steps:
    1. Return a {{Promise}} |promise:Promise| and execute the next steps in parallel. @@ -1070,476 +1395,297 @@

      The unsubscribeEvent() method

      If invoking this method is not allowed for the current scripting context for security reasons, reject |promise| with a {{SecurityError}} and abort these steps.
    2. - Let |interaction| be the value of ||td||'s |events|'s |eventName|. + If |listener| is not a {{Function}}, reject |promise| + with a {{TypeError}} and abort these steps. +
    3. +
    4. + Let |interaction| be the value of ||td||'s |properties|'s + |propertyName|.
    5. - If |option|'s {{formIndex}} is defined, let |form| be the - Form associated with {{formIndex}} in |interaction|'s |forms| + If |option|'s |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`. + |interaction|'s |forms| array whose |op| is `observeproperty`.
    6. If |form| is failure, reject |promise| with a {{SyntaxError}} and abort these steps.
    7. - Make a request to the underlying platform (via the Protocol Bindings) to unsubscribe from the Event identified by |eventName| with |form| and optional URI templates given in |options|' {{uriVariables}}. + Make a request to the underlying platform (via the Protocol Bindings) to observe Property identified by |propertyName| with |form| and optional URI templates given in |options|' |uriVariables|.
    8. If the request fails, reject |promise| with the error received from the Protocol Bindings and abort these steps.
    9. - Resolve |promise|. + Otherwise resolve |promise|.
    10. - If the underlying platform receives further notifications for this Event subscription, implementations SHOULD silently suppress them. -
    11. -
    -
    -
    - -
    -

    ConsumedThing Examples

    -

    - The next example illustrates how to fetch a TD by URL, create a {{ConsumedThing}}, read metadata (title), read property value, subscribe to property change, subscribe to a WoT event, unsubscribe. -

    -
    -        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;
    -        };
    -      
    -
    -
    - -
    -

    The 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 Forms, out of which one is selected - for the interaction. The - - Form contains a `contentType` to describe the data. - For certain content types, a DataSchema is defined, based on - JSON schema, making possible to represent these contents as - JavaScript types and eventually set range constraints on the data. -

    -

    - This interface exposes the data returned from the WoT Interaction. -

    -
    -      [SecureContext, Exposed=(Window,Worker)]
    -      interface InteractionData {
    -        readonly attribute ReadableStream? data;
    -        readonly attribute boolean dataUsed;
    -        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). -

    + Whenever the underlying platform receives a notification for this subscription with new Property value |value|, run the following sub-steps: +
      +
    • + Let |reply| be the result of running parse interaction response + with |value|, |form| and |interaction|. If that throws, reject |promise| with that exception and abort these steps. +
    • +
    • + Invoke |listener| with |reply|. +
    • +
    + + + +
    -

    The arrayBuffer() function

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

    The unobserveProperty() method

    +
    + Makes a request for unsubscribing from Property value change notifications. + Takes as arguments |propertyName:string| and optionally |options:InteractionOptions|. + It returns a {{Promise}} that resolves on success and rejects on failure. + The method MUST run the following steps: +
      +
    1. + Return a {{Promise}} |promise:Promise| and execute the next steps in parallel. +
    2. +
    3. + If invoking this method is not allowed for the current scripting context for security reasons, reject |promise| with a {{SecurityError}} and abort these steps. +
    4. +
    5. + Let |interaction| be the value of ||td||'s |properties|'s + |propertyName|. +
    6. +
    7. + If |option|'s |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`. +
    8. +
    9. + If |form| is failure, reject |promise| with a {{SyntaxError}} and abort these steps. +
    10. +
    11. + Make a request to the underlying platform (via the Protocol Bindings) to stop observing the Property identified by |propertyName|, with |form| and optional URI templates given in |options|' |uriVariables|. +
    12. +
    13. + If the request fails, reject |promise| with the error received from the Protocol Bindings and abort these steps. +
    14. +
    15. + Otherwise resolve |promise|. +
    16. +
    +
    -

    The value() function

    - Parses the data returned from the WoT Interaction and returns a - value with the type described by the DataSchema of the interaction - if that exists, or by the `contentType` of the Form used for the - interaction. The method MUST run the following steps: -
      -
    1. - Return a {{Promise}} |promise:Promise| and execute the next steps - in parallel. -
    2. -
    3. - If the value of the [[\value]] internal slot is not - `undefined`, resolve |promise| with that value and abort these steps. -
    4. -
    5. - If the value of the |data| property is not a {{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. -
    6. -
    7. - If |form|'s |contentType| is not `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. -
    8. -
    9. - Let |reader| be the result of - - getting a reader from |data|. If that threw an exception, reject - |promise| with that exception and abort these steps. -
    10. -
    11. - Let |bytes| be the result of - reading all bytes from |data| with |reader|. -
    12. -
    13. - Set |dataUsed| to `true`. -
    14. -
    15. - If |form|'s |contentType| is not `application/json` and if a mapping is - available in the Protocol Bindings from |form|'s |contentType| - to [JSON-SCHEMA], transform |bytes| with that mapping. -
    16. -
    17. - Let |json| be the result of running parse JSON from bytes on - |bytes|. If that throws, reject |promise| with that exception and - abort these steps. -
    18. -
    19. - Set [[\value]] to the result of running check data schema - on |json| and |schema|. If that throws, reject |promise| with that - exception and abort these steps. -
    20. -
    21. - Resolve |promise| with [[\value]]. -
    22. -
    +
    +

    The invokeAction() method

    +
    + Makes a request for invoking an Action and return the result. + Takes as arguments |actionName:string|, optionally + |params:InteractionInput| and optionally |options:InteractionOptions|. + It returns a {{Promise}} that resolves with the result of the Action + represented as an {{InteractionOutput}} object, or rejects with an error. + The method MUST run the following steps: +
      +
    1. + Return a {{Promise}} |promise:Promise| and execute the next steps in parallel. +
    2. +
    3. + If invoking this method is not allowed for the current scripting context for security reasons, reject |promise| with a {{SecurityError}} and abort these steps. +
    4. +
    5. + Let |interaction| be the value of ||td||'s |actions|'s + |actionName|. +
    6. +
    7. + If |option|'s |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`. +
    8. +
    9. + If |form| is failure, reject |promise| with a {{SyntaxError}} and abort these steps. +
    10. +
    11. + Let |args| be the result of running the create interaction datasteps on |params|, |form| and |interaction|. If that throws, reject promise with that exception and abort these steps. +
    12. +
    13. + Make a request to the underlying platform (via the Protocol Bindings) to invoke the Action identified by |actionName| with parameters provided in |args| and optional URI templates given in |options|'s |uriVariables|. +
    14. +
    15. + If the request fails locally or returns an error over the network, reject |promise| with the error received from the Protocol Bindings and abort these steps. +
    16. +
    17. + Let |value| be the reply returned in the reply. +
    18. +
    19. + Let |result| be the result of running parse interaction response + with |value|, |form| and |interaction|. If that throws, reject |promise| with that exception and abort these steps. +
    20. +
    21. + Resolve |promise| with |result|. +
    22. +
    +
    -

    The check data schema algorithm

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

    The subscribeEvent() method

    +
    + Makes a request for subscribing to Event notifications. + Takes as arguments |eventName:string|, |listener:WoTListener| and + optionally |options:InteractionOptions|. + It returns a {{Promise}} to signal success or failure. + The method MUST run the following steps: +
      +
    1. + Return a {{Promise}} |promise:Promise| and execute the next steps in parallel. +
    2. +
    3. + If invoking this method is not allowed for the current scripting context for security reasons, reject |promise| with a {{SecurityError}} and abort these steps. +
    4. +
    5. + If |listener| is not a {{Function}}, reject |promise| + with a {{TypeError}} and abort these steps. +
    6. +
    7. + Let |interaction| be the value of ||td||'s |events|'s |eventName|. +
    8. +
    9. + If |option|'s |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`. +
    10. +
    11. + If |form| is failure, reject |promise| with a {{SyntaxError}} and abort these steps. +
    12. +
    13. + Make a request to the underlying platform (via the Protocol Bindings) to subscribe to an Event identified by |eventName:string| with |form| and optional URI templates given in |options|' |uriVariables| + and optional subscription data given in |options|'s |data|. +
    14. +
    15. + If the request fails, reject |promise| with the error received from the Protocol Bindings and abort these steps. +
    16. +
    17. + Otherwise resolve |promise|. +
    18. +
    19. + Whenever the underlying platform receives a notification for this Event subscription, implementations SHOULD invoke + |listener| with the result of running parse interaction response on the data provided with the Event, |form| and |interaction|. +
    20. +
    +
    -

    The create interaction data algorithm

    - For a given ConsumedThing object |thing:ConsumedThing|, in order to - create interaction data given |source|, |form:Form| and - |schema:object|, run these steps: -
      -
    1. - If |source| is an {{InteractionData}} object whose |data| is a - {{ReadableStream}}, set its |form| to |form|, - return |source| and abort these steps. -
    2. -
    3. - Let |idata| be a new an {{InteractionData}} object whose [[\value]] - internal slot is `undefined`, whose |form| is |form|, - whose |schema| is set to |schema| and whose |data| is `null`. -
    4. -
    5. - If |schema|'s |type| is defined, run these sub-steps: -
        -
      1. - If |type| is `"null"` and |source| is not, - throw {{TypeError}} and abort these steps. -
      2. -
      3. - If |type| is `"boolean"` and |source| is a falsy value, set - |idata|'s [[\value]] |value| to `false`, otherwise to `true`. -
      4. -
      5. - If |type| is `"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. -
      6. -
      7. - If |type| is `"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. -
      8. -
      9. - If |type| is `"array"`, run these sub-steps: -
          -
        1. - If |source| is not an array, throw a {{TypeError}} and abort these steps. -
        2. -
        3. - Let |length| be the length of |source|. -
        4. -
        5. - If |form|'s |minItems| is defined and |length| is less than - that, or if |form|'s |maxItems| is defined and |length| is - more than that, throw {{RangeError}} and abort these steps. -
        6. -
        7. - For each |item| in |source|, let |itemschema| be |schema|'s - |items| and let |item| be the result of running the - create interaction data steps on |item|, |form| and - |itemschema|. - If this throws, re-throw that exception and abort these steps. -
        8. -
        9. - Set |data|'s [[\value]] to |source|. -
        10. -
        -
      10. -
      11. - If |type| is `"object"`, run these sub-steps: -
          -
        1. - If |source| is not an object, throw {{TypeError}} and abort these steps. -
        2. -
        3. - If |schema|'s |properties| is not an object, - throw {{TypeError}} and abort these steps. -
        4. -
        5. - For each property |key| in |source|, -
            -
          1. Let |value| be the value of |key|.
          2. -
          3. Let |propschema| be the value of |key| in |properties|.
          4. -
          5. - Let |value| be the result of running the - create interaction data steps on |value|, |form| - and |propschema|. - If this throws, re-throw that exception and abort these steps. -
          6. -
          -
        6. -
        7. - If |schema|'s |required| is an array, for each |item| in |required| - check if |item| is a property name in |source|. If an |item| is - not found in |source|, throw {{SyntaxError}} and abort these steps. -
        8. -
        9. - Set |data|'s [[\value]] to |source|. -
        10. -
        -
      12. -
      -
    6. -
    7. - Set |idata|'s |data| to a new {{ReadableStream}} created from |idata|'s - [[\value]] internal slot as its - - underlying source. -
    8. -
    9. - Return |idata|. -
    10. -
    +
    +

    The unsubscribeEvent() method

    +
    + Makes a request for unsubscribing from Event notifications. + Takes as arguments |eventName:string| and optionally + |options:InteractionOptions|. + It returns a {{Promise}} to signal success or failure. + The method MUST run the following steps: +
      +
    1. + Return a {{Promise}} |promise:Promise| and execute the next steps in parallel. +
    2. +
    3. + If invoking this method is not allowed for the current scripting context for security reasons, reject |promise| with a {{SecurityError}} and abort these steps. +
    4. +
    5. + Let |interaction| be the value of ||td||'s |events|'s |eventName|. +
    6. +
    7. + If |option|'s |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`. +
    8. +
    9. + If |form| is failure, reject |promise| with a {{SyntaxError}} and abort these steps. +
    10. +
    11. + Make a request to the underlying platform (via the Protocol Bindings) to unsubscribe from the Event identified by |eventName| with |form| and optional URI templates given in |options|' |uriVariables| + and optional unsubscribe data given in |options|'s |data|. +
    12. +
    13. + If the request fails, reject |promise| with the error received from the Protocol Bindings and abort these steps. +
    14. +
    15. + Resolve |promise|. +
    16. +
    17. + If the underlying platform receives further notifications for this Event subscription, implementations SHOULD silently suppress them. +
    18. +
    +
    -

    The parse interaction response algorithm

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

    ConsumedThing Examples

    +

    + The next example illustrates how to fetch a TD by URL, create a {{ConsumedThing}}, read metadata (title), read property value, subscribe to property change, subscribe to a WoT event, unsubscribe. +

    +
    +        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; + }; + +
    +

    The ExposedThing interface

    @@ -1550,232 +1696,981 @@

    The ExposedThing interface

    [SecureContext, Exposed=(Window,Worker)] interface ExposedThing: ConsumedThing { ExposedThing setPropertyReadHandler(DOMString name, - PropertyReadHandler readHandler); + PropertyReadHandler handler); ExposedThing setPropertyWriteHandler(DOMString name, - PropertyWriteHandler writeHandler); + PropertyWriteHandler handler); + ExposedThing setPropertyObserveHandler(DOMString name, + PropertyReadHandler handler); + ExposedThing setPropertyUnobserveHandler(DOMString name, + PropertyReadHandler handler); + ExposedThing setActionHandler(DOMString name, ActionHandler action); - void emitEvent(DOMString name, any data); + + ExposedThing setEventSubscribeHandler(DOMString name, + EventSubscriptionHandler handler); + ExposedThing setEventUnsubscribeHandler(DOMString name, + EventSubscriptionHandler handler); + ExposedThing setEventHandler(DOMString name, + EventListenerHandler eventHandler); + void emitEvent(DOMString name, InteractionInput data); + Promise<void> expose(); Promise<void> destroy(); }; + callback PropertyReadHandler = Promise<any>( optional InteractionOptions options = null); - callback PropertyWriteHandler = Promise<void>(any value, + + callback PropertyWriteHandler = Promise<void>( + InteractionOutput value, + optional InteractionOptions options = null); + + callback ActionHandler = Promise<InteractionInput>( + InteractionOutput params, optional InteractionOptions options = null); - callback ActionHandler = Promise<any>(any params, + + callback EventSubscriptionHandler = Promise<void>( optional InteractionOptions options = null); + + callback EventListenerHandler = Promise<InteractionInput>(); +
    +

    Constructing {{ExposedThing}}

    +

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

    +

    + Note that an existing {{ThingDescription}} object can be optionally modified (for instance by adding or removing elements on its |properties|, |actions| and |events| internal properties) and the resulting object can used for constructing an + {{ExposedThing}} object. This is the current way of adding and + removing Property, Action and Event definitions, as illustrated in the examples. +

    +

    + Before invoking expose(), the {{ExposedThing}} object does not serve any requests. This allows first constructing {{ExposedThing}} and then initialize its Properties and service handlers before starting serving requests. +

    +
    + To construct an {{ExposedThing}} with the {{ThingDescription}} + |td:ThingDescription|, run the following steps: +
      +
    1. + If invoking this method is not allowed for the current scripting context for security reasons, [= exception/throw =] a {{SecurityError}} and abort these steps. +
    2. +
    3. + Run the expand a TD steps on |td|. If that fails, re-[= exception/throw =] the error and abort these steps. +
    4. +
    5. + Let |thing:ExposedThing| be a new {{ExposedThing}} object. +
    6. +
    7. + Let ||td|| be an internal slot of |thing| and let |td| be its value. +
    8. +
    9. + Return |thing|. +
    10. +
    +
    +
    +
    +

    Methods inherited from {{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 {{ConsumedThing}} interface in an {{ExposedThing}} provide the default methods to interact with the {{ExposedThing}}. +

    +

    + After constructing an {{ExposedThing}}, 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 expose() on the {{ExposedThing}} in order to start serving external requests. +

    +

    + The request handlers actually implement the behavior and it is the + responsibility of the developers to keep the Thing Description + defined in {{ExposedThing}} synchronized with the implementation of the + request handlers. +

    +
    + +
    +

    The PropertyReadHandler callback

    +

    + A function that is called when an external request for reading a + Property is received and defines what to do with such requests. + It returns a {{Promise}} and resolves with an {{ReadableStream}} object or an + + ECMAScript value conforming to DataSchema, or rejects with an + error if the property is not found or the value cannot be retrieved. +

    +
    + +

    The setPropertyReadHandler() method

    +

    + Takes as arguments |name:string| and |handler: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 |handler| callback function should implement reading a Property and SHOULD be called by implementations when a request for reading a Property is received from the underlying platform. +

    +

    + There MUST be at most one handler for any given Property, so newly added handlers MUST replace the previous handlers. If no handler is initialized for any given Property, implementations SHOULD implement a default property read handler based on the Thing Description provided in the ||td|| internal slot. +

    +
    + When the method is invoked given |name:string| and + |handler:PropertyReadHandler|, implementations MUST run the + following steps: +
      +
    1. + If invoking this method is not allowed for the current scripting context for security reasons, [= exception/throw =] a {{SecurityError}} and abort these steps. +
    2. +
    3. + Let |interaction| be the value of ||td||'s |properties|'s + |name|. +
    4. +
    5. + If a Property interaction with |name| is not found, + [= exception/throw =] {{NotFoundError}} and abort these steps. +
    6. +
    7. + Set the internal slot ||readHandler|| on |interaction| to + |handler|. +
    8. +
    +
    +
    + +

    Handling requests for reading a Property

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

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

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

    Handling requests for reading multiple Poperties

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

    Handling requests for reading all Properties

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

    The setPropertyObserveHandler() method

    +

    + Takes as arguments |name:string| and |handler: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 {{InteractionOutput}} object or + reject with an error. +

    +

    + There MUST be at most one handler for any given Property, so newly added handlers MUST replace the previous handlers. If no handler is initialized for any given Property, implementations SHOULD implement a default property read handler based on the Thing Description. +

    +
    + When the method is invoked given |name:string| and + |handler:PropertyReadHandler|, implementations MUST run the + following steps: +
      +
    1. + If invoking this method is not allowed for the current scripting context for security reasons, [= exception/throw =] a {{SecurityError}} and abort these steps. +
    2. +
    3. + Let |interaction| be the value of ||td||'s |properties|'s + |name|. +
    4. +
    5. + If a Property interaction with |name| is not found, + [= exception/throw =] {{NotFoundError}} and abort these steps. +
    6. +
    7. + Set the internal slot ||observeHandler|| on |interaction| + to |handler|. +
    8. +
    +
    +
    + +

    Handling Property observe requests

    +
    + When a network request for observing a Property |name:string| is + received by the implementation with |options:InteractionOptions|, + run the following steps: +
      +
    1. + If this operation is not supported, send back a {{NotSupportedError}} + according to the Protocol Bindings and abort these steps. +
    2. +
    3. + If this operation is not allowed, send back a {{NotAllowedError}} + according to the Protocol Bindings and abort these steps. +
    4. +
    5. + Let |property| be the value of ||td||'s |properties|'s |name|. + If it does not exist, send back a {{NotFoundError}} in the + reply and abort these steps. +
    6. +
    7. + Save the request sender information together with |options| to + |property|'s internal observer list, in order to be able + to notify about Property value changes. +
    8. +
    9. + Every time the value of |property| changes, run the following sub-steps: +
        +
      1. + Let |handler:function| be `null.` +
      2. +
      3. + If there is an ||observeHandler|| internal slot associated + with |name| on |property|, let |handler| be that. +
      4. +
      5. + Otherwise, if there is a ||readHandler|| internal slot + associated with |name| on |property|, let |handler| be that. +
      6. +
      7. + If |handler| is `null`, abort these steps. +
      8. +
      9. + Let |promise| be the result of invoking |handler| wih |options|. +
      10. +
      11. + If |promise| rejects, abort these steps. +
      12. +
      13. + If |promise| resolves with |data|, then for each |observer| in |property|'s internal observer list, run the following + sub-steps: +
          +
        1. + Let |options| be the interaction options saved with |observer|. +
        2. +
        3. + Create a |reply| from |data| and |options| according to the + Protocol Bindings. +
        4. +
        5. + Send |reply| to |observer|. +
        6. +
        +
      14. +
      +
    10. +
    +
    +
    + +

    The setPropertyUnobserveHandler() method

    +

    + Takes as arguments |name:string| and |handler: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. +

    +
    + When the method is invoked given |name:string| and + |handler:PropertyReadHandler|, implementations MUST run the + following steps: +
      +
    1. + If invoking this method is not allowed for the current scripting context for security reasons, [= exception/throw =] a + {{SecurityError}} and abort these steps. +
    2. +
    3. + Let |interaction| be the value of ||td||'s |properties|'s + |name|. +
    4. +
    5. + If a Property interaction with |name| is not found, + [= exception/throw =] {{NotFoundError}} and abort these steps. +
    6. +
    7. + Set the internal slot ||unobserveHandler|| on |interaction| + to |handler|. +
    8. +
    +
    +
    + +

    Handling Property unobserve requests

    +
    + When a network request for unobserving a Property |name:string| + with |options:InteractionOptions| is received by the implementation, + run the following steps: +
      +
    1. + If this operation is not supported, send back a {{NotSupportedError}} + according to the Protocol Bindings and abort these steps. +
    2. +
    3. + If this operation is not allowed, send back a {{NotAllowedError}} + according to the Protocol Bindings and abort these steps. +
    4. +
    5. + Let |property| be the value of ||td||'s |properties|'s |name|. + If it does not exist, send back a {{NotFoundError}} in the + reply and abort these steps. +
    6. +
    7. + If there is an ||unobserveHandler|| defined for |name| on |property|, + invoke that with |options|, send back a reply following the + Protocol Bindings and abort these steps. +
    8. +
    9. + Let |sender| be the matching observer found in |property|'s + internal observer list. If not found, send back a + {{NotFoundError}} in the reply and abort these steps. +
    10. +
    11. + Remove |sender| from |property|'s internal observer list + and send back a reply following the Protocol Bindings. +
    12. +
    +
    +
    -
    -

    Constructing {{ExposedThing}}

    +
    +

    The PropertyWriteHandler callback

    - The {{ExposedThing}} interface extends {{ConsumedThing}}. It - is constructed from a full or partial {{ThingDescription}} object. + A function that is called when an external request for writing a + Property is received and defines what to do with such requests. + Takes as argument |value:InteractionOutput| and returns a {{Promise}}, + resolved when the value of the Property - identified by the name + provided when setting the handler has been updated -, or rejects + with an error if the property is not found or the value cannot be updated. +

    +

    + Note that the code in this callback function can read the property before updating it in order to find out the old value, if needed. Therefore the old value is not provided to this function.

    - Note that an existing {{ThingDescription}} object can be optionally modified (for instance by adding or removing elements on its |properties|, |actions| and |events| internal properties) and the resulting object can used for constructing an - {{ExposedThing}} object. This is the current way of adding and - removing Property, Action and Event definitions, as illustrated in the examples. + The value is provided by implementations as an {{InteractionOutput}} object + in order to be able to represent values that are not described by a + DataSchema, such as streams. +

    +
    + +

    The setPropertyWriteHandler() method

    +

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

    - Before invoking expose(), the {{ExposedThing}} object does not serve any requests. This allows first constructing {{ExposedThing}} and then initialize its Properties and service handlers before starting serving requests. + 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.

    - To construct an {{ExposedThing}} with the {{ThingDescription}} - |td:ThingDescription|, run the following steps: + When the method is invoked given |name:string| and + |handler:PropertyWriteHandler|, implementations MUST run the + following steps:
    1. If invoking this method is not allowed for the current scripting context for security reasons, [= exception/throw =] a {{SecurityError}} and abort these steps.
    2. - Run the expand a TD steps on |td|. If that fails, re-[= exception/throw =] the error and abort these steps. + Let |interaction| be the value of ||td||'s |properties|'s + |name|.
    3. - Let |thing:ExposedThing| be a new {{ExposedThing}} object. + If a Property interaction with |name| is not found, + [= exception/throw =] {{NotFoundError}} and abort these steps.
    4. - Let ||td|| be an internal slot of |thing| and let |td| be its value. + Set the internal slot ||writeHandler|| on |interaction| + to |handler|. +
    5. +
    +
    +
    + +

    Handling requests for writing a Property

    +
    + When a network request for writing a Property |name:string| + with a new value |value:InteractionOutput| and |options:InteractionOptions| + is received, implementations MUST run the following + update property steps, given |name|, |value|, |options| + and |mode| set to `"single"`: +
      +
    1. + If this operation is not supported, send back a {{NotSupportedError}} + according to the Protocol Bindings and abort these steps.
    2. - Return |thing|. + If this operation is not allowed, send back a {{NotAllowedError}} + according to the Protocol Bindings and abort these steps. +
    3. +
    4. + Let |interaction| be the value of ||td||'s |properties|'s + |name|. +
    5. +
    6. + If a Property with |name| does not exist, return a {{NotFoundError}} in the reply and abort these steps. +
    7. +
    8. + Let |handler:function| be `null`. +
    9. +
    10. + If there is a user provided internal slot ||writeHandler|| + on |interaction|, let |handler| be that. +
    11. +
    12. + Otherwise, if there is a default write handler provided by the implementation, let |handler| be that. +
    13. +
    14. + Otherwise, if |handler| is `null`, send back a {{NotSupportedError}} + with the reply and abort these steps. +
    15. +
    16. + Let |promise| be the result of invoking |handler| with |name| and + |options|. If it fails, return the error in the reply and abort these steps. +
    17. +
    18. + If |mode| is `"single"`, reply to the request reporting success, following the Protocol Bindings and abort these steps.
    -
    -

    Methods inherited from {{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 {{ConsumedThing}} interface in an {{ExposedThing}} provide the default methods to interact with the {{ExposedThing}}. -

    -

    - After constructing an {{ExposedThing}}, 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 expose() on the {{ExposedThing}} in order to start serving external requests. -

    + +

    Handling requests for writing multiple Properties

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

    The PropertyReadHandler callback

    +
    +

    The ActionHandler 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 the value of the Property matching the |name:string| argument is obtained, or rejects with an error if the property is not found or the value cannot be retrieved. + A function that is called when an external request for invoking an + Action is received and defines what to do with such requests. + It is invoked with |params:InteractionOutput| and optionally + with an |options:InteractionOptions| object. + It returns a {{Promise}} that rejects with an error or resolves with + the value returned by the Action as {{InteractionInput}}. +

    +

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

    -

    The setPropertyReadHandler() method

    +

    The setActionHandler() method

    - Takes |name:string| as string argument and |readHandler:PropertyReadHandler| as argument of type PropertyReadHandler. Sets the service handler for reading the specified Property matched by |name|. Throws on error. Returns a reference to |this| object for supporting chaining. + Takes as arguments |name:string| and |action:ActionHandler|. + Sets the handler function that defines what to do when a request is + received to invoke the Action matched by |name|. + Throws on error. + Returns a reference to |this| object for supporting chaining.

    - The |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. + 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 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. + There MUST be at most one handler for any given Action, so newly added handlers MUST replace the previous handlers.

    -
    - -

    Handling Property read requests

    - When a network request for reading Property |propertyName:string| is received by the implementation, run the following steps: + When the method is invoked given |name:string| and |action:ActionHandler|, + run the following steps:
    1. - If a Property with |propertyName| does not exist, return a {{ReferenceError}} in the reply and abort these steps. + If invoking this method is not allowed for the current scripting context for security reasons, [= exception/throw =] a {{SecurityError}} and abort these steps.
    2. - If there is a user provided read handler registered with setPropertyReadHandler(), invoke that given |propertyName|, return the obtained value in the reply and abort these steps. + Let |interaction| be the value of ||td||'s |actions|'s + |name|.
    3. - Otherwise, if there is a default read handler provided by the implementation, invoke it with |propertyName|, include the returned |value| with the reply and abort these steps. + If an Action with name |name| is not found, + [= exception/throw =] a {{NotFoundError}} and abort these steps.
    4. - if there is no default handler defined by the implementation, return a {{NotSupportedError}} with the reply and abort these steps. + Set the internal slot ||actionHandler|| on |interaction| + to |action|.
    -

    Handling Property observe requests

    +

    Handling Action requests

    - When a network request for observing a Property |propertyName:string| is received by the implementation, run the following steps: + When a network request for invoking the Action identified by |name:string| is received with |inputs| and optionally with + |options:InteractionOptions|, run the following steps:
    1. - If a Property with |propertyName| does not exist, return an error in the reply (as defined in the - Thing Description) and abort these steps. + If this operation is not supported, send back a {{NotSupportedError}} + according to the Protocol Bindings and abort these steps. +
    2. +
    3. + If this operation is not allowed, send back a {{NotAllowedError}} + according to the Protocol Bindings and abort these steps. +
    4. +
    5. + Let |interaction| be the value of ||td||'s |properties|'s + |name|. +
    6. +
    7. + If an Action identified by |name| does not exist, return a {{NotFoundError}} in the reply and abort these steps. +
    8. +
    9. + Let |handler:function| be `null`. +
    10. +
    11. + If there is a user provided internal slot ||actionHandler|| + on |interaction|, let |handler| be its value. +
    12. +
    13. + If |handler| is `null`, return a {{NotSupportedError}} with the reply + created by following the Protocol Bindings and abort these steps. +
    14. +
    15. + Let |promise| be the result of invoking |handler| with |name|, |inputs| and |options|.
    16. - Save the request sender information to the Property's internal observer list in order to be able to notify about Property value changes. + If |promise| rejects, send the error with the reply and abort these + steps. +
    17. +
    18. + When |promise| resolves with |data:InteractionInput|, use |data| to + create and send the reply according to the Protocol Bindings.
    -
    -

    The PropertyWriteHandler callback

    +
    +

    The EventListenerHandler 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:string| argument has been updated with |value|, or rejects with an error if the property is not found or the value cannot be updated. + 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 {{InteractionInput}} value + that represents the Event data, or rejects with an error.

    -

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

    + Applications MAY return {{ReadableStream}} from an {{EventListenerHandler}} + Implementations will then use the stream provided in + {{InteractionOutput}} when constructing the event notification.

    -

    The setPropertyWriteHandler() method

    +
    +

    The EventSubscriptionHandler callback

    - Takes |name:string| as string argument and |writeHandler:PropertyWriteHandler| - as argument of type PropertyWriteHandler. Sets the service handler - that will be used for serving write requests for writing the specified - Property matched by |name|. Throws on error. + 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:InteractionOptions| 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. +

    +
    + +

    The setEventSubscribeHandler() method

    +

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

    -

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

    + 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 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. + There MUST be at most one event subscribe handler for any given + Event, so newly added handlers MUST replace the previous handlers.

    +
    + When the method is invoked given |name:string| and + |handler:EventSubscriptionHandler|, run the following steps: +
      +
    1. + If invoking this method is not allowed for the current scripting context for security reasons, [= exception/throw =] a {{SecurityError}} and abort these steps. +
    2. +
    3. + Let |interaction| be the value of ||td||'s |events|'s + |name|. +
    4. +
    5. + If an Event with the name |name| is not found, + [= exception/throw =] a {{NotFoundError}} and abort these steps. +
    6. +
    7. + Set the internal slot ||subscribeHandler|| on |interaction| + to |handler|. +
    8. +
    9. + Return `this`. +
    10. +
    +
    -

    Handling Property write requests

    +

    Handling Event subscribe requests

    - When a network request for writing a Property |propertyName:string| - with a new value |value| is received, implementations SHOULD run - the following update property steps, given - |propertyName|, |value| and |mode| set to `"single"`: + When an Event subscription request for |name| is received by the + underlying platform with optional |options:InteractionOptions|, + run the following steps:
    1. - If a Property with |propertyName| does not exist, return a {{ReferenceError}} in the reply and abort these steps. + If this operation is not supported, send back a {{NotSupportedError}} + according to the Protocol Bindings and abort these steps. +
    2. +
    3. + If this operation is not allowed, send back a {{NotAllowedError}} + according to the Protocol Bindings and abort these steps. +
    4. +
    5. + Let |interaction| be the value of ||td||'s |events|'s + |name|. +
    6. +
    7. + If an Event with the name |name| is not found, send back a + {{NotFoundError}} and abort these steps. +
    8. +
    9. + If |name| has an associated ||subscribeHandler|| internal slot, + invoke it with |options| and abort these steps.
    10. - If there is a user provided write handler registered with setPropertyWriteHandler(), or if there is a default write handler, + Otherwise, if no ||subscribeHandler|| is defined, then implement the + default subscriber mechanism:
      1. - Invoke the handler with |propertyName|. If it fails, return the error in the reply and abort these steps. -
      2. -
      3. - Otherwise, if |mode| is `"single"`, reply to the request with the new value, following to the Protocol Bindings. + Let |subscriber| be a tuple formed of |options| (from which + |uriVariables| and |data| may be used) and the + subscriber information needed to create an Event + notification response.
      4. - For each item stored in the internal observer list of the Property with |propertyName|, send an observe reply with the new value attached. + Add |subscriber| to the internal listener list of |interaction|.
    11. -
    12. - If there is no handler to handle the request, return a {{NotSupportedError}} in the reply and abort these steps. -
    +
    + +

    The setEventUnsubscribeHandler() method

    +

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

    +

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

    +

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

    - When a network request for writing multiple Properties given in an object |propertyNames| is received, run the following steps: + When the method is invoked with |name:string| and + |handler:EventSubscriptionHandler|, run the following steps:
    1. - For each property with key |name| and value |value| defined in |propertyNames|, run the update property steps with |name|, |value| and |mode| set to `"multiple"`. + If invoking this method is not allowed for the current scripting context for security reasons, [= exception/throw =] a {{SecurityError}} and abort these steps. +
    2. +
    3. + Let |interaction| be the value of ||td||'s |events|'s + |name|. +
    4. +
    5. + If an Event with the name |name| is not found, + [= exception/throw =] a {{NotFoundError}} and abort these steps. +
    6. +
    7. + Set the internal slot ||unsubscribeHandler|| on |interaction| + to |handler|.
    8. - Reply to the request (by sending a single or multiple replies) according to the Protocol Bindings defined for the Property. + Return `this`.
    -
    -

    The ActionHandler callback

    -

    - A function that is called when an external request for invoking an - Action is received and defines what to do with such requests. - It is invoked with a |params:object| dictionary argument. - It returns a {{Promise}} that rejects with an error or resolves if - the action is successful. -

    +

    Handling Event unsubscribe requests

    +
    + When an Event unsubscribe request for |name| is received by the + underlying platform optionally with |options:InteractionOptions|, + run the following steps: +
      +
    1. + If this operation is not supported, send back a {{NotSupportedError}} + according to the Protocol Bindings and abort these steps. +
    2. +
    3. + If this operation is not allowed, send back a {{NotAllowedError}} + according to the Protocol Bindings and abort these steps. +
    4. +
    5. + Let |interaction| be the value of ||td||'s |events|'s + |name|. +
    6. +
    7. + If an Event with the name |name| is not found, send back a + {{NotFoundError}} and abort these steps. +
    8. +
    9. + If |name| has an associated ||unsubscribeHandler|| + internal slot that is a function, + invoke it with |options| and abort these steps. +
    10. +
    11. + Otherwise let |subscriber| be the tuple saved in |interaction|'s + internal listener list. +
    12. +
    13. + Remove |subscriber| from |interaction|'s + internal listener list. +
    14. +
    15. + Return `this`. +
    16. +
    +
    -

    The setActionHandler() method

    +

    The setEventHandler() method

    - Takes |name:string| as string argument and |action:ActionHandler| as argument of type ActionHandler. Sets the handler function for the specified Action matched by |name|. May throw an error. Returns a reference to |this| object for supporting chaining. + Takes as arguments |name:string| and |eventHandler:EventListenerHandler|. + Sets the event handler function for the specified Event + matched by |name|. + Throws on error. + Returns a reference to |this| object for supporting chaining.

    - The |action| callback function will implement an Action and SHOULD be called by implementations when a request for invoking the Action is received from the underlying platform. + The |eventHandler| callback function will implement what to do when the + event is emitted. It SHOULD resolve with a value that represents the + Event data, or reject with an error.

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

    +
    + When the method is invoked with |name:string| and + |eventHandler:EventListenerHandler|, run the following steps: +
      +
    1. + If invoking this method is not allowed for the current scripting context for security reasons, [= exception/throw =] a {{SecurityError}} and abort these steps. +
    2. +
    3. + Let |interaction| be the value of ||td||'s |events|'s + |name|. +
    4. +
    5. + If an Event with the name |name| is not found, + [= exception/throw =] a {{NotFoundError}} and abort these steps. +
    6. +
    7. + Set the internal slot ||eventHandler|| of |interaction| to + |eventHandler|. +
    8. +
    9. + Return `this`. +
    10. +
    +
    -

    Handling Action requests

    +

    Handling Events

    - When a network request for invoking the Action identified by |name:string| is received, the runtime SHOULD execute the following steps: + When an Event with name |name| is emitted with + |data:InteractionInput| either by the underlying platform or by the + emitEvent() method, run the + following steps:
    1. - If an Action identified by |name| does not exist, return a {{ReferenceError}} in the reply and abort these steps. + Let |interaction| be the value of ||td||'s |events|'s + |name|.
    2. - If there is a user provided action handler registered with setActionHandler(), invoke that wih |name|, return the resulting |value| with the reply and abort these steps. + If |data| is not defined or `null`, +
        +
      1. + Let |eventHandler| be the value of the ||eventHandler|| + internal slot of |interaction|. +
      2. +
      3. + If |eventHandler| is failure, abort these steps. +
      4. +
      5. + Let |data| be the result of awaiting to resolve the invocation + of |eventHandler|. If it rejects, abort these steps. +
      6. +
    3. - Otherwise return a {{NotSupportedError}} with the reply and abort these steps. + For each |subscriber| in the internal listener list of + |interaction|, run the following sub-steps: +
        +
      1. + Create an Event notification |response| according to the + Protocol Bindings from |data| and |subscriber|, including + its |options|. +
      2. +
      3. + Send |response| to the subscriber identified by |subscriber|. +
      4. +
    @@ -1783,16 +2678,28 @@

    The ActionHandler callback

    The emitEvent() method

    - Takes a |name:string| argument denoting an Event name, and a |data:any| argument of `any` type. The method MUST run the following steps: + Takes as arguments |name:string| denoting an Event name and + |data:InteractionInput|. + Triggers emitting the Event with the given data. + The method MUST run the following steps:
    1. If invoking this method is not allowed for the current scripting context for security reasons, [= exception/throw =] a {{SecurityError}} and abort these steps.
    2. - If an Event with the name |name| is not found, [= exception/throw =] a {{NotFoundError}} and abort these steps. + Let |interaction| be the value of ||td||'s |events|'s |name|.
    3. - Make a request to the underlying platform to send an Event with |data| attached as property, using the Protocol Bindings, then abort these steps. + If an Event with the name |name| is not found, + [= exception/throw =] a {{NotFoundError}} and abort these steps. +
    4. +
    5. + Make a request to the underlying platform to emit an Event + with |data|, using the Protocol Bindings. +

      + This will trigger the handling events + steps. +

    @@ -1816,9 +2723,16 @@

    The ActionHandler callback

    reject |promise| with a {{TypeError}} and abort these steps.
  • - For each Property definition in ||td||, initialize an |internal observer list| internal slot + For each Property definition in ||td||'s |properties|, + initialize an |internal observer list| internal slot in order to store observe request data needed to notify the observers on value changes.
  • +
  • + For each Event definition is ||td||'s |events|, + initialize an |internal listener list| internal slot + in order to store subscription request data needed to notify the + Event listeners. +
  • Set up the WoT Interactions based on introspecting ||td|| as explained in [[!WOT-TD]] and [[!WOT-PROTOCOL-BINDINGS]].