w3c / wot-thing-description

Web of Things (WoT) Thing Description
http://w3c.github.io/wot-thing-description/
Other
131 stars 63 forks source link

Missing event/notification affordance or operation #1323

Open mlagally opened 2 years ago

mlagally commented 2 years ago

In the context of the recent profile conversations it became apparent to me, that the TD is currently missing an important affordance.

When we have two servients that communicate via events, one is the event producer and the other the event consumer. The TD allows to describe the producer interface (subscribe/unsubscribe) but it is missing the notify affordance, i.e. an affordance that receives events.

This is the blocking point to describe Event consumers with TDs. As a workaround, the profile can define action endpoints actions/notify, however this is quite unnatural and an extension of the current TD draft is desirable.

egekorkan commented 2 years ago

Do you mean something like describing an endpoint of a server where the event notifications should be sent by the Thing? I sort of agree that this is missing but I do not know how we can describe it in the TD. TD describes the Thing for any Consumer in the end.

If this is coming from the WebHook perspective, I think that this kind of information should not be really visible in the WoT. Analogy with MQTT: The Thing is programmed with the certain topics it will publish or subscribe to. In the case of Webhook, it would be like having static URLs programmed into the Thing to send messages to. There can be mechanisms implemented in MQTT that allows the Thing to publish to certain topics however we do not have a standardized way.

There can be a bit of overlap with the opening connection mechanism that will hopefully come to TD 2.0? See https://github.com/w3c/wot-thing-description/issues/878, https://github.com/w3c/wot-thing-description/issues/1242, https://github.com/w3c/wot-thing-description/issues/1070

egekorkan commented 2 years ago

I would also say that this is not an affordance but an operation

mlagally commented 2 years ago

Do you mean something like describing an endpoint of a server where the event notifications should be sent by the Thing? I sort of agree that this is missing but I do not know how we can describe it in the TD. TD describes the Thing for any Consumer in the end.

Yes, I'm thinking of an endpoint of a server / servient. You are right, we probably do not need a new affordance, it could likely be just an additional operation in the current event model.

benfrancis commented 2 years ago

This is something I've noticed too when trying to describe anything other than a Thing as an HTTP server.

All of the current values for op describe messages sent from a Consumer to a Producer.

Messages sent from a Producer to a Consumer can't easily be described by a Form because Forms represent endpoints of a Producer (the Thing the Thing Description describes), not the Consumers that may consume it. As @egekorkan alluded to, even if a Consumer had a URI that could be used as the href member, that URI would likely be different for every Consumer so can't be described by a static Form in the Thing Description.

The Web Thing WebSocket API illustrates this problem too. Messages sent from the client to the server like setProperty, requestAction and addEventSubscription map obviously onto the operations writeproperty, invokeaction and subscribeevent, but the messages which are sent from the server to the client like propertyStatus, actionStatus and event have no corresponding operations. This is one of the reasons why the WebSocket sub-protocol can't be described declaratively by a protocol binding in a Thing Description using Forms and must rely on the subprotocol member of the Form to refer to out-of-band information (the sub-protocol specification).

For WebHooks, the Thing Description specification has an example which provides a workaround via the subscription member. It includes a data schema for subscription metadata sent by a Consumer in a subscribeevent operation which provides a callback URL for an individual subscription. This seems like a bit of a stretch to me, since it assigns a special meaning to the callbackURL and subscriptionID members which would have to be manually implemented in the Consumer. I noticed that @sebastiankb was working on updating that example in #1283 to use uriVariables instead (which could be another workaround) but I don't know whether those changes actually landed.

I don't really have a proposed solution to this problem as I think it's a consequence of choosing hypermedia Forms as the mechanism by which protocol bindings are defined. They are just naturally suited to describing endpoints on servers to which data can be sent from clients. It can probably only be worked around by adding additional metadata to Interaction Affordances (like subscription), or relying on out-of-band information (like subprotocol).

mlagally commented 2 years ago

I think there's a - not too difficult - solution for that.

