-
Notifications
You must be signed in to change notification settings - Fork 0
3.1 Integrate a resource by using the symbIoTe agent
Currently the supported Arduino chipset is the ESP8266; in the example sketch of the GitHub repo, there's an example based on the AdafruitHuzzah ESP8266.
Following this example you can build any SDEV firmware based on the ESP8266 (if you will use the same board of the example it will be quicker). Follow this guide to compose the SDEV firmware.
Download the last Arduino software. Add the ESP8266 board inside the software, enter http://arduino.esp8266.com/stable/package_esp8266com_index.json into Additional Board Manager URLs field in the Arduino v1.6.4+ preferences. Add the correct external library:
- ArduinoJson
- RestClient
- Crypto
- Hash
- sha1 ( please be sure to use the forked version at https://github.com/bbx10/Cryptosuite )
- base64
- ESP8266WebServer
- WiFiUdp
- NTPClient
Instantiate the correct class to handle your resources (sensors classes) and define the basic function to actuate/read the resources.
This format should be used for reading a resource:
String read_res1(void)
{
// get the data
// Convert the data in a String format followed by a blank space and the unit of measurment
return String var;
}
To actuate a resource, use the following format:
bool set_res1(int in)
{
// Do what you need
// return true if successful actuate the resource
}
So all the previous dissertations translate in:
#define SDA 4
#define SCL 5
#define WS2812_PIN 2
// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS 32
Adafruit_MPL3115A2 baro = Adafruit_MPL3115A2();
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, WS2812_PIN, NEO_GRB + NEO_KHZ800);
uint8_t j = 0;
String readTemp()
{
return String (baro.getTemperature()) + " °C";
}
bool setRed(int in)
{
uint32_t color;
for(int i=0;i<NUMPIXELS;i++) {
color = pixels.getPixelColor(i);
//set red color in the RGB 32 bit color variable
color = ((color & 0xFF00FFFF) | (in << 16));
pixels.setPixelColor(i, color); //Moderately bright green color.
}
pixels.show();
return true;
}
After that you should declare the classes to handle the semantic description of the SDEV you are building. You have 3 choices:
- SDEV with only sensor resources (mapped in the class Property)
- SDEV with only actuator resources (mapped in the classes Parameter and Capability)
- SDEV with both actuator and sensor resources
You should declare an array of Property, one instance for each sensor property. Every instance should be linked with the function that reads the property and give back the measurment. Like this:
Property propertyPointer[2] = {Property("temperature", "nc", &readTemp), Property("pressure", "nc", &readPr)};
Then declare an empty Capability class with 0 param_num:
Parameter* paramPointer = NULL;
Capability c1("none", 0, paramPointer);
After that, you can create a Semantic Class, linking all these steps together:
Semantic s1("aggeggio", 1, &c1, 2, propertyPointer);
You should declare an empty Property Class. Then you should declare an array of Parameter Class for each actuation that you want to be handled by the SDEV.
Parameter paramPointer[3] = {Parameter("r", "xsd:unsignedByte", "0", "255", &setRed), Parameter("g", "xsd:unsignedByte", "0", "255", &setGreen), Parameter("b", "xsd:unsignedByte", "0", "255", &setBlue)};
Then create a Capability class and link the array of Parameter:
Capability c1("RGBCapability", 3, paramPointer);
After that, you can create a Semantic Class passing 0 as the obsNumber and a NULL pointer for propertyPointer:
Property* propertyPointer = NULL;
Semantic s1("aggeggio", 1, &c1, 0, propertyPointer);
In this case you should merge the two previous step, so do something like this:
Property propertyPointer[2] = {Property("temperature", "nc", &readTemp), Property("pressure", "nc", &readPr)};
Parameter paramPointer[3] = {Parameter("r", "xsd:unsignedByte", "0", "255", &setRed), Parameter("g", "xsd:unsignedByte", "0", "255", &setGreen), Parameter("b", "xsd:unsignedByte", "0", "255", &setBlue)};
Capability c1("RGBCapability", 3, paramPointer);
After that, you can create a Semantic Class, linking all these steps together:
Semantic s1("aggeggio", 1, &c1, 2, propertyPointer);
Then create the symAgent Class, passing the keepalive interval, the name, the roaming feature and the just created Semantic class:
symAgent sdev1(20000, "RGB Leds HAT", false, &s1);
To let the keep-alive ISR quicker, you should use a variable inside your firmware to check if the keep alive interval is triggered; in this way you can use the boolean variable:
extern volatile boolean keepAlive_triggered;
After that you should call the following method to correctly setup the agent:
sdev1.begin();
sdev1.registry();
sdev1.join();
Then remember to handle the expiration of the registration, if it is different from 0 (so no registration expiration).
In the main loop, remember to call periodically the handleSSPRequest() to handle the requests coming from RAP.
Further information:
- To enable/disable the debug print on Serial, define the macro DEBUG_SYM_CLASS to 1/0 in the sym_agent.h and lsp.h
- Please note that at this point the crypto protocol supported from both SDEV and Innkeeper for the lightweight security protocol used in the sym_agent class are TLS_PSK_WITH_AES_128_CBC_SHA and PBKDF2.
This chapter show how create your custom SDEV: you need to deal with the LightWeight security protocol and then use the security session created to register the SDEv usign the agent interfaces. So let's start describing the first part, the LWSP.
Depends of the type of connection between the SDEV and the symbiote GW, it can happen using WiFi or local Ethernet connection. In the first case, the SDEV should connects to the WiFi; the SSID of the symbiote GW is something like "sym-sspSpecificId". The password can be retrieved from the SSID: use the SSID changing the character 'f' with '9' and '5' with 'a'.
Before the agent can interact with the Innkeeper for register the SDEV and its resources, it should use the LWSP to establish a secure communication channel where data passes in a crypt way. The procedure is initiated by the SDEV. The LWSP uses 2 couple of Json message to do that sent as body of a HTTP POST to the Innkeeper. The Innkeeper could be reach at "innkeeper.symbiote.org:8080" and the LWSP should contact "innkeeper.symbiote.org:8080/innkeeper/sdev/register". The following list shows the message handled by the LWSP:
- Sdev Hello It is sent from SDEV to Innkeeper.
- GW_INNK Hello This is the response of the Innkeeper of the first message.
- SDEV AuthN It is sent from SDEV to Innkeeper. It prove the activation of the chiper suite.
- GW_INNK AuthN This is the response of the Innkeeper of the SDEV AuthN. It prove the activation of the chiper suite.
Now let's see in details what these messages are in detail.
This is a Json message like this:
{
"mti": "0x10",
"SDEVmac": "55:66:77:3a:61:76",
"cp": "0x008c",
"kdf": "PBKDF2",
"nonce": "215044f1"
}
Where:
- "mti" should be 0x10, it is the code of the Sdev Hello message
- "SDEVmac" is the mac address of the SDEV
- "cp" is the "crypto proposal" of the SDEV. At this time of implementation, it is supported only "0x008c" that means TLS_PSK_WITH_AES_128_CBC_SHA1: use AES128 CBC and SHA1 as hashing algoritm
- "kdf" stands for "Key Derivation Function" and at this time of implementation it is only supported the PBKDF2.
- "nonce" is a random nonce generated from the SDEV, 8 hexadecimal characters.
This is a Json message like this:
{
"cc": "0x008c",
"mti": "0x20",
"sessionId": "8L6hAZyv",
"iv": "PPqg3p5yzDJp0mMb",
"nonce": "65c9d307"
}
Where:
- "mti" should be 0x20, it is the code of the Gw Hello message
- "cc" is the "crypto choice", at this time of the implementation is "0x008c"
- "sessionId" is the id of the session, choiced from the Innkeeper
- "iv" is the initialization vector used for the crypto suite choiced, in this case for the AES128 CBC
- "nonce" is the Gateway nonce, 8 hexadecimal characters.
After these two messages, SDEV and Innkeeper should calculate their crypto stuff to insert in the next two messages.
Before send this message, the LWSP (in both parties) should calculate the two key used to crypth and sign the data, DK1 and DK2. To retrive the DK1, based on the kdf and crypto suite used: DK1 = PBKDF2(HMAC-SHA1, PSK, SDEVNONCE||GW_INKNONCE, 4, 16) where:
- HMAC_SHA1 is the pseudorandom function used by PBKDF2
- PS is the key used from the PBKDF2, at this point of implementation is fixed and it is {0x46, 0x72, 0x31, 0x73, 0x80, 0x52, 0x78, 0x92, 0x52, 0x81, 0xad, 0xd7, 0x57, 0x2c, 0x04, 0xa5, 0xdd, 0x84, 0x16, 0x68};
- SDEVNONCE||GW_INKNONCE is the salt of the PBKDF2
- 4 is the number of iterations
- 16 is the length, in bytes, of the DK
To retrive the DK2, based on the kdf and crypto suite used: DK2 = PBKDF2(HMAC-SHA1, firstpart(PSK/2)||SDEVNONCE||GW_INKNONCE, SDEVNONCE||GW_INKNONCE, 4, 16)
- HMAC_SHA1 is the pseudorandom function used by PBKDF2
- "firstpart(PSK/2)||SDEVNONCE||GW_INKNONCE" is the key used from the PBKDF2
- SDEVNONCE||GW_INKNONCE is the salt of the PBKDF2
- 4 is the number of iterations
- 16 is the length, in bytes, of the DK
Then this is the Json message sent to Innkeeper:
{
"mti": "0x30",
"sn": "db06a",
"nonce": "2f1230d3",
"sessionId": "8L6hAZyv",
"authn": "ChbtlrhAxe7ajFTpKx/AZAdqGTEja158z+ly2/j83ZE=",
"sign": "CjE2yVM1UDgjYB8+arAjqAhmBMc="
}
Where:
- "mti" should be 0x30, it is the code of the SDEV AuthN
- "sn" is a sequence number that should be incremented every message exchanged from the two endpoints.
- "sessionId" is the id of the session.
- "authn" should be calculated in the following mode (based on the cc and kdf currently supported):
authn = B64(cryptAuthN)
where: cryptAuthN = AES128_CBC(hasedData || sn) encrypted with DK1.
where: sn is the sequence number as character string hasedData = SHA1(SDEVnonce || GWnonce)
- SDEVnonce is the nonce sent from the SDEV in mti=0x10
- GWnonce is the nonce sent from the Innkeeper in mti=0x20
- "sign" is the sign of the encrypted data. It is calculated as follows:
sign = B64(SHA1-HMAC(cryptAuthN)) , where SHA1-HMAC is used with the DK2.
This is a Json message like this:
{
"mti": "0x40",
"sign": "ZarEGFpxKzio/6W8G+tV1ZFfsRM=",
"sessionId": "8L6hAZyv",
"sn": "db06b",
"authn": "u10tAKS5X3oLm4cpZi9esbntXqwwu+X+9g34TvuOXgE=",
"nonce": "9b0efad8"
}
Where:
- "mti" should be 0x40, it is the code of the GW AuthN
- "sn" is a sequence number that should be incremented every message exchanged from the two endpoints. It is the previous sent from SDEV incremented of one unit.
- "sessionId" is the id of the session.
- "authn" should be calculated in the same way as the SDEV AuthN message (note that the value is different from the previous calculated one since the sn is incremented of 1 unit)
- "sign" this is the sign of the encrypted data, calculated in the same way as the SDEV AuthN message.
Once this set of messages are exchanged, LWSP is up and running and the agent can use the LWSP to send/receive encrypted data. Two other messages are defined.
This is a Json message like this:
{
"mti": "0x50",
"sessionId": "8L6hAZyv",
"data": "uVJt9VGH15oKBaKyCGwA9tOd2jUqMRfBh8ZtCrakBTzqzwwWIPZKtPe3xSBQ1BaAN2AG/scC3AGgjGUuAMv5XU3+ha1O59owJqgLQ=="
}
Where:
- "mti" should be 0x50, it is the code of the encrypt data coming from agent to Innkeeper
- "sessionId" is the id of the session.
- "data" is the encrypted data as base64 string. It is calculated as follow:
data = AES128_CBC(message || sn) encrypted with DK1.
where: sn is the sequence number as character string message is the data to be encrypted
This is a Json message like this:
{
"mti": "0x60",
"data": "iNr5/wL0aR04zFTNBsD0+Ccwk2p2hSG6t1MO4nWAf32wCV1FS81Zd3oOf1ycTuYmo=",
"sessionId": "8L6hAZyv"
}
Where:
- "mti" should be 0x60, it is the code of the encrypt data coming from Innkeeper to Agent
- "sessionId" is the id of the session.
- "data" is the encrypted data as base64 string. It is calculated as the previous message (mti=0x50)
The agent should develop the following series of interfaces that can be divided in two class:
- The first class of interfaces should use the LWSP tunnel to send data, thus encrypt data in message type mti=0x50 and mti=0x60
- The second class of interfaces flow as body of simple HTTP POST request, as plain text
The following interfaces are shown as plain text Json request, but please take in mind that you should cypth this data using the LWSP message mti=0x50 (for the request from agent to innkeeper). For the response coming from the innkeeper the same story, here are shown the response as plain text, but remember that you should expect a message with mti=0x60 and decrypting the "data" field you get back the Json response.
Interface | path | From | To | Description |
---|---|---|---|---|
register SDEV | /innkeeper/sdev/register/ | agent | Innkeeper | Registers SDEV. |
join | /innkeeper/sdev/join/ | agent | Innkeeper | Register every single resource of the agent. |
keep-alive | /innkeeper/keep_alive/ | agent | Innkeeper | Notify that SDEV is alive |
unregister SDEV | /innkeeper/sdev/unregister | Agent | Innkeeper | De-register the SDEV in the SSP |
Request:
{
"symId": "",
"pluginId": "55:66:77:3a:61:76",
"sspId": "",
"roaming": false,
"pluginURL": "http://10.20.30.5/rap/v1/request",
"dk1": "5f6377981f97fb5be6d6dfbb3a32e372",
"hashField": "00000000000000000000"
}
Where:
- "symId" is the symbiote Id, if it is the first time ever the SDEV connects to a SSP or not a roaming SDEV it could be empty, otherwise is the assigned symbiote Id (SDEV thus need to save the symId returned from the Innkeeper in the last registration)
- "pluginId" is the name with which the Agent recognize itself in the SSP. it is the identifier used from the RAP to address request to the agent.
- "sspId" is the local identifier of the agent inside the SSP, used in case of no internet connectivity
- "pluginURL" is the URL at which the agent respond at the RAP data request
- "dk1" it is the derived key of the LWSP
- "hashfield" is a field used in roaming. if it is the first time ever the SDEV connects to a SSP or not a roaming SDEV it is "00000000000000000000", otherwise it is calculated as follows:
hashfield = SHA1(symIdSDEV || previous dk1)
Response:
{
"symId": "",
"sspId": "4",
"result": "OFFLINE",
"registrationExpiration": 3600
}
Where:
- "symId" is the assigned symbiote Id, it could be empty if the SSP has no internet connection
- "sspId" is the local identifier of the agent inside the SSP, used in case of no internet connectivity
- "result" it is the result of the Register, it could be "OK", "REJECT", "ALREADY_REGISTERED"
- "registrationExpiration" is the alive time of this registratin, SDEV should send periodically keep-alive message to refresh this expiration time. Value time is in milliseconds.
Request as plain Text:
{
"internalIdResource": "5c:cf:7f:3a:6b:76",
"sspIdResource": "",
"sspIdParent": "4",
"symIdParent": "",
"accessPolicy": {
"policyType": "PUBLIC",
"requiredClaims": {}
},
"filteringPolicy": {
"policyType": "PUBLIC",
"requiredClaims": {}
},
"resource": {
"@c": ".Actuator",
"id": "",
"name": "ACT-SDEV_6B:76",
"description": null,
"interworkingServiceURL": "http://10.20.30.5/rap/v1/request",
"locatedAt": null,
"services": null,
"capabilities": [
{
"name": "RGBCapability",
"parameters": [
{
"name": "r",
"datatype": {
"@c": ".PrimitiveDatatype",
"baseDatatype": "xsd:unsignedByte",
"isArray": false
},
"mandatory": true,
"restrictions": [
{
"@c": ".RangeRestriction",
"min": 0,
"max": 255
}
]
},
{
"name": "g",
"datatype": {
"@c": ".PrimitiveDatatype",
"baseDatatype": "xsd:unsignedByte",
"isArray": false
},
"mandatory": true,
"restrictions": [
{
"@c": ".RangeRestriction",
"min": 0,
"max": 255
}
]
},
{
"name": "b",
"datatype": {
"@c": ".PrimitiveDatatype",
"baseDatatype": "xsd:unsignedByte",
"isArray": false
},
"mandatory": true,
"restrictions": [
{
"@c": ".RangeRestriction",
"min": 0,
"max": 255
}
]
}
],
"effects": null
}
]
}
}
Where:
- "internalIdResource" is the internal identifier used from the agent, it is used by the RAP to addresses requests.
- "sspIdResource" is the sspId of that resource, it is returned from the innkeeper
- "sspIdParent" is the sspId os the container SDEV
- "symIdParent" is the symbioteId of the SspSDEVInfo class, the "conteiner" SDEV of that resource
- "accessPolicy" is the accessPolicy class, for a public resource you can use the description shown in the example
- "filteringPolicy" is the filtering class, for a public resource you can use the description shown in the example
- "resource" is the resource class that describes the semantic of the resource
Response:
{
"registrationExpiration": 3600,
"symIdResource": "",
"sspIdResource": "0",
"symId": "",
"sspId": "4",
"result": "OFFLINE"
}
Where:
- "registrationExpiration" is the alive time of this registration, SDEV should send periodically keep-alive message to refresh this expiration time. Value time is in milliseconds.
- "symIdResource" is the retuned symbioteId of the resource
- "sspIdResource" is the sspId of that resource
- "symId" is the symbioteId of the SspSDEVInfo class, the "conteiner" SDEV of that resource
- "result" it is the result of the Join, it could be "OK", "REJECT", "ALREADY_REGISTERED"
Request:
{
"sspId": "4"
}
Where:
- "sspId" is the sspId of the SDEV container, the SspSDEVInfo class
Response:
{
"symId": "",
"sspId": "4",
"result": "OFFLINE",
"updatedSymId": [
{
"sspIdResource": "0",
"symIdResource": ""
},
{
"sspIdResource": "1",
"symIdResource": ""
}
]
}
Where:
- "symId" is the symbioteId of the SspSDEVInfo class, the "conteiner" SDEV of that resource
- "sspId" is the sspId os the container SDEV
- "result" it is the status of SSP, it could be "ONLINE" or "OFFLINE"
- "updatedSymId" is a list of updated identifier for the resources. It could be useful if the SSP change its status form OFFLINE to ONLINE
Request:
{
"sspId": "4"
}
Where:
- "sspId" is the sspId of the SDEV container, the SspSDEVInfo class
Response: It is just a HTTP 200.
These interfaces, are sent as plain-text Json in the body of the HTTP POST:
Interface | path | From | To | Description |
---|---|---|---|---|
RAP GET | /rap/v1/request | RAP | Agent | Get data from the agent |
RAP HISTORY | /rap/v1/request | RAP | Agent | Get historic data from the agent |
RAP SUBSCRIBE | /rap/v1/request | RAP | Agent | Request a push subscription of data from agent |
RAP UNSUBSCRIBE | /rap/v1/request | RAP | Agent | Delete the subscribe request |
RAP SET | /rap/v1/request | RAP | Agent | Actuate the resource from the agent |
Request from RAP:
{
"resourceInfo": [
{
"symbioteId": "",
"internalIdResource": "5c:cf:7f:3a:6b:76",
"type": "Light"
},
{
"type": "Observation"
}
],
"type": "GET"
}
Where:
-
"resourceInfo":
- "symbioteId" is the symbiote-Id of the SDEV
- "internalIdResource" is the internal identifier used from the agent, it is used by the RAP to addresses requests
- "type" is the semantic type of the resource that RAP wants to get information, it is related to the semantic description of the resource.
- "type": "Observation", always as this.
-
"type": "GET", always as this.
Response from Agent:
{
"resourceId": "5c:cf:7f:3a:6b:76",
"location": {
"longitude": -2.944728,
"latitude": 43.26701,
"altitude": 20
},
"resultTime": "1970-1-1T02:00:12",
"samplingTime": "1970-1-1T02:00:12",
"obsValues": [
{
"value": "25.19",
"obsProperty": {
"@c": ".Property",
"name": "temperature",
"description": ""
},
"uom": {
"@c": "UnitOfMeasurment",
"symbol": "°C",
"name": "°C",
"description": ""
}
},
{
"value": "1009.34",
"obsProperty": {
"@c": ".Property",
"name": "pressure",
"description": ""
},
"uom": {
"@c": "UnitOfMeasurment",
"symbol": "mBar",
"name": "mBar",
"description": ""
}
}
]
}
Where:
- "resourceId" is the resource identifier, the internalIdResource.
- "location" is a Json with the geographic collocation, you can leave also as null if no information available
- "longitude" is the longitude
- "latitude" is the latitude
- "altitude" is the altitude
- "resultTime" is time of the end of measurement
- "samplingTime" is the time of the start of the measurement
- "obsValues" is the ObservationValue Class, as defined in the CIM (Core Information Model) https://github.com/symbiote-h2020/SymbIoTeLibraries/tree/master/src/main/java/eu/h2020/symbiote/model/cim
- "uom" is the UnitOfMeasurment Class, as defined in the CIM (Core Information Model) https://github.com/symbiote-h2020/SymbIoTeLibraries/tree/master/src/main/java/eu/h2020/symbiote/model/cim
Request from RAP:
{
"resourceInfo": [
{
"symbioteId": "",
"internalIdResource": "5c:cf:7f:3a:6b:76",
"type": "Light"
},
{
"type": "Observation"
}
],
"type": "HISTORY"
}
Where:
-
"resourceInfo":
- "symbioteId" is the symbiote-Id of the SDEV
- "internalIdResource" is the internal identifier used from the agent, it is used by the RAP to addresses requests
- "type" is the semantic type of the resource that RAP wants to get information, it is related to the semantic description of the resource.
- "type": "Observation", always as this.
-
"type": "HISTORY", always as this.
Response from Agent:
[
{
"resourceId": "5c:cf:7f:3a:6b:76",
"location": {
"longitude": -2.944728,
"latitude": 43.26701,
"altitude": 20
},
"resultTime": "1970-1-1T02:00:16",
"samplingTime": "1970-1-1T02:00:16",
"obsValues": [
{
"value": "25.19",
"obsProperty": {
"@c": ".Property",
"name": "temperature",
"description": ""
},
"uom": {
"@c": "UnitOfMeasurment",
"symbol": "°C",
"name": "°C",
"description": ""
}
},
{
"value": "1009.31",
"obsProperty": {
"@c": ".Property",
"name": "pressure",
"description": ""
},
"uom": {
"@c": "UnitOfMeasurment",
"symbol": "mBar",
"name": "mBar",
"description": ""
}
}
]
}
]
The response is the same of the GET, but it is actually an array of Json like the GET response.
Request from RAP:
{
"resourceInfo" : [ {
"symbioteId" : "abcdefgh",
"internalId" : "123456",
"type" : "Light"
}, {
"type" : "Observation"
} ],
"type" : "SUBSCRIBE"
}
Where:
-
"resourceInfo":
- "symbioteId" is the symbiote-Id of the SDEV
- "internalIdResource" is the internal identifier used from the agent, it is used by the RAP to addresses requests
- "type" is the semantic type of the resource that RAP wants to get information, it is related to the semantic description of the resource.
- "type": "Observation", always as this.
-
"type": "SUBSCRIBE", always as this.
Response from Agent: It is just an HTTP 200.
Request from RAP:
{
"resourceInfo" : [ {
"symbioteId" : "abcdefgh",
"internalId" : "123456",
"type" : "Light"
}, {
"type" : "Observation"
} ],
"type" : "UNSUBSCRIBE"
}
Where:
-
"resourceInfo":
- "symbioteId" is the symbiote-Id of the SDEV
- "internalIdResource" is the internal identifier used from the agent, it is used by the RAP to addresses requests
- "type" is the semantic type of the resource that RAP wants to get information, it is related to the semantic description of the resource.
- "type": "Observation", always as this.
-
"type": "UNSUBSCRIBE", always as this.
Response from Agent: It is just an HTTP 200.
{
"resourceInfo" : [ {
"symbioteId" : "{symbioteId}",
"internalId" : "{internalId}",
"type" : "{Model}"
} ],
"body" : {
"{capability}": [
{ "{restriction}": "{value}" }
]
},
"type" : "SET"
}
Where:
-
"resourceInfo":
- "symbioteId" is the symbiote-Id of the SDEV
- "internalIdResource" is the internal identifier used from the agent, it is used by the RAP to addresses requests
- "type" is the semantic type of the resource that RAP wants to get information, it is related to the semantic description of the resource.
-
"body" contains the actuation to do on the resource:
- "capability" is the Capability Class, as defined in the CIM (Core Information Model) https://github.com/symbiote-h2020/SymbIoTeLibraries/tree/master/src/main/java/eu/h2020/symbiote/model/cim. Inside this class there are the restriction values that represents the values to change on the resource capability.
-
"type": "SET", always as this.