This article is a tutorial which considers a fictional smart coffee machine in order to demonstrate the capabilities of Web of Things (WoT) and the usage of node-wot API. In the world of Web of Thing properties, actions and events provided by a Thing are called Property Affordances, Action Affordances and Event Affordances, respectively. The difference of each becomes clear as we proceed the tutorial. So, we imagine a smart coffee machine which provides the following Property Affordances:
allAvailableResources
- that is a current level of all available resources (i.e. water, milk, chocolate and coffee beans) given as an integer percentage for each particular resource.
This data is read-only and is obtained from the machine’s sensors but can also be set manually via the availableResourceLevel
property (next one) in case the sensors are broken.availableResourceLevel
- that is a current level of a particular resource, which should be specified as a query string (called uriVariables
in node-wot).
This data is obtained from the previous property (allAvailableResources
).
The difference is in usage of uriVariables
and that this property is also writable, so that it can be used to override the values of allAvailableResources
.possibleDrinks
- a read-only list of possible drinks in general.
Doesn’t depend on the available resources.servedCounter
- the total number of served beverages.
This property is writable.
The data is obtained from the machine but can also be set manually (thus, writable).
The case for that is explained below.maintenanceNeeded
- a boolean value showing whether the machine needs a maintenance.
The property is observable, which means in WoT that a user can get notified every time the value of this property changes.
Automatically set to true when the servedCounter
property exceeds 1000.schedules
- a read-only array containing scheduled tasks, i.e. a task which should be performed according to a specific schedule.The idea behind servedCounter
and maintenanceNeeded
is that, every time servedCounter
exceeds 1000 the maintenanceNeeded
flag is set to true.
And since this value is observable a “maintainer” gets notified, who then comes and performs the maintenance of the machine, and afterwards sets the servedCounter
and maintenanceNeeded
to 0 and false, respectively.
The smart coffee machine has also the following Action Affordances:
makeDrink
- make a drink from the list of possible beverages.
Accepts drink id, size and quantity as uriVariables
.
Brews one medium americano if no uriVariables
are specified.setSchedule
- add a scheduled task to the schedules
property. Accepts drink id, size, quantity, time and mode as body of a request (i.e. a request payload).
Assumes one medium americano if not specified, but time and mode are mandatory fields.
Notice that, even though the schedules
property is read-only, it’s being modified through the setSchedule
action.
That’s the same principle as creating setters for private properties in object-oriented programming languages.Finally, the coffee machine has the following Event Affordances:
outOfResource
- an out-of-resource event.
Emitted when the available resource level is not sufficient for a desired drink.Two main functionalities of node-wot is creating a WoT Thing and interacting with another WoT Thing.
These functionalities can also be combined to have a Thing interacting with other Things.
Creating a WoT Thing is called exposing or producing a Thing.
In order to expose our smart coffee machine we need to invoke produce
method of the WoT object.
It takes a Thing Description (TD) as the only parameter.
Note that, this TD can be set only partially.
The full TD will then be produced by the produce
method, assuming default data for non-specified TD terms and creating default protocol bindings (HTTP and CoAP).
This produced TD can then be used by other Things or clients to interact with the Thing.
|
|
The full script is available at node-wot GitHub repository.
Note that, all affordances (i.e. property, action and event) should be added withing the produce
method.
After producing the Thing, we need to initialize the properties and all required handlers.
This is done by chaining .then
method after WoT.produce()
.
Property initialization looks as follows.
|
|
In case a property needs a write handler, we can set the according propertyWriteHandler
.
See an example below.
|
|
And then we are ready to initialize its value.
|
|
We also want to override write and read handlers for availableResourceLevel
property, since we need to utilize uriVariables
.
|
|
As it has already been mentioned, maintenanceNeeded
property is observable, meaning we can could get notified when its value changes. For that, we need to provide a callback for the observeProperty
method.
Done with the Property Affordances! Now we need to set up action handlers, which proceed when another Thing or client invokes the action.
|
|
Notice, how in case of insufficient resources the outOfResource
event is emitted.
Note also that uriVariables
is being passed into options variable as a second argument of the handler.
The first argument params
contains a request body (i.e. payload of a request).
Another handler is for setSchedule
action.
|
|
As mentioned above, here we use the payload of a request, therefore we utilize the params
variable.
Done with the Action Affordances! Now our final affordances, that is Event Affordances. We can specify a handler for an event, which then gets executed whenever the event is emitted. In node-wot this process is called “subscribing” for an event. Although it is possible to create event handlers in the producer Thing, usually it is done on the client side. So, the producer (server) only emits a particular event and the client is responsible for handling it. Below in the client part we will cover this process.
Now, finally, expose the Thing!
|
|
So far we are done with the “producer” Thing and now our smart coffee machine can be interacted via any HTTP or CoAP client (default protocol bindings as mentioned above).
The produced Thing Description is available at http://127.0.0.1:8080/smart-coffee-machine for HTTP and coap://127.0.0.1:5683/smart-coffee-machine for CoAP.
By default, in HTTP binding the GET method is used for reading properties, PUT for writing and POST for invoking actions.
So, for example the value of allAvailableResources
property can be read as:
|
|
The value of availableResourceLevel
property can be set as:
|
|
And makeDrink
and setSchedule
actions can be invoked as:
|
|
|
|
As we already mentioned, note that one uses query string and the other one uses a payload.
We can also create a consumer Thing (i.e. a client) for our smart coffee machine using the node-wot API.
In order to create a consumer Thing, we need to invoke fetch
method of the WoTHelpers object giving it an exposed Thing Description as the only parameter, and then invoke consume
method of the WoT object as follows.
|
|
The full “client” script is available at node-wot GitHub repository.
Notice that, we are awaiting asynchronous functions to complete before proceeding, which is quite logical here.
Remember that we need the async
keyword in the outer function in order to use await
inside the function.
We could also chain the asynchronous consume
method with other methods using .then
.
But let’s stick with async/await
for our example.
A property can be read using thing.readProperty
method.
|
|
A property can be written using thing.writeProperty
method.
|
|
Notice on usage of uriVariables
here.
In the same manner they can be used when reading properties which utilize uriVariables
.
|
|
It’s also possible to set a client-side handler for observable properties.
|
|
Notice that, here we don’t need to await for a function to complete, since observing a property is a persistent action.
We can invoke an action using thing.invokeAction
method.
|
|
Notice on usage of uriVariables
here.
They are passed as a third argument, whereas the second one is the payload of a request.
This can be well noted on invoking of setSchedule
action.
|
|
As it is already mentioned above, we also want a client to subscribe for events emitted from the producer Thing.
|
|
Again, here we don’t need to await for a function to complete, since subscribing for an event is a persistent action.
As it is mentioned above, these example scripts are available at node-wot GitHub repository. In order to run them do the following:
|
|
Now you can run the scripts as follows:
|
|
If you want to add your own example scripts be sure to follow the workflow.
In case you want to just consume a Thing you can use a tool like the Browsified node-wot. It allows you to interact with Things right from your browser. There is a deployed smart coffee machine producer Thing at http://plugfest.thingweb.io:8083/smart-coffee-machine that you can consume. You can also see its property values in real-time at http://plugfest.thingweb.io/examples/smart-coffee-machine.html.
Currently, node-wot supports different security schemas.
In the example above we have used the NoSecurityScheme
, as it is assumed by default if no security scheme is explicitly used.
Node-wot also supports oAuth 2.0, so let’s extend this tutorial with the same coffee machine having oAuth 2.0 authorization.
Extend the Thing Description within the produce
method with the following lines (add id
, securityDefinitions
and security
):
|
|
Now if we run the producer and the consumer Things as before that will fail. The reason is simple - the client is not authorized. Let’s fix it.
We need an additional configuration file which will contain the appropriate credentials. The configuration file contains the following and is available as smart-coffee-machine-client.conf.json:
|
|
The first setting allows self-signed certificates, which is okay for testing and development purposes.
The other setting provides the credentials in a form of “Thing id - credentials” pair.
We will be using the oAuth 2.0 test server which is equipped with node-wot, and it expects exactly these clientId
and clientSecret
.
Now run the test oAuth server:
|
|
You should see a message saying listening
.
Now in a different terminal run the coffee machine producer Thing as usual:
|
|
Finally, from the third terminal run the coffee machine consumer Thing providing it with the configuration file we have created:
|
|
Now the client interacts with the smart coffee machine as before since it is authorized by the oAuth server.