The TD needs a way to describe a servient that exposes one or more server endpoints, i.e. acts as a message consumer. The Thing is a message producer and sends messages to the consumer. These can be used to receive notification messages, e.g. via WebHooks. It can be as simple as adding an new operation notify to the event affordance. It should take a message dataschema as input and provides a message-response dataschema as a result.

This endpoint can be used for observable properties and events in the same way.

A Servient in the message consumer role provides a protocol binding on that endpoint and communicates the URI of that endpoint to the message producer (thing) via the existing subscribe/unsubscribe operations.

I think it is really important to include a solution into the current TD draft before publication to ensure we can describe servients with a TD.

egekorkan commented 2 years ago

Could you write an example TD and an example on how it is consumed and used? In any case, I do not see it being implemented in the current TD spec and I have big doubts about implementing it at all, from my point of view it is not a Consumer-driven operation like all the other ones and it can be either invisible in WoT or happening in the configuration phase, like configuring MQTT broker to publish to.

For anything that should go in any WoT spec, we need implementation experience and we have zero experience with this feature that comes at the end of spec work.

Notes:

mlagally commented 2 years ago

To illustrate the proposal and give details on two alternatives for implementation in the TD, I have created two PRs:

1329

1330

mlagally commented 2 years ago

Since there's some contentious discussion split into these two PRs, I would like to provide some clarifications here, to avoid fragmented conversations:

  1. I don't understand why we turn around the process and ask for an implementation first. before we create the specification - it should be the other way round.

  2. Given that there are currently no known public implementations of the proposed event listener, it is appropriate to mark it with "at risk". We can then test during the plug fest, if two implementations of this feature can interoperate.

  3. I understand that it is probably not feasible to implement a notification listener (Server) in a Client-only consumer. However for a Consumer-Servient, which is a Thing, it is a fairly simple implementation task.

  4. Servient based implementations can support a notification mechanism, i.e. they will be defining an event listener as a fairly simple server implementation.

  5. A Java based Servient implementation is feasible in a few hundred lines, this also is valid for C#, C++ or other high level languages. It may be a bit more implementation effort for TypeScript / JavaScript based Servients, but I'm convinced it can be done.

  6. There seems to be a misunderstanding about whether each consumer MUST implement the notification operation. This is not the case, there may be many consumers (client based implementations) that will not (and don't have to) support notification listeners.

  7. The assumption that every consumer is only a client has been leading to the wrong conclusions, and I strongly recommend to think of a Servient based consumer as a common abstraction going forward. A telemetry use case is a very simple example for that.

mlagally commented 2 years ago

The following sequence diagram illustrates the Webhook based event mechanism.

image

sebastiankb commented 2 years ago

@mlagally this is very helpful. The WoT Thing calls kind of an action of the consumer. Is this POST based call in notify sync or async? Can you share how an example dataResponse message would look like?

mlagally commented 2 years ago

@sebastiankb In the use cases that are currently considered in scope, it is probably empty in most cases, however it could also contain response data that the application wants to provide back to the consumer. For telemetry use cases it could contain a desired time or delay, until when the next notification should be sent.

sebastiankb commented 2 years ago

it is probably empty in most cases

In the empty case we already have https://w3c.github.io/wot-thing-description/#example-temperature-event-with-subscription-and-cancellation as example, right?

however it could also contain response data that the application wants to provide back to the consumer.

If I read the sequence diagram correctly the response data is for the Thing and not for the consumer, right?

relu91 commented 2 years ago

Thank you for providing more information about this new mechanism. I actually got a different idea from the previous post, the sequence diagram and the concrete example help a lot. Looking again at the sequence diagram I am wondering have considered the option to just introduce a new subprotocol for HTTP called maybe webhook.

Let me expand a little further. If we abstract from the protocol details, what the consumer does is simply subscribe to an event. To me, we don't really need a new affordance for this because semantically speaking the consumer obtain the same result. For example, it is different from the Aync Actions where the consumer obtains different services with the ability to control or cancel an action during its execution.

Now about the new operation. In the past, we introduce new operations on affordances when we wanted to augment the possible functions that operated on the remote web thing. Taking again your diagram the remote calls are still subscribe and unsubscribe (you call this cancellation). Therefore, I don't think we need a new operation type either.

You might argue that we are missing to describe the notify operation, but in the diagram, it appears to be "protocol private" information since the URL endpoint is sent beforehand in the "subscribe" operation.

Strictly taking as reference your diagram above, my conclusion is that this is an application protocol flow that can be used to receive notifications with an HTTP endpoint. Since it is closely HTTP related and the notify endpoint is agreed in the protocol messages I think this is a good case for introducing a new subprotocol called webhook.

egekorkan commented 2 years ago

Since there was no examples so far, I will create one below that has a form with notify to illustrate my, @benfrancis and @relu91 's point.

{
    "events": {
        "temperature": {
            "description": "Provides periodic temperature value updates.",
            "subscription": {
                "type": "object",
                "properties": {
                    "subscriptionID": {
                        "type": "string",
                        "description": "Unique subscription ID for cancellation provided by WebhookThing.",
                        "readOnly": true
                    }
                }
            },
            "data": {
                "type": "number",
                "description": "Latest temperature value that is sent to the callback URL."
            },
            "cancellation": {
                "type": "object",
                "properties": {
                    "subscriptionID": {
                        "type": "integer",
                        "description": "Required subscription ID to cancel subscription.",
                        "writeOnly": true
                    }
                }
            },
            "forms": [
                {
                    "op": "subscribeevent",
                    "href": "http://192.168.0.124:8080/events/temp/subscribe",
                    "contentType": "application/json",
                    "htv:methodName": "POST"
                },
                {
                    "op": "notify",
                    "href": "http://mycloud.com/myUniqueThing/events/temperature",
                    "contentType": "application/json",
                    "htv:methodName": "POST"
                },
                {
                    "op": "unsubscribeevent",
                    "href": "http://192.168.0.124:8080/events/temp/unsubscribe",
                    "htv:methodName": "DELETE"
                }
            ]
        }
    }
}

This example is taken from the TD spec. I have put the newly proposed notify operation in the second form where the href in there belongs to the event consumer which is a cloud server. What is someone supposed to do with this form? Should another Consumer (not the Cloud nor the Thing) push data to this URL? Having this form does not tell the Thing to do anything. In the end, a TD does not describe what a Thing does but it describes how a Consumer can interact with the Thing.

To achieve this requirement, we do not need another operation. We need to:

Note: Please have a look at how MQTT works, you do not tell a Thing to publish to a certain topic beforehand and describe that in the TD but rather you describe where a Consumer can subscribe to in order to get the events published by the Thing.

chachamimm commented 2 years ago

I think we need consensus use cases for the discussion. It is important both the sequence diagram of @mlagally and the sample TD of @egekorkan. But I don't think there is a consensus. If it is possible, @mlagally should preset the expected use case.

benfrancis commented 2 years ago

@sebastiankb wrote:

The WoT Thing calls kind of an action of the consumer.

+1

@egekorkan wrote:

I have big doubts about implementing it at all, from my point of view it is not a Consumer-driven operation like all the other ones

+1

@relu91 wrote:

In the past, we introduce new operations on affordances when we wanted to augment the possible functions that operated on the remote web thing. Taking again your diagram the remote calls are still subscribe and unsubscribe (you call this cancellation). Therefore, I don't think we need a new operation type either.

+1

I agree with the above comments. A Thing Description describes a Thing, not a Consumer. Mixing the two would be very confusing.

As @egekorkan's example demonstrates, the notify URL would be different for each Consumer so it can't be provided to all Consumers in a static Thing Description.

I mentioned above that Example 66 in the Thing Description specification shows a way for a Consumer to dynamically provide a Thing with a callback URL for an event subscription. It's not ideal because it relies on a Consumer knowing what the callbackURL and subscriptionID properties mean, which probably doesn't allow for ad-hoc interop. But it does at least work.

If you really want to provide a Thing Description for the entity which is acting as a Consumer (the cloud service), then as @sebastiankb alluded to the notify operation is more like an action exposed by that service. You could give the cloud service its own Thing Description separate to the one describing the event emitter (the sensor), and expose the notify operation as an action affordance. The sensor device could then separately act as a Consumer which invokes that action to notify the cloud services of changes.

@relu91 wrote:

I am wondering have considered the option to just introduce a new subprotocol for HTTP called maybe webhook.

That sounds reasonable and if well defined could better support ad-hoc interop. Would that be any different to the existing websub subprotocol which is already mentioned in the Thing Description specification? I've never seen that used so I'm not sure how it's supposed to work, but the WebSub specification is based on HTTP WebHooks.

@mlagally wrote:

5. A Java based Servient implementation is feasible in a few hundred lines, this also is valid for C#, C++ or other high level languages. It may be a bit more implementation effort for TypeScript / JavaScript based Servients, but I'm convinced it can be done.

I think you've missed the point here. A web server is just as easy (probably easier) to implement in Node.js or TypeScript as any other programming language. The point is that a web server can't be implemented in client-side JavaScript, so a WoT Consumer implemented in that way (i.e. a client-side web application like the front end of WebThings Gateway) wouldn't be able to implement this mechanism.

6. There seems to be a misunderstanding about whether each consumer MUST implement the notification operation. This is not the case, there may be many consumers (client based implementations) that will not (and don't have to) support notification listeners.

This is true for Thing Descriptions in general, but the discussion you're referring to was in relation to the Core Profile in the WoT Profile specification. The point was that in order to enable out-of-the-box interoperability, Consumers which conform to the Core Profile MUST support all notification mechanisms which conformant Things MAY use. If the Core Profile made webhooks support mandatory, it would be un-implementable in Consumers (e.g. a desktop/mobile app or client-side web application) which can't be expected to expose an HTTP server in addition to being an HTTP client, either due to practical deployment issues or programming language limitations.

I think we need consensus use cases for the discussion. It is important both the sequence diagram of @mlagally and the sample TD of @egekorkan. But I don't think there is a consensus. If it is possible, @mlagally should preset the expected use case.

I believe the use case @mlagally is thinking of is the one described in slide 3 of this presentation from a WoT Architecture call. It involves sensor devices periodically pushing sensor data to a cloud service.

I agree that use case isn't currently well supported if you want to describe the sensor with a Thing Description, but the discussion above has identified three alternative implementation approaches which fit within the WoT architecture:

  1. By following the approach in Example 66 of the Thing Description specification where the Consumer provides a callback URL to the Thing when subscribing to an event
  2. By using a "webhook" or "websub" subprotocol in a form of an event affordance, with that same subscription mechanism described out-of-band in a standardised way
  3. By making the sensor devices Consumers of a Thing exposed by the cloud service, with the Consumer (sensor) invoking a notify action on the Thing (cloud service) to push sensor data

I suggest closing this issue as currently worded on the basis that the proposed "notify" affordance describes a capability of a Consumer, not a Thing, and is therefore out of scope for a Thing Description. I think it would be reasonable to further explore this use case in plugfests and perhaps work towards a cleaner solution in future versions of the specification, but this seems too late for 1.1.

mlagally commented 2 years ago

@mlagally wrote:

  1. A Java based Servient implementation is feasible in a few hundred lines, this also is valid for C#, C++ or other high level languages. It may be a bit more implementation effort for TypeScript / JavaScript based Servients, but I'm convinced it can be done.

I think you've missed the point here. A web server is just as easy (probably easier) to implement in Node.js or TypeScript as any other programming language. The point is that a web server can't be implemented in client-side JavaScript, so a WoT Consumer implemented in that way (i.e. a client-side web application like the front end of WebThings Gateway) wouldn't be able to implement this mechanism.

A Consumer that's only implemented in client-side JavaScript is not a Servient. We should not constrain the profile because of this limitation and rather find a way to express (and allow) both client-only and servient based consumers. Both consumer types are things (note the definition of a thing in the architecture spec), i.e. can be described in a TD. A servient based consumer has a TD with actual interaction affordances, whereas a TD for client based consumers will only have metadata.

A servient based consumer should have the notification operation on events to enable identification / mapping of the events to the corresponding affordance.

Example: 2 event types on a lamp:

On the consumer side you could have two "notify" event operations with a corresponding listener endpoint, one for each situation, or you would have a single endpoint which is capable of handling two event types.

In these cases A TD for the consumer would just have an event affordance with the "notify" op, nothing else.

  1. There seems to be a misunderstanding about whether each consumer MUST implement the notification operation. This is not the case, there may be many consumers (client based implementations) that will not (and don't have to) support notification listeners.

This is true for Thing Descriptions in general, but the discussion you're referring to was in relation to the Core Profile in the WoT Profile specification. The point was that in order to enable out-of-the-box interoperability, Consumers which conform to the Core Profile MUST support all notification mechanisms which conformant Things MAY use.

That's a good point, however I would doubt that we have to take that route and mandate an implementation of ALL event mechanisms from ALL consumers. It depends very much on the deployment and application context, which notification mechanism will be used. It is obvious, that a client-only implementation can not implement server functionality. However ruling out all server-based notification mechanisms, just because some consumers (client-only implementations) do not support them, is fundamentally wrong.

To elaborate a bit: If you think of battery driven low power sensors, it is unlikely that they can maintain active network connections for SSE or longpoll or other consumer initiated network connections. These devices typically wake up, take a measurement, send a message with the new sensor data and go to sleep again. An active network connection at TCP/IP level has traffic, i.e. consumes energy (see: https://en.wikipedia.org/wiki/Keepalive)

For large sensor deployments with thousands of connected devices it is natural that the device posts messages, i.e. initiates the connection with the consumer servient.

If the Core Profile made webhooks support mandatory, it would be un-implementable in Consumers (e.g. a desktop/mobile app or client-side web application) which can't be expected to expose an HTTP server in addition to being an HTTP client, either due to practical deployment issues or programming language limitations.

We have to find a way that enables both Client and Servient based Consumers. Ruling out the latter is not possible.

benfrancis commented 2 years ago

@mlagally wrote:

Both consumer types are things (note the definition of a thing in the architecture spec), i.e. can be described in a TD.

This makes no sense to me, does anyone else share this interpretation?

The definition of a Thing in the architecture specification has no mention of consumers:

An abstraction of a physical or a virtual entity whose metadata and interfaces are described by a WoT Thing Description, whereas a virtual entity is the composition of one or more Things.

The definition of a Consumer has no mention of a Consumer being a Thing:

An entity that can process WoT Thing Descriptions (including its JSON-based representation format) and interact with Things (i.e., consume Things).

A Thing Description describes a Thing. A Consumer consumes a Thing by processing its Thing Description.

A Consumer is not a Thing. The same software application could implement both the Thing and Consumer roles, but that seems like an unusual edge case.

A servient based consumer has a TD with actual interaction affordances, whereas a TD for client based consumers will only have metadata.

What use is a Thing Description with no interaction affordances?

On the consumer side you could have two "notify" event operations with a corresponding listener endpoint, one for each situation, or you would have a single endpoint which is capable of handling two event types. In these cases A TD for the consumer would just have an event affordance with the "notify" op, nothing else.

This is backwards, a Thing Description doesn't describe the Consumer (e.g. the mechanism by which it listens for events), it describes the Thing (e.g. the "overheated" and "broken" events it emits and how it emits them). A cloud service could theoretically have a Thing Description which describes a notify action, but that would make it a Producer, not a Consumer. The sensor device would be the Consumer in that case.

That's a good point, however I would doubt that we have to take that route and mandate an implementation of ALL event mechanisms from ALL consumers.

I suggest that is exactly what we need to do in order to guarantee out-of-the box interoperability, and create a separate profile for implementations which use a different deployment model, but that's a discussion for the WoT Profile specification.

If you think of battery driven low power sensors, it is unlikely that they can maintain active network connections for SSE or longpoll or other consumer initiated network connections. These devices typically wake up, take a measurement, send a message with the new sensor data and go to sleep again. An active network connection at TCP/IP level has traffic, i.e. consumes energy (see: https://en.wikipedia.org/wiki/Keepalive)

This is all true, but that's why battery powered devices rarely use Wi-Fi, TCP and HTTP. They use other technologies which are better suited for resource constrained devices such as Zigbee or Z-Wave, which requires an entirely different approach. I'm sure there are devices which act as HTTP clients, but the client part of that operation can not be described by a hypermedia form, and forms are a mandatory member of interaction affordances.

We have to find a way that enables both Client and Servient based Consumers. Ruling out the latter is not possible.

Nobody is saying we should rule that out, only that Thing Descriptions describe Things, not Consumers, so the approach described in this issue doesn't really make sense. I've summarised three alternative approaches which I suggest do make sense within the WoT architecture.

mmccool commented 2 years ago

So my understanding of the proposal is that we would add an additional affordance/op for "Notifications" to represent the incoming calls for webhooks. We can describe such things with the current TD but we can easily identify them in a standard way, e.g. with an op. One simple proposal is that we just add an "op" (perhaps called notify) that can be attached to an action.

However, one concern I have is that some webhooks embed ids in the URL, i.e. a new resource is created for each webhook callback. This would require dynamic resources in TDs, a separate but troublesome issue.

mmccool commented 2 years ago

Hmm... I guess we can use uritemplates etc. for ids embedded in URLs, though.

mmccool commented 2 years ago

following up on my comments in the meeting:

  1. There is a possibility that the webhook target does not have a TD
  2. I said that I was not sure that we needed dataresponse but I misunderstood its purpose. I now see that is a description of what the webhook target returns. IDEALLY that would be in the TD of the target BUT given 1, I can see how putting it in the "source" TD makes sense. However both data and dataresponse need to be consistent with the target if it DOES have a TD
mmccool commented 2 years ago

So one concern I have is with duplication of information:

  1. If the webhook target DOES have a TD, then it makes more sense to me to create an action, mark it as a "notify" op, and put input and output data schemas on it. Then data/dataresponse in the source TD are redundant (and it's unclear which is the primary version if they disagree).
  2. If the webhook target does NOT have a TD, then it's useful to have both data/dataresponse in the source TD.

The advantage of 1 is it only needs us to add a single "op". If we do 2, then we have to have to have clear guidance about what to do in case 1.

mmccool commented 2 years ago

So my suggestion is that Lagally's proposal is adopted, but some edge cases are clarified, in particular what we do when the target of a callback/webhook is also a Thing; how do we model callback points? Some options are

  1. Model them as an action with a special op.
  2. Model them as a new affordance.
  3. Don't model them; the data schemas etc. would come from the source.

1 has the problem that we don't want redundant input/output data schemas and if we follow Lagally's proposal these would be given by the source. I think this is what people would expect, so if we do 1 I would suggest we omit the data schemas in this case (but maybe include a link to the source TD). If we do 2, then we can make these requirements clearer (but it's more spec work, maybe). 3 is easiest to implement, and leaves things open for us doing 2 in the future (e.g. in TD 2.0).

benfrancis commented 2 years ago

@mmccool I don't see why either a new operation or a new type of affordance are needed.

  1. If the TD describes a sensor device which emits an event, an EventAffordance can be used with a subscribeevent operation, with the callback URL included in a subscription payload whose format is described by the subscription member, as shown in Example 66
  2. If the TD describes a cloud service which receives data from a sensor then an ActionAffordance can be used, with the existing invokeaction operation

The proposed new affordance type or operation would both be very odd, since they would break the existing model that a Thing Description describes a Thing (as opposed to a Consumer of a Thing).

If a more prescriptive solution is needed to enable out-of-the-box interoperability then that could be provided by a sub-protocol and/or profile.

mlagally commented 2 years ago

The proposed new affordance type or operation would both be very odd, since they would break the existing model that a Thing Description describes a Thing (as opposed to a Consumer of a Thing).

@benfrancis Your view of the thing definition is too narrow. Everything with a network interface is a thing and can be described with a TD. A Consumer may expose a notification interface which is described with a TD. Please do not think Client / Server. Better think Servient <-> Servient communication

benfrancis commented 2 years ago

@mlagally wrote:

Your view of the thing definition is too narrow. Everything with a network interface is a thing and can be described with a TD. A Consumer may expose a notification interface which is described with a TD.

I'm just quoting the definitions of "Thing" and "Consumer" from the WoT Architecture specification, which seem to contradict these statements.

Please do not think Client / Server. Better think Servient <-> Servient communication

That's all very well in theory, but in practice:

  1. href is a mandatory member of Form
  2. forms is a mandatory member of InteractionAffordance
  3. A Thing Description without InteractionAffordances is not useful

Since the client part of an HTTP servient doesn't have a URL, it can not be described by an InteractionAffordance in a static Thing Description.

Unless I'm missing something very fundamental this isn't an opinion, it's a technical constraint of the Thing Description design which uses hypermedia forms to serialise protocol bindings.

benfrancis commented 2 years ago

@mlagally If you disagree then in order to help us understand, can you provide an example Thing Description which:

  1. Uses one of the two approaches you have proposed in #1329 and #1330
  2. Describes a sensor device which emits events via webhooks (the use case I understand you are trying to solve)
  3. Can allow more than one Consumer to consume those events

Then explain how a Consumer of that Thing Description should interpret and use those metadata.

mlagally commented 2 years ago

@benfrancis

A Thing Description without InteractionAffordances is not useful

I hope the latest version still permits a TD without any interaction afforndance. Contradicting myself to something I wrote earlier: There are use cases for things with only metadata, but no interaction affordance. If you think about geofencing, it is perfectly fine to just know the location and dimensions of a building to determine if another thing is inside or outside. These are valid application scenarios.

Since the client part of an HTTP servient doesn't have a URL, it can not be described by an InteractionAffordance in a static Thing Description.

Not sure why you want to create a TD for the "client part" of a servient only. A servient is a combination of a client and a server which can be described with a TD. The server part can have notification endpoints; these can be described in a TD.

I will provide examples for event producer and consumer TDs in the next TD call.

benfrancis commented 2 years ago

@mlagally wrote:

Not sure why you want to create a TD for the "client part" of a servient only. A servient is a combination of a client and a server which can be described with a TD. The server part can have notification endpoints; these can be described in a TD.

Because for an EventAffordance using webhooks the Thing emitting an event is the client and the Consumer listening for the event is the server. The Thing Description describes the Thing entity, not the Consumer entity. The notification endpoint (callback URL) is different for each Consumer, which means they can't be serialised as a form in a static Thing Description. This is why in Example 66 the callback URL is provided dynamically during a subscribe operation rather than provided statically in the Form.

This seems very obvious to me, so I think we must be talking at cross-purposes. Hopefully your examples will help explain the misunderstanding.

mlagally commented 2 years ago

Use Case example: there are many use-cases for a push notification, please review the Use cases document. Some simple examples are in retail, e.g. AllStopButton, think of a fire alarm button on a gas station.

Let's assume a battery powered fire alarm button as a concrete example of a device, which will not support keeping TCP connections open, because the keep alive messages will quickly drain the thing's battery.

When the fire alarm button is pressed, an alarm notification is sent to the fire patrol station which will send a fire engine to the gas station. This is modeled as a push event, i.e. the Thing servient connects to the listener

Deployment: Consumer Servient: FireAlarmListener Thing Servient: FireAlarmButton Protocol: HTTP

Operation flow:

  1. FireAlarmListener subscribes to FireAlarmButton event affordance, registers itself as a listener. (There could be also an implicit onboarding flow that registers the lister via a config file)
  2. Human user pushes the button, the FireAlarmListener gets notified, this triggers multiple actions, such as calling the police, sending a fire engine, ...
  3. There may be additional events, such as "batteryLow" to indicate the need of human intervention and maintenance, i.e. a service person replaces the battery of the FireAlarmButton

Based on Ege's example, here are two pseudo TDs to illustrate the listener concept:

FireAlarmButton - pseudo JSON:

...

{
    "events": {
        "fireAlarm": {
            "description": "Fire!",
            "subscription": {
                "type": "object",
                "properties": {
                       ...
                       listener: uri,
                       ...
                    }
                }
            },
           "data": {
                "type": "boolean",
                "description": "true, if the alert button has been pushed, false, if the button was armed again."
            },
            "dataResponse": {
                "type": "string",
                "description": "acknowledged by operator named: "
            },

            "cancellation": {
                "type": "object",
                 "properties": {
                       listener: uri
                    }
                }
            },
            "forms": [
                {
                    "op": "subscribeevent",
                    "href": "http://192.168.0.124:8080/events/fireAlarm",
                    "contentType": "application/json",
                    "htv:methodName": "POST"
                },
                {
                    "op": "unsubscribeevent",
                    "href": "http://192.168.0.124:8080/events/fireAlarm/unsubscribe",
                    "htv:methodName": "DELETE"
                }
            ]
        },
        "batteryLow": {
            ...
        }
    }
}

FireAlarmListener - pseudo JSON:

<snip> ... </snip>

{
    "events": {
        "fireAlarm": {
            "description": "Fire!"
             },
           "data": {
                "type": "boolean",
                "description": "true, if the alert button has been pushed, false, if the button was armed again."
            },
            "dataResponse": {
                "type": "string",
                "description": "acknowledged by operator named: "
            },
            "notification": {
                "type": "object",
                 "properties": {
                       <event details, time stamp, ...>
                    }
                }
            },
            "forms": [
                 {
                    "op": "notify",
                    "href": "http://mycloud.com/GasStation1/events/fireAlarm",
                    "contentType": "application/json",
                    "htv:methodName": "POST"
                }
            ]
        },
        "batteryLow": {
        ... 
        }
    }
}
sebastiankb commented 2 years ago

from today's TD call:

benfrancis commented 2 years ago

@mlagally wrote:

Based on Ege's example, here are two pseudo TDs to illustrate the listener concept

Thank you for sharing these examples to demonstrate what you had in mind.

I agree with the comment from the TD call that it's not clear why the second TD is needed and who would consume it.

Why does the FireAlarmListener entity need a Thing Description at all? FireAlarmListener provides the callback URL to FireAlarmButton when it subscribes to the event, so why does that URL also need exposing as a "notify" operation on the listener side? What Consumer would consume that Thing Description, and what would it use it for?

As I understand it, an EventAffordance describes an event that the Thing emits, not an event that the Thing consumes (an "event that a Thing consumes" is probably better modelled as an ActionAffordance). The second Thing Description therefore appears to be backwards, since it has an affordance describing an event which is emitted by another Thing.

If we add a notify operation to describe how an event is consumed by a Consumer, should we also add operations to describe how a Consumer listens for an observed property change or a change in action status?

Hypermedia forms are limited to describing interactions using discrete URL endpoints for each interaction, which makes them poorly suited to describing protocols which send a variety of different messages over a single bi-directional connection, like WebSockets. In that case, there's no choice but to describe the protocol specifics out-of-band in a sub-protocol. However, in the case of webhooks example 66 demonstrates how the additional subscription, data and cancellation data schemas can be used to describe a webhook interaction. The proposed notify operation doesn't seem to add much value to that use case.