-
Notifications
You must be signed in to change notification settings - Fork 65
Description
Before we take the first step on the slippery slope of turning Thing Descriptions into a turing complete programming language by adding "variables" as a top level member of Thing Descriptions, I wanted to re-visit an old idea of using semantic annotations to describe dynamic resources in a declarative way.
What if we created a semantic context that defined some special terms that can be used in data schemas (including URI variables) to explain their semantic meaning in the context of the WoT information model?
Specifically, I'm thinking about terms such as:
wot:propertyNamewot:propertyValuewot:actionNamewot:actionInputwot:actionOutputwot:actionStatuswot:actionIDwot:eventNamewot:eventData
To make this work I would also suggest adding a status member to ActionAffordance as per @relu91's suggestion in #2068.
These special semantic annotations could be used to specify particular URI variables and specific members of data payloads as action IDs, and also point to which part of an action status is its output.
In more complicated bindings with data wrappers in their payloads they may also be used to point out which part of a payload is the actual value of a property or data from an event, and potentially also details like how to enumerate property names in a readmultipleproperties request.
Below I have provided an example which attempts to solve the long term problem of how to describe the HTTP Basic Profile protocol bindings (particularly the action protocol bindings) declaratively in a Thing Description!
{
"@context": [
"https://www.w3.org/ns/wot-next/td",
"wot": "https://www.w3.org/ns/wot-next/"
],
"id": "https://mywebthingserver.com/things/lamp",
"base": "https://mywebthingserver.com/things/lamp/",
"title": "My Lamp",
"description": "A web connected lamp",
"securityDefinitions": {
"oauth2": {
"scheme": "oauth2",
"flow": "code",
"authorization": "https://mywebthingserver.com/oauth/authorize",
"token": "https://mywebthingserver.com/oauth/token"
}
},
"schemaDefinitions": {
"error": {
"type": "object",
"properties": {
"type": {
"type": "string",
"format": "uri"
},
"title": {
"type": "string"
},
"status": {
"type": "number"
},
"detail": {
"type": "string"
},
"instance": {
"type": "string",
"format": "uri"
}
}
},
"actionStatus": {
"@type": "wot:actionStatus",
"type": "object",
"properties": {
"state": {
"type": "string",
"emum": ["pending", "running", "completed", "failed"]
},
"output": {
"@type": "wot:actionOutput"
},
"error": {
"$ref": "#/schemaDefinitions/error"
},
"href": {
"@type": "wot:actionID",
"type": "string"
},
"timeRequested": {
"type": "string",
"format": "date-time"
},
"timeEnded": {
"type": "string",
"format": "date-time"
}
}
},
"actionStatuses": {
"type": "object",
"propertyNames": {
"type": "string",
"@type": "wot:actionName"
},
"additionalProperties": {
"type": "array",
"items": {
"$ref": "#/schemaDefinitions/actionStatus"
}
}
}
},
"security": "oauth2",
"properties": {
"on": {
"type": "boolean",
"title": "On/Off",
"description": "Whether the lamp is turned on",
"forms": [{"href": "properties/on"}]
},
"level" : {
"type": "integer",
"title": "Brightness",
"description": "The level of light from 0-100",
"unit": "percent",
"minimum" : 0,
"maximum" : 100,
"forms": [{"href": "properties/level"}]
}
},
"actions": {
"fade": {
"title": "Fade",
"description": "Fade the lamp to a given level",
"synchronous": false,
"input": {
"type": "object",
"properties": {
"level": {
"title": "Brightness",
"type": "integer",
"minimum": 0,
"maximum": 100,
"unit": "percent"
},
"duration": {
"title": "Duration",
"type": "integer",
"minimum": 0,
"unit": "milliseconds"
}
}
},
"status": {
"schema": "actionStatus"
},
"uriVariables": {
"actionID": {
"type": "string",
"@type": "wot:actionID"
}
},
"forms": [
{
"href": "actions/fade",
"op": "invokeaction"
},
{
"href": "{actionID}",
"op": "queryaction"
},
{
"href": "{actionID}",
"op": "cancelaction"
}
],
}
},
"forms": [
{
"op": ["readallproperties", "writemultipleproperties"],
"href": "properties"
},
{
"op": "queryallactions",
"href": "actions",
"additionalResponses": [{
"success": "true",
"schema": "actionStatuses"
}]
}
]
}This example makes use of JSON pointers (I hope I got that part right!) assumes that a queryaction request and an invokeaction request for an asynchronous action would get a response according to the status data schema. It also uses additionalResponses to define a data schema for the queryallactions operation which is a bit of a hack, but I'm hoping we might come up with something cleaner than that for TD 2.0. It also assumes default HTTP verbs for queryaction (GET), cancelaction (DELETE) and queryallactions (GET).
Please let me know what you think.
P.S. If we were very brave we might even consider defining the data schemas from schemaDefinitions as default data schemas for the queryaction and queryallactions operations in the HTTP Binding for if a TD doesn't specify something itself. That would make TDs using future HTTP-based Profiles much cleaner!