Interacting with Eclipse sensiNact¶
In this example we will see how to interact with Eclipse sensiNact using the REST interface. This will help you to understand how to navigate the gateway, access the data from sensors, and update values stored in the digital twin.
To start with you will need a runnable gateway configured with REST access. If you don’t have one of these then you may wish to consider starting with the earlier examples.
Listing and Introspecting the gateway¶
Listing the providers present in the gateway is very simple. Assuming that you’re using the configuration from the previous example then issuing a GET request to http://localhost:8082/sensinact/providers
will return something like:
{
"type": "PROVIDERS_LIST",
"uri": "/",
"statusCode": 200,
"providers": ["sensiNact"]
}
This shows the result was successful, is of type PROVIDERS_LIST
and the providers
property contains a single provider sensiNact
.
The sensiNact
provider is a built in provider which we can further introspect at http://localhost:8082/sensinact/providers/sensiNact
{
"type": "DESCRIBE_PROVIDER",
"uri": "/sensiNact",
"statusCode": 200,
"response": {
"name": "sensiNact",
"services": ["system","admin"]
}
}
This shows us details about the sensiNact
provider in the form of a DESCRIBE_PROVIDER
response. We could get similar information by listing the services for the sensiNact
provider at http://localhost:8082/sensinact/providers/sensiNact/services
{
"type": "SERVICES_LIST",
"uri": "/sensiNact",
"statusCode": 200,
"services": ["system","admin"]}
We can further query the resources available for a given service at http://localhost:8082/sensinact/providers/sensiNact/services/system
or http://localhost:8082/sensinact/providers/sensiNact/services/system/resources
{
"type": "DESCRIBE_SERVICE",
"uri": "/sensiNact/system",
"statusCode": 200,
"response": {
"name": "system",
"resources": [
{"name": "version", "rws": "RO", "type":"SENSOR"},
{"name": "started", "rws": "RO", "type": "SENSOR"}
]
}
}
{
"type": "RESOURCES_LIST",
"uri": "/sensiNact/system",
"statusCode": 200,
"resources": ["started","version"]
}
Querying resources¶
When you describe a resource such as http://localhost:8082/sensinact/providers/sensiNact/services/system/resources
you get a list of all of the possible sensiNact verbs that can be used, and the parameters that they accept:
{
"type": "DESCRIBE_RESOURCE",
"uri": "/sensiNact/system/started",
"statusCode": 200,
"response": {
"name": "started",
"type": "SENSOR",
"attributes": [],
"accessMethods":[
{
"name": "GET",
"parameters": [
{"name": "attributeName", "type": "string", "fixed": false, "constraints": []}
]
},
{
"name": "SUBSCRIBE",
"parameters": [
{"name": "topics", "type": "array", "fixed": false, "constraints": []},
{"name": "isDataListener", "type": "boolean", "fixed": false, "constraints":[]},
{"name": "isMetadataListener", "type": "boolean", "fixed": false, "constraints": []},
{"name": "isLifecycleListener", "type": "boolean", "fixed": false, "constraints": []},
{"name": "isActionListener", "type": "boolean", "fixed": false, "constraints": []}
]
},
{
"name": "UNSUBSCRIBE",
"parameters": [
{"name": "subscriptionId", "type":"string", "fixed":false, "constraints":[]}
]
},
{
"name": "SET",
"parameters": [
{"name": "value", "type": "java.time.Instant", "fixed": false, "constraints": []}
]
}
]
}
}
We can then “GET” the value of the resource from http://localhost:8082/sensinact/providers/sensiNact/services/system/resources/started/GET
{
"type": "GET_RESPONSE",
"uri": "/sensiNact/system/started",
"statusCode": 200,
"response": {
"name": "started",
"timestamp": 1693387668421,
"type": "java.time.Instant",
"value": "2023-08-30T09:27:48.421487Z"
}
}
Adding a southbound provider¶
In order to get more meaningful data from our sensiNact we can deploy a virtual sensor by updating the configuration/configuration.json
to include the virtual-temperature-sensor-feature
.
{
// ...
"sensinact.launcher": {
"features": [
"core-feature",
"jakarta-servlet-whiteboard-feature",
"jakarta-rest-whiteboard-feature",
"northbound-rest-feature",
"virtual-temperature-sensor-feature"
],
"repository": "repository",
"featureDir": "features"
},
"sensinact.virtual.temperature": {
"name": "temp1",
"interval": 5000,
"latitude": 1.0,
"longitude": 2.0
}
// ...
}
We can query the value of the sensor/temperature
resource as before at http://localhost:8082/sensinact/providers/temp1/services/sensor/resources/temperature/GET
{
"type": "GET_RESPONSE",
"uri": "/temp1/sensor/temperature",
"statusCode": 200,
"response": {
"name": "temperature",
"timestamp": 1693394285012,
"type": "double",
"value": 29.60935577560438
}
}
Subscribing to updates¶
The virtual temperature sensor periodically updates its temperature reading. This could be read by repeatedly polling the value, however this is inefficient, and risks missing changes if the update is more frequent than the polling interval.
The SUBSCRIBE verb allows clients to register so that they can be notified when an update occurs, for the REST interface this is delivered as a Server Sent Event (SSE) stream, but other interfaces (such as Web Socket) may use other push mechanisms.
Making a GET request to http://localhost:8082/sensinact/providers/temp1/services/sensor/resources/temperature/SUBSCRIBE
will result in a series of data
events being delivered:
event: data
data: {
"provider": "temp1",
"service": "sensor",
"resource": "temperature",
"timestamp": 1693402975313,
"oldValue": 18.758596432570382,
"newValue": 17.56708764568309
}
event: data
data: {
"provider": "temp1",
"service": "sensor",
"resource": "temperature",
"timestamp": 1693402980313,
"oldValue": 17.56708764568309,
"newValue": 8.2679466364907
}
...
In the REST interface unsubscription occurs automatically when the client closes the SSE connection. For other northbound providers the initial subscription response will include a subscription identifier. This identifier can be passed back in an unsubscription request to terminate the delivery of notifications.
Updating values¶
If a resource is MODIFIABLE
that means that it can have its value updated by an end user using the SET
verb. Unlike the other requests so far a SET
operation in the REST API is a PUT
, not a GET
. The body of the PUT request is an array of parameters with a name
, type
and value
. We can test this using curl
curl -d "[{\"name\": \"value\", \"type\": \"string\", \"value\": \"Lassie\"}]" \
-H "Content-Type: application/json" \
http://localhost:8082/sensinact/providers/temp1/services/admin/resources/friendlyName/SET
A successful response will look like the following:
{
"type": "SET_RESPONSE",
"uri": "/temp1/admin/friendlyName",
"statusCode": 200,
"response": {
"name": "friendlyName",
"timestamp": 1693407687936,
"type": "java.lang.String",
"value":"Lassie"
}
}
After a SET
subsequent GET
requests will return the previously set value.
Note
If your resource is FIXED
or UPDATABLE
then it cannot be set by the northbound API. Also, the behaviour of SET
requests may be slightly different if your resource is pull-based and the device returns a different value.