Closed mkovatsc closed 5 years ago
Mozilla's proposal (developed from the JSON-TD proposal but using the new links format) is to use top level links to properties, actions and events resources for this purpose, e.g.
"links": [
{
"rel": "properties",
"href": "/things/lamp/properties"
},
{
"rel": "actions",
"href": "/things/lamp/actions"
},
{
"rel": "events",
"href": "/things/lamp/events"
}
]
The properties resource lets you get all property values at once, the actions resource is an action queue and the events resource is an event log. See https://github.com/mozilla-iot/wot/issues/82 for a summary of Property
, Properties
, Action
, Actions
, Event
and Events
resources.
I'd argue this is another good reason for standardising on "links" rather than "forms" for properties, actions and events.
Do others have this use case or opinions? @ToruKawaguchi @mlagally cloud service?
Yes. It's also common in cloud service for home appliances.
yes, we also have this use case.
There is a corresponding concept in nearly every similar API (not only in IoT). It can be associated with getting the current representation (state) of the Thing.
One way to do it is by synchronizing the local proxy Thing object with the remote original Thing object (in other words, the protocol details are encapsulated, and it can be optimized in the background whether all properties need a fresh read, or only some). So you call e.g. a synchronize()
method, then properties
, actions
and events
will be up to date.
Another way to do it is by making explicit requests for:
In general, this sounds like (standardized) actions on a Thing, so we could say this belongs to the Thing application interface. If someone defines these actions, fine, but it's also fine to not implement them.
Otherwise, we can also view them as new interactions that have protocol bindings (for e.g. fetching all properties), in which case we could extend the functionality of the fetch()
method on the wot
object, by providing a URL, a fetch type (TD, properties, action log, event log) and eventually a name for the property, action or event.
The question is what is a better interface:
As a developer, I'd surely prefer synchronizing the object and working with one representation. Also, it is more efficient (if constrained devices are in the picture).
Links currently do not have security fields, and this is something we need to allow to specify in links.
Interesting that there is so much sudden agreement and nobody brought it up before :D Good, let's do this!
Internally, we have a solution that is in line with @benfrancis description.
Overall, it is a convenience feature that a device may support or not. So it is not a common element across platforms like Properties, Actions, Events, but an abstraction above. Thus, both sides need to have a common understanding of this concept. Thus, links with a special relation type could be a good way to do it -- compare with a stylesheet for a website: both sides need to understand the concept, the server tells where to get it, and the client knows how to get it and what to do with it.
However, it would be good to collect use cases of concrete platforms that support this feature, but require a concrete request to do so, which can be described with our forms
vocabulary. OPC UA is one candidate. In this case, a standardized Action might also be a good way forward, as it does not extend the interaction patterns with a a higher-layer concept as described above.
Are there any comments on this? @mjkoster Does OCF have a mechanism for this?
@takuki Links currently do not have security fields, and this is something we need to allow to specify in links.
Web Linking in general does not have this. Also the recent update from 5988 to 8288 did not take this into account.
Links usually point to related Web resources. The Thing usually does not have authority over those, and hence (I would say) should not define their security.
For such links to special resources on the Thing itself, security should apply, though. Here I feel a slight mismatch between links and the special interactions such as reading all Properties...
Here are some more or less random thoughts that sort of converge.
Since TD points to instances, there needs to be an underlying protocol mechanism that exposes an instance of a resource that aggregates the property resources.
In OCF, LWM2M, and other similar use cases this is implemented with a collection pattern.
We discussed collection patterns a few times previously in WoT but no one seemed to be interested in adding the pattern at the time. The closest use case was the array of LEDs, etc.
Therefore, I see the question as "how do we model collections in TD"?
Typically in the underlying implementation there is a collection schema that includes the individual elements, with some sort of differentiating identifiers, in an overall array structure. OCF and LWM2M are good examples of this. The entity which processes the representation needs to be able to identify the individual items in the collection representation. In OCF we use "href" per item, and in LWM2M there is an object ID and instance identifier.
Maybe some implementations would not be able to include all properties in the collection, so we probably will want to be able to describe what is included. Some implementations may expose more than one collection.
We could use semantic annotation in the TD to describe what is included in any given collection.
It is beginning to sound like a collection should be modeled with an interaction, like either a property or an action. It could have some schema that describes what is in it using the property identifiers and semantic annotation.
This pattern could be used to describe both "native" wot server resources which we can design, and other ecosystems like OCF, etc, which we can't design.
PS I think the link security metadata discussion should be moved to a new issue.
In my WebSocket based protocol, I allowed for updates to all properties with one message, or updates for a single property. The former is used, for example, when initialising a client thing.
In respect to collections of things, I allow for things as first class types, which in practice, means the passing the URI for the thing description. You can then have collections of things modelled as an array of such URIs. Alternatively, you can use a property per thing. My implementation for the web of things platform automatically creates the software objects from these URIs, dealing with cyclic dependencies between things. A use case is where a thing acts as a coordinator for other things, e.g. a hotel room thing that coordinates the things in that room, including the lights, door lock, door key sensor and so forth.
We tried to get a common understanding of the use case and possible solutions at the Bundang F2F Meeting. To summarize:
Possible ways forward:
properties
field of the Thing without the forms
fields)properties
field as DataSchema properties
(cf. recursive Properties)
forms
field at the Thing level to describe the request (e.g., for required headers or mediaType)links
field entry with a special rel
attribute that identifies the target resource as a Property where to get all Property values
TODO:
Alternatively a specially annotated Action might be used, as a FETCH with specific input media type
I like that approach. We could define what the state of a Thing is (e.g. the implicit collection of properties and perhaps other data represented as an object), then there could be an implicit action to fetch the state, analogous to e.g. OCF resource representation. Eventually we could also set that state (as Dave mentioned). The TD already defines the data schema for all properties, no need to repeat the info, they are identified by the property names in the state object.
Use links field entry with a special rel attribute that identifies the target resource as a Property where to get all Property values
Just to re-iterate, this is Mozilla's preferred approach, as described in https://github.com/w3c/wot-thing-description/issues/151#issuecomment-395150076
Links are not forms, so TD cannot provide metadata for how the read request must be created (e.g., required headers, special method such as FETCH, ...)
A concrete protocol binding could define this (e.g. define that an HTTP GET on a properties URL will return all property values in JSON).
Thing Description:
{
"name":"My Lamp",
"properties": {
"on": {
"label": "On/Off",
"type": "boolean",
"description": "Whether the lamp is turned on",
"links": [
{
"href": "/things/lamp/properties/on",
"mediaType": "application/json"
}
]
},
"level" : {
"label": "Level",
"type": "number",
"description": "The level of light from 0-100",
"minimum" : 0,
"maximum" : 100,
"links": [
{
"href": "/things/lamp/properties/level",
"mediaType": "application/json"
}
]
}
},
"links": [
{
"rel": "properties",
"href": "/things/lamp/properties",
"mediaType": "application/json"
}
]
}
Request:
GET http://mythingserver.com/things/lamp/properties
Accept: application/json
Response:
200 OK
{
"on": true,
"level": 50
}
https://github.com/w3c/wot-scripting-api/issues/132 started a discussion on how such a feature could be used by the Scripting API.
As Matthias pointed out we discussed a similar (same?) issue about reading/writing multiple properties in Scripting also.
When we started the WoT work I had the impression that IF someone needs multiple read/write one was forced to use composed types.
For example let's use a light. This specific light is able to be set R, G and B value. Moreover, this specific light also allows for setting R, G and B value at the same time. This can be done by modelling it as a composed type.
"rgb": {
"r" : 255,
"g" : 200,
"b" : 0
}
Now it seems we want to have something that allows for setting R, G and B at the same time by "labelling" the 3 properties somewhat differently.
I wonder why we actually need that and why we cannot "(re-)use" the initial grouping (composed types) to solve this issue.
Let's suppose a protocol like MODBUS allows setting multiples values at once. Why don't simply add an additional property in the TD with the composed values? The thing itself knows its protocols and can decide whether the thing description provides such a "multiple/composed" property.
Do I miss anything here?
I have similar trouble understanding some of the directions proposed around the topic.
If a Thing wants a set of Properties to be read or generally treated atomically and has protocol support to do so (including putting them into one resource representation), it shall put them together in one object- or array-type Property. This is straightforward grouping how it is done naturally already in other ecosystems and most of our PlugFest systems.
The only issue we should look at is when a Thing supports a special read- or write-multiple protocol operation, where independent Properties can be interacted with simultaneously for efficiency reasons.
Modbus for instance does not allow arbitrary read-/write-multiple, but only adjacent registers. Usually the programmer of such a thing puts Properties in adjacent registers when they are supposed to be treated together. This could be reflected in the the TD as also outlined by Daniel by having additional composite Properties. Maybe we are only looking for an efficient way to omit data schema redundancy... we still need to solve the $ref issue anyway!
Another solution for protocols that require dependent properties to be set at the same time as the property they are dependent on, is to explicitly declare the dependency as part of the thing description, preferably as part of the protocol binding since this is likely to protocol dependent given that it is defined for modbus or OCF etc. In other words the metadata for a given property would include a triple whose subject is that property, predicate is "dependent-on" and object is the corresponding property this property depends upon.
I like the idea @danielpeintner pointed out about the use of composite type. On the other hand, not all composite types requires atomic updates, either. Therefore, I think it also makes sense to provide a way to explicitly mark them so that it is clear to WoT clients that an updates of the member fields need to be done all at once.
I think it is also a question of how open you want to be for the clients and how you can increase the acceptances of your service. Composite type definitions always lead to the expectation that clients have to handle it, especially its content that can be quite complex.
I would also prefer to introduce a kind of flag to identify properties which have a kind of dependency (e.g., RGB values, GPS coordinates, etc). This flag can be a group container within the properties, e.g.:
{
"properties": {
"groups": [
{
"r": {"type": "integer", "links": [{ "href": "..."}]},
"g": {"type": "integer", "links": [{ "href": "..."}]},
"b": {"type": "integer", "links": [{ "href": "..."}]}
},
{
"longitude": {"type": "number", "links": [{ "href": "..."}]},
"latitude": {"type": "number", "links": [{ "href": "..."}]}}
],
"temperature": {"type": "number", "links": [{ "href": "..."}]}
}
}
Reads can be done individually (if desired), however, writings have to take care all properties within a group to avoid inconstancy.
Both IKEA Tradfri and Philips Hue APIs have a "state" property that exposes most/all of the operational properties. This is easy to handle in TD since it is just another interaction.
"properties": {
"state": {
"type": "object",
"properties": {
"state": {
"type": "object",
"properties": {
"on": {
"@type": ["iot:SwitchStatus", "iot:SwitchData"],
"type": "boolean"
},
"bri": {
"@type": ["iot:LevelData", "iot:CurrentLevel"],
"type": "number",
"minimum": 0,
"maximum": 254
},
"hue": {
"@type": ["iot:HueData", "iot:CurrentHue"],
"type": "number",
"minimum": 0,
"maximum": 65535,
"iot:ScaleUnits": "iot:ColorHueDegrees",
"iot:ScaleMinimum": 0,
"iot:ScaleMaximum": 360
},
"sat": {
"@type": ["iot:SaturationData", "iot:CurrentSaturation"],
"type": "number",
"minimum": 0,
"maximum": 254
},
"ct": {
"@type": ["iot:CurrentColorTemperature", "iot:ColorTemperatureData"],
"type": "number",
"iot:Units": "iot:Mired",
"minimum": 153,
"maximum": 500,
"iot:ScaleUnits": "iot:DegreesKelvin",
"iot:ScaleMinimum": 6500,
"iot:ScaleMaximum": 2000
}
}
}
},
"forms": [
{
"href": "lights/24",
"mediaType": "application/json"
}
]
},
For things that are updated together, there are other interactions that can be specified in TD:
"setlevel": {
"label": "SetLevel",
"@type": ["iot:SetLevelAction"],
"description": "Action to change the brightness of the light",
"input": {
"type": "object",
"properties": {
"bri": {
"@type": ["iot:LevelData"],
"type": "integer",
"minimum": 0,
"maximum": 254
},
"transitiontime": {
"@type": ["iot:TransitionTimeData"],
"type": "number",
"minimum": 0,
"maximum": 65535,
"iot:units": "iot:Seconds",
"iot:UnitScaleFactor": 0.1
}
}
},
The grouping will be done at the data property level, as shown above, in many APIs.
If we want to provide a new pattern, it would of course only apply to WoT exposed things. In this case, maybe a collection if links, each link pointing to an interaction, and a "batch" read or write, as OCF provides, would be a solution. This would be similar to the Mozilla proposal, but with explicit links.
An application could possibly select a subset of the result set by including link attributes in a filter expression.
This could work as a general grouping mechanism. A new interaction consisting of a link collection, or index, might be added. It may be useful to include more then one in a TD, or even make a TD that only contains link collections.
We would probably need to extend the path with a JSON pointer syntax like
{"href": "http://0m2m.net/td/f3ca255f/properties/switch"}
and maybe add queries...
[
{"href": "http://0m2m.net/td/f3ca255f/properties/?label%3Donoff"},
{"href": "http://0m2m.net/td/f3ca255f/properties/?@type%3Diot%3Atemperature"}
]
@mjkoster wrote:
In this case, maybe a collection if links, each link pointing to an interaction, and a "batch" read or write, as OCF provides, would be a solution. This would be similar to the Mozilla proposal, but with explicit links.
With a Thing Description like the one below:
{
"name":"Web Lamp",
"description": "A web connected lamp",
"properties": {
"on": {
"label": "On/Off",
"type": "boolean",
"links": [{"href": "/things/lamp/properties/on"}],
},
"brightness": {
"label": "Brightness",
"type": "number",
"links": [{"href": "/things/lamp/properties/brightness"}],
},
"color": {
"label": "Color",
"type": "string",
"links": [{"href": "/things/lamp/properties/color"}],
}
},
"links": [
{
"rel": "properties",
"href": "/things/lamp/properties"
}
]
}
If you want to set a single property, you can PUT to the property resource URL provided in the property object:
PUT/things/lamp/properties/on
{
"on": false
}
If you want to set multiple properties at the same time, you can PUT to the properties resource URL provided in the links member:
PUT /things/lamp/properties
{
"on": true,
"brightness": 30,
"color": "#ff0000"
}
If you want to set a subset of properties at the same time, you can send a PATCH request to the properties resource:
PATCH /things/lamp/properties
{
"brightness": 40,
"color": "#00ff00"
}
Note that CoAP also has a PATCH request, specified in RFC8132.
A webthing WebSocket subprotocol could also defined a setProperty
message which can set multiple properties at the same time:
{
"messageType": "setProperty",
"data": {
"brightness": 40,
"color": "#00ff00"
}
}
RGB and longitude/latitude don't seem like great examples of separate properties that have to be set together, as the individual components could be set separately. But if such a constraint does exist, that property could be specified as an object property:
{
"properties": {
"rgb": {
"type": "object",
"properties": {
"r": { "type": "number" },
"g": { "type": "number" },
"b": { "type: "number"}
}
}
}
}
}
A PUT request which doesn't specify all three components could return an error response.
Is there a use case that I'm missing?
Is there a use case that I'm missing?
It makes implicit assumptions about how the Things are implemented. For instance, from just the link, no one can know that PATCH is accepted and what the patch document format is. Furthermore, there is no way to express additional metadata that might be needed for the targeted Thing. These are requirements that WG Member Participants have.
One trade-off would be to introduce a top-level forms
field for meta-interactions. We can collect those and define a vocabulary for rel
(e.g., for reading all, reading multiple, writing multiple, running actions, historic access?).
I am not so sure if they are really meta. They could also be put into the actions
entry point and then annotated in their respective @type
. @zolkis had a preference for this approach (https://github.com/w3c/wot-thing-description/issues/151#issuecomment-402059225).
@mkovatsc wrote:
It makes implicit assumptions about how the Things are implemented. For instance, from just the link, no one can know that PATCH is accepted and what the patch document format is.
The fact that a PATCH
request can be used to make partial modifications to a resource is part of the HTTP specification (https://tools.ietf.org/html/rfc5789). If you want a client to be able to dynamically determine whether a given resource accepts a PATCH
request, then HTTP provides the OPTIONS
request for that purpose (https://tools.ietf.org/html/rfc7231#section-4.3.7). Note that whether a PATCH
request is accepted may also depend on the authentication credentials provided in the request and differ from user to user (some users may have read-only access). The body of that response could also theoretically document the types of payloads which are accepted.
But really any remaining implicit assumptions (such as the patch document format) are intended to be made explicit through a concrete protocol binding specification.
Furthermore, there is no way to express additional metadata that might be needed for the targeted Thing. These are requirements that WG Member Participants have.
Can you provide examples?
We can collect those and define a vocabulary for rel (e.g., for reading all, reading multiple, writing multiple, running actions, historic access?).
We've discussed this before, but those don't seem like good use cases of rel
to me. This is what HTTP verbs are for.
They could also be put into the actions entry point and then annotated in their respective @type.
IMO an action shouldn't be needed just to set properties.
If you want a client to be able to dynamically determine whether a given resource accepts a PATCH request, then HTTP provides the OPTIONS request for that purpose
We are fully aware of this. For automated planning and validation purposes we want to have this information a priori in the TD. A TD shall contain all information required to interact with a Thing. It is generally a requirement for industrial systems.
But really any remaining implicit assumptions (such as the patch document format) are intended to be made explicit through a concrete protocol binding specification.
Your requirement seems to be to omit such information in exchanged TDs. We can try to accomodate this just like other optimizations.
Before starting to omit information, we should ensure that the information fits into the underlying model. Think of it as the possibility of inlining the binding into the TD to have a formal definition instead of prose.
If you don't like forms, do you have a better proposal for formally describing a binding?
Can you provide examples?
Intel or in a broader sense OCF needs specific CoAP options. Panasonic as well as Fujitsu need to support Web services that have specific HTTP header fields. SmartThings integrates multiple existing echosystems that use different methods for writing state and even triggering actions (e.g., Hue, IKEA Trådfri). Oracle wants to describe their existing IoT Cloud Service product. Siemens needs to support several industrial systems that need such adaptions.
This is what HTTP verbs are for.
When did you add your last HTTP verb to the standard to give semantic information to a request?
@mkovatsc wrote:
If you don't like forms, do you have a better proposal for formally describing a binding?
It is tricky to agree on a solution until we agree on the scope and definition of the problem.
There are two requirements that I think are in contention:
Regarding no. 1, my opinion is that the scope of the Web of Things should be constrained to only web protocols, with non-web protocols requiring a gateway or software adapter to bridge them to the Internet and/or the web. The Web of Things can then complement other Internet of Things protocols by acting as an abstraction layer which links together multiple underlying IoT protocols and is constrained to a very small number of web protocols in order to enable ad-hoc interoperabilty. Like with the web of pages.
In practice I'm only aware of two common web protocols:
This intentionally excludes non-web protocols like MQTT, AMQP, Zigbee, Z-Wave and Bluetooth from the scope of the Web of Things layer (by abstracting on top of them, not by ignoring them) and significantly narrows the problem space. This doesn't prevent someone inventing a new, better, web protocol in future which could replace both HTTP and CoAP.
Regarding requirement no. 2, if requirement no. 1 is assumed then I think no. 2 is simply impossible. You can't possibly adequately describe any possible network protocol in a JSON file in a way that a client could magically adapt to use all those protocols without a person manually implementing support in the client for every protocol. This would result in a very fragmented "Web of Things" where only some web of things clients can communicate with some web of things devices.
If requirement no. 1 is dropped then no. 2 becomes more feasible. It's then a question of whether protocol bindings should be specified in a concrete protocol binding specification like the HTTP and WebSocket protocol bindings Mozilla is working on which would form a new standard, or whether the Thing Description should be able to describe any web-based API (like existing Intel/Panasonic/Fujitsu/Samsung/Oracle/Siemens etc. web services, and possibly OCF as well) and map it onto the Web of Things data model.
That sounds a lot like the Open API initiatve. We could try to explore whether it is possible to do that inside a Thing Description, as a potential middle ground. I can see how that could potentially work if the problem space is constrained to REST-like web services using HTTP and CoAP. It may not work so well for WebSockets which seems better suited to a concrete webthing sub-protocol specification (due its slightly strange and non-weblike nature!).
We have quite good and practical constraints for no. 1 and successfully tested this approach with a number of different existing IoT platforms and Web services.
The constraints are:
;
-- are also needed in some use cases; cf. CoAP content format)Our problem with no. 2 is that it provides no novelty over existing technology and standards, and does not help with the situation that device manufacturers and moreover integrators face: there are already too many declarative IoT platforms out there. It is counterproductive when the the W3C just adds yet another one.
Our problem with no. 2 is that it provides no novelty over existing technology and standards, and does not help with the situation that device manufacturers and moreover integrators face: there are already too many declarative IoT platforms out there. It is counterproductive when the the W3C just adds yet another one.
I think this boils down to a fundamental disagreement about the definition of the Web of Things and how to achieve interoperability. But maybe there is a way forward regardless. Let's see where the discussion goes in #179 as that could provide a solution to this issue too.
We have quite good and practical constraints for no. 1 and successfully tested this approach with a number of different existing IoT platforms and Web services.
The constraints are:
a URI must be used to identify the target of a request/message media types must be used to identify the format of the payload (actually content types, as we discovered that parameters -- the part after a ; -- are also needed in some use cases; cf. CoAP content format) only additional configuration parameters may be defined to be passed to the implementation (protocol stack), e.g., method, header fields, or options
This is a pretty good definition for what is a web protocol. I think we satisfy the definition of constraints for requirement no.1, to make no. 2 practical, as demonstrated in our plugfests to date.
I see more reuse of CoAP, HTTP, and MQTT over time, displacing dedicated protocols. Are there examples where new protocols are being created that would require more support in the general case?
Also I think we need to have a better discussion about the optionality of supported bindings. It's not all-or-nothing. I see no reason my smart home WoT gateway would be required to support BacNET in either concrete or declarative translation scenarios.
Another point to this discussion: with WoT Protocol Bindings we are describing a few distinct categories of adaptation.
I think we have struck a good balance in our current support for a few sensible options in these categories, which allow for adaptation to a broad range of commercial products and services.
Further constraints on these adaptations would IMO result in us taking a more prescriptive approach and a more competitive stance WRT other SDOs (see XKCD 927)
@mjkoster wrote:
This is a pretty good definition for what is a web protocol. I think we satisfy the definition of constraints for requirement no.1, to make no. 2 practical, as demonstrated in our plugfests to date.
Perhaps this is a good basis for a set of constraints about what a Web Thing Description should and shouldn't be expected to describe, to help narrow the problem space. Could we dictate in the Thing Description specification that WoT devices MUST expose their resources using individually addressable web URIs with standard URI schemes and defined media types (allowing for defaults)?
I see no reason my smart home WoT gateway would be required to support BacNET in either concrete or declarative translation scenarios.
I agree. A network gateway is by definition something which allows data to flow from one network to another, a WoT gateway can provide as many or as few adapters as it wants to bridge non-WoT networks to a WoT network. Not all gateways need to support the same set of protocols and we don't even need to standardise how those adapters work.
A network client on the other hand is something which communicates with a server, usually using a single (or a very small number) of protocols (e.g. HTTP/HTTPS, IMAP, CalDAV or IRC). In order for an application (e.g. a web app, desktop app or mobile app etc.) to act as a WoT client which can talk to any WoT device (such that devices are linkable and client-agnostic), WoT devices need to be exposed using a very small number of commonly known protocols and data formats.
Another point to this discussion: with WoT Protocol Bindings we are describing a few distinct categories of adaptation.
Transfer protocol adaptation; consisting of the scheme and options, and how interactions are > mapped to the transfer layer. This is the extension of links we call forms. Data structure adaptation; map vs. array, how JSON keys are named, how to parse, described by > DataSchema terms array, object, contains, properties, oneOf, allOf, anyOf, const, etc. Data type adaptation; described by DataSchema terms number, boolean, string, minimum, maximum, etc. plus extension for units and scale constraints
That is a good point. And a lot of the debate is around which of these should be prescribed in a specification vs. declared in a Thing Description. I agree there's a balance to be reached.
One good example of the declarative approach is the use of schema repositories to provide an extensible mechanism for defining shared sets of device capabilities (vs. trying to prescribe them all in one specification, which would be impossible). Another good example is the use of JSON Schema to describe primitive data constraints for properties, actions and events in a way that clients can understand.
But if we don't prescribe at least one new MIME type and at least some constraints as to which protocols and data formats can be used and how they are used, I argue it will be very difficult to create web-style ad-hoc interoperability.
Further constraints on these adaptations would IMO result in us taking a more prescriptive approach and a more competitive stance WRT other SDOs
I understand the concern. But in trying to entirely avoid competing with protocols and data formats at any level of the IP stack (i.e. being completely protocol agnostic, encoding agnostic and language-agnostic), I worry that the current Thing Description and Protocol Binding Template specifications are not being prescriptive enough to achieve any more interoperability than is already possible with the status quo. In which case, what is the point?
I think we all agree on the idea that the Web of Things is intended as an application layer for the Internet of Things. I suggest that application layer protocol needs to provide a common language for WoT clients to talk to WoT devices.
If we can restrict the protocols to only web protocols (so that devices are linkable) and provide enough information in a Thing Description for any WoT client to talk to any WoT device without requiring custom code (i.e. devices are client-agnostic), then that seems like a good basis for a Web of Things application layer.
I think the latest Thing Description specification is quite close, it just needs some clearer constraints (around using only web protocols) and some tweaks to its communication metadata format to be a bit more web-like (e.g. links, link relations and operations rather than forms and form relations).
see XKCD 927
This meme could of course be directed at basically any activity going on at the W3C! :)
Based on today's discussion in the TD call: @mkovatsc make a proposal for meta interaction until next TD meeting. This can be compared with the 'action' approach that defines to get all properties at once.
Looking into this again after getting some distance and discussing more on hypermedia concepts again (#179), I no actually am in favor of allowing forms
at the Thing level:
properties
, actions
, and events
structures.<link />
tags into its context ("the sum of representations currently rendered in the Web browser").properties
predicate from the Thing RDF resource...forms
, why not a Thing when it is a similar description resource?forms
at the Thing level describe the possible meta interactions, that is, interactions on Interactions such as reading all Properties, writing multiple Properties, but possibly also listing all running Actions or collecting all buffered/undelivered Events (or listing all active events when an Event is more than just data).Here is a slide set to illustrate what I am trying to explain (to be shown in TD call on 23 Nov 2018): wot-meta-interactions.pptx
I should add that forms
at the Thing level are intended to be optional. Most of these meta interactions are just optimizations and many platforms might not even provide them. (At the Interaction level, forms
are mandatory because what is the point of describing an Interaction with which a client cannot interact...)
I think the term "resource view" makes it too complicated. Slide 9 describes the meta-ops (meta-actions?) on a Thing:
Thing readall (e.g., GET) writeall (e.g., PUT) writemultiple (e.g., PATCH) readmultiple (e.g., FETCH) listinvokedactions ? listactiveevents ?
[Edited for removing points clarified in the TD call. Remaining comments below.]
IMHO implementing listinvokedactions
and listactiveevents
is complex and we don't really need them. We can already list all actions in a Thing. We could add a flag at Action level whether is it currently running, but a given script should already know which are the long-running actions, so this is not needed.
Also, we can already list all events and the script should know which events have subscribers.
It would make sense for multi-tenancy, i.e. when one client/script can list (and control) actions started by another client/script, but the complexity of ownership management and transfer has to be explored and we should ponder whether it's worth it.
Slight updates to the slides after the call: wot-meta-interactions-v2.pptx
Summery:
forms
)properties
member of the Thing to the validator)The Arena Web Hub already supports HTTPS GET and PUT on the set of properties. Can anyone provide an example for how to declare that the set of all properties can be retrieved with an HTTPS GET on the path "/things/thingID/properties" where thingID is replaced by the specific thing's ID?
OK, so I've tried to create a full example based on the proposal in the presentation:
{
"@context": "https://iot.mozilla.org/schemas/",
"@type": ["Light", "OnOffSwitch"],
"name": "My Lamp",
"description": "A web connected lamp",
"properties": {
"on": {
"@type": "OnOffProperty",
"type": "boolean",
"title": "On/Off",
"description": "Whether the lamp is turned on",
"forms": [{
"href": "/things/lamp/properties/on",
"op": ["readproperty", "writeproperty"],
"http:methodName": ["GET", "PUT"]
}]
},
"brightness" : {
"@type": "BrightnessProperty",
"type": "integer",
"title": "Brightness",
"description": "The level of light from 0-100",
"minimum" : 0,
"maximum" : 100,
"forms": [{
"href": "/things/lamp/properties/brightness",
"op": ["readproperty", "writeproperty"],
"http:methodName": ["GET", "PUT"]
}]
}
},
"actions": {
"fade": {
"@type": "FadeAction",
"title": "Fade",
"description": "Fade the lamp to a given level",
"input": {
"type": "object",
"properties": {
"level": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"duration": {
"type": "integer",
"minimum": 0,
"unit": "milliseconds"
}
}
},
"forms": [{
"href": "/things/lamp/properties/brightness",
"op": "invokeaction",
"http:methodName": "POST"
}]
}
},
"events": {
"overheated": {
"title": "Overheated",
"@type": "OverheatedEvent",
"type": "number",
"unit": "degree celsius",
"description": "The lamp has exceeded its safe operating temperature",
"forms": [{
"href": "/things/lamp/properties/brightness",
"op": ["subscribeevent"]
}]
}
},
"forms": [
{
"href": "/things/lamp/properties",
"op": ["readall", "writeall", "writemultiple"],
"http:methodName": ["GET", "PUT", "PATCH"]
},
{
"href": "/things/lamp/actions",
"op": ["invokeaction", "listinvokedactions"],
"http:methodName": ["POST", "GET"]
},
{
"href": "/things/lamp/events",
"op": "listactiveevents",
"http:methodName": "GET"
}
],
"links": [
{
"rel": "alternate",
"href": "wss://mywebthingserver.com/things/lamp",
"subProtocol": "webthing"
},
{
"rel": "alternate",
"mediaType": "text/html",
"href": "/things/lamp"
}
]
}
A few things that aren't clear to me from the Thing Description alone in this example:
ops
and http:methodNames
are provided in an array, how does the client know which op maps to which method (given the order of arrays is not guaranteed)?readall
op is performed? If JSON is assumed, is the collection of properties output as a map? An array? What does that look like exactly?writeall
op on a properties resource?invokeaaction
or listinvokedactions
op on an actions resource?listactiveevents
op on an events resource?Do we agree that providing a contentType
of application/json
isn't sufficient to answer these questions?
In the presentation it says:
Allow Links in Interaction Nodes? Should we allow links to model a default readproperty operation, which is when reading the Property can be done by simply dereferencing its URI? (this is a wishful ideal, but unfortunately not the default for deployed IoT devices, which is why we often need forms to construct more complex requests)
If we acknowledge that a property resource is in fact a resource linked from the root Thing Description resource and that "reading the Property can be done by simply dereferencing its URI" makes sense, then why not expand this from read to also include assumptions about write, create and delete?
If a web thing uses HTTP or CoAP correctly as per their specifications, quite a lot of assumptions like this can be made.
The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI.
The PUT method requests that the enclosed entity be stored under the supplied Request-URI.
The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line.
The DELETE method requests that the origin server delete the resource identified by the Request-URI.
The GET method retrieves a representation for the information that currently corresponds to the resource identified by the request URI.
The PUT method requests that the resource identified by the request URI be updated or created with the enclosed representation.
The POST method requests that the representation enclosed in the request be processed.
The DELETE method requests that the resource identified by the request URI be deleted.
Then the Thing Description can be simplified from having forms and links, to simply having links to resources, with the protocol specifications explaining what effect different protocol methods should have when performed on those resources.
{
"@context": "https://iot.mozilla.org/schemas/",
"@type": ["Light", "OnOffSwitch"],
"name": "My Lamp",
"description": "A web connected lamp",
"properties": {
"on": {
"@type": "OnOffProperty",
"type": "boolean",
"title": "On/Off",
"description": "Whether the lamp is turned on",
"links": [{"href": "/things/lamp/properties/on"}]
},
"brightness" : {
"@type": "BrightnessProperty",
"type": "integer",
"title": "Brightness",
"description": "The level of light from 0-100",
"minimum" : 0,
"maximum" : 100,
"links": [{"href": "/things/lamp/properties/brightness"}]
}
},
"actions": {
"fade": {
"@type": "FadeAction",
"title": "Fade",
"description": "Fade the lamp to a given level",
"input": {
"type": "object",
"properties": {
"level": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"duration": {
"type": "integer",
"minimum": 0,
"unit": "milliseconds"
}
}
},
"links": [{"href": "/things/lamp/actions/fade"}]
}
},
"events": {
"overheated": {
"title": "Overheated",
"@type": "OverheatedEvent",
"type": "number",
"unit": "degree celsius",
"description": "The lamp has exceeded its safe operating temperature",
"links": [{"href": "/things/lamp/actions/overheated"}]
}
},
"links": [
{
"rel": "properties",
"href": "/things/lamp/properties"
},
{
"rel": "actions",
"href": "/things/lamp/actions"
},
{
"rel": "events",
"href": "/things/lamp/events"
},
{
"rel": "alternate",
"href": "wss://mywebthingserver.com/things/lamp",
"subProtocol": "webthing"
},
{
"rel": "alternate",
"mediaType": "text/html",
"href": "/things/lamp"
}
]
}
This still isn't enough information on its own for a client to know what payloads to expect from those resources, but it seems like either way we still need some kind of separate concrete protocol binding specification or at least some kind of media type specification to provide that additional information.
It seems like the only reasons for using forms rather than links are:
How can a created action request resource (created with a POST) have its current state queried or be cancelled? How do you get the URL of that created resource?
Note that I've filed #302 to address this specific issue, which is a good example of something which is very hard to express in the Thing Description using the current declarative protocol binding approach.
Where multiple ops and http:methodNames are provided in an array, how does the client know which op maps to which method (given the order of arrays is not guaranteed)?
Exactly, that is why your example does not work. We had such array index arithmetic some time ago, because they seem to make it simpler, but actually, they make implementation more complex and only shorten the TD. So you would not put all the possible interactions into just a single form, but list them separately -- IF the default values do not apply (remember, you can omit that readproperty
uses a GET and only need to state explicitly http:methodName
to override the default value.
{
"name": ...
"forms": [{
"op": ["readall", "writeall"],
"href": "/things/lamp/properties",
"contentType": "application/json"
},{
"op": ["writemultiple"],
"href": "/things/lamp/properties",
"http:methodName": "PATCH",
"contentType": "application/merge-patch+json"
}],
...
}
PATCH might be a sensible choice for a writemultiple default value. I am not sure about "application/merge-patch+json". Since the content type is different from readall, writeall, we need a second form to state this. There are other platforms that use POST with application/json
to write multiple. Also, there are not many devices that support this at all (hence each form operation is optional).
What output is expected when a readall op is performed? If JSON is assumed, is the collection of properties output as a map? An array? What does that look like exactly?
A schema of type object with properties
equal to the the properties
member of the Thing (i.e., all Properties as defined, wrapped in a map). If a Thing does something weird here and uses a completely different format, the TD must have a Property that contains all data items and explicitly states the weird custom schema. (basically a penalty to encourage good implementation.)
What format is expected when performing a writeall op on a properties resource?
See above.
What format is expected when performing an invokeaaction or listinvokedactions op on an actions resource?
invokeaction
is at the normal Action level and remains unchanged.
Let's forget about listinvokedactions
for now if there is no constructive proposal or positive understanding. If someone has a custom way to list, use a Property.
What format is expected when performing a listactiveevents op on an events resource? How can a created action request resource (created with a POST) have its current state queried or be cancelled? How do you get the URL of that created resource?
See #302
Do we agree that providing a contentType of application/json isn't sufficient to answer these questions?
Yes, if interpret your writing correctly. For the running Actions questions see #302. For the Property questions see the answers above: the data schema is central and the content type only one aspect.
If we acknowledge that a property resource is in fact a resource linked from the root Thing Description resource and that "reading the Property can be done by simply dereferencing its URI" makes sense, then why not expand this from read to also include assumptions about write, create and delete?
You are mixing up metadata and data. Resources serving TDs and the hypothetical Interaction Descriptions are what we define here and we can make them behave like Web resources and can expect normal dereferencing to work. Devices from the real world unfortunately rarely behave like this, which is why integration and maintenance costs are so high, and why the active Member companies want Web of Things to support the use cases as chartered. This is why a link is not enough and we need a mechanism that describes how to construct the required request (e.g., with the OCF CoAP options, the cloud service x header fields, the POST instead of PUT to write, etc.).
Please, please try to understand that there are a number of companies that have several decades of experience with devices and system integration, and thus have these use cases, use cases for actual business. Just because Mozilla has not, we cannot render the intended standard worthless for all the active Member companies.
If a web thing uses HTTP or CoAP correctly as per their specifications, quite a lot of assumptions like this can be made.
The protocol standards do not have any such statements. This is unfortunately just wishful assumption on your side. And I understand you. I even fully agree that it could be so much simpler. Unfortunately, the real world is a mess and the deployed systems do not behave as you wish. We are trying to remedy this situation a bit by evolving from endless, tedious manual integration to semi-automated integration based on uniform descriptions for developers (instead of reading each specific standard and chasing the undocumented assumptions) and machine-understandable annotations for an even higher degree of automation.
Maybe you can acknowledge that the Web was not build in 2 years. Similarly, it takes time to educate developers and companies to build systems in a way we wish. A first step is to show what the benefits are when systems become easy to integrate, re-configure like mashups etc. For this it is a bit more complex at the moment with more detailed descriptions -- similar to the idiotic Web pages from the 90s/2000s.
@mkovatsc wrote:
The protocol standards do not have any such statements.
I was quoting directly from the specifications.
@benfrancis I'm going to jump in here because Hyper-Schema makes many of the assumptions that you are suggesting. But for the TD I agree with @mkovatsc - the real world is messy, and explicit and flexible is better than implicit and constrained.
With Hyper-Schema, we made a conscious choice to target only well-formed APIs. In the context of HTTP, this means APIs that follow the most clearly recommended HTTP semantics, which can be describe quite concisely. That excludes many (probably most) existing APIs that are out there today (well, sort-of, there are ways you can contort Hyper-Schema if you really want to describe them, but it gets messy fast).
The reason we did this is that there are already many excellent technologies (many of which use JSON Schema core and validation) that do a great job of describing operation-oriented APIs that are, um... aggressively casual about following the HTTP specifications 😁 Basically, we didn't want to compete head-on with OpenAPI, and decided to collaborate with them to support their JSON Schema usage while targeting a different type of API that gets needlessly verbose in the OpenAPI format. We're not pioneering HTTP-based API descriptions, so we don't have to cover the whole problem domain.
The TD is more analogous to OpenAPI at this stage- I'm guessing there are some thresholds of pathological behavior at which the TD throws up its hands and gives up, but the goal is to be inclusive.
One thing that we've started to recommend in JSON Schema for certain use cases in place of adding complicated new keywords is to instead generate schemas from some other format, as a preprocessing step (or more likely, as a build / publication step). Perhaps one solution might be to define (as a separate project) a "nice" TD format, which can be used to generate the corresponding verbose format.
(also, you were quoting from RFC 2616 which is quite out of date. I'm not sure what the exact policy is regarding obsoleted RFCs, but I would avoid depending on anything related to method semantics that is not present in RFC 7231. In particular, POST in 7231 implies fewer assumptions than in 2616.
Updated slides: wot-meta-interactions-2.pptx
Main changes / points:
properties
field of the Thing as DataSchema properties
field)read all is already compiled in the TD spec and was tested during the TestFest in Princeton
We have the use case of fetching all Property values at once, i.e., with a single request. This is very common in industrial systems, but also the Web recently picked up such ways to reduce round-trips (HTTP/2 PUSH/preload).
Do others have this use case or opinions? @ToruKawaguchi @mlagally cloud service? @mryuichi proxy service? @benfrancis @handrews Web perspective?
In case of yes, I currently cannot see a really nice solution:
forms
field to describe where and how to retrieve it?application/json
? Hmm, and if the Thing only does CBOR?@type
for a Property that would deliver all other Properties in one object. The DataSchema could default totype: object
with theproperties
at Thing level.