The IoT RESTful API defines a RESTful web service interface, in which users can perform CREATE, READ, UPDATE, and DELETE (CRUD) actions to any uniquely-identifiable resources in the service.
The OGC IoT RESTful service interface is different from the other OGC web services, in that it is based on RESTful web service style and JSON encoding. The OGC IoT RESTful API is inspired by the OASIS Open Data Protocol (OData), which defines a general-purpose RESTful service interface. The IoT RESTful service interface is very similar to the OData but specifically designed for the IoT.
Besides the OData, the IoT RESTful service interface also leverages the existing and widely implemented OGC standards. For example, the capabilities part of the OGC IoT RESTful service interface adapts some elements from the GetCapabilities response defined in the OWS Common by converting the XML encoding into the JSON encoding.
The OGC IoT RESTful API consists of two major profiles: the Sensing Profile and the Tasking Profile. The Sensing Profile is designed based on the OGC Sensor Observation Service (SOS) specification in which defines an interoperable framework to manage and access sensors and observations. The Tasking Profile is based on the OGC Sensor Planning Service (SPS) specification, in which defines an interoperable way to submit tasks to control sensors and actuators.
In the following subsections, we first describe the URI Components for addressing resources and possible query options. Then we define the protocol of retrieving the capabilities document. Finally, we explain the CRUD requests and responses in the Sensing Profile and Tasking Profile.
In this API reference page, we mainly focus on describing the behaviour of the OGC IoT RESTful API without presenting detailed examples. Instead, the complete data model and JSON examples of resources can be found in the Data Model page.
Similar to the OASIS OData, the OGC IoT RESTful API groups the same types of entities into collections. Each entity has a unique identifier and one to many properties. Also, in the case of an entity holding a relationship with entities in another collection, this entity has a navigation property (i.e., a link) linking to other entities. The complete data model and JSON examples of resources can be found in the Data Model page.
Therefore, in order to perform CRUD actions on the resources, the first step is to address to the target resource(s) through URI. There are three major URI components used in this API: the service root URI, the Resource Path, and the Query Options. The service root URI is the location of the IoT RESTful service. By attaching the resource path after the service root URI, users can address to different kinds of resources such as a collection, an entity, a property, or a navigation property. Finally, when users perform the READ (i.e., HTTP GET) action on resource(s), users can apply query options to further process the addressed resources, such as sorting by properties and filtering with criteria.
The resource path comes right after the service root URI and can be used to address to different resources. The following lists the general usages of the resource path.
Usage 1: No resource path
If a user request does not include any resource path, the IoT RESTful service returns a list of collection names as the service document.
URI Pattern: SERVICE_ROOT_URI
Response: A list of collection names.
Example: http://demo.student.geocens.ca:8080/SWIOT_V0.9/
Usage 2: Address to an entity collection
To address to an entity collection, users can simply put the collection name after the service root URI. The service returns a list of all the entities in the specified collection.
URI Pattern: SERVICE_ROOT_URI/COLLECTION_NAME
Response: A list of all entities (with all the properties) in the specified collection (if no service-driven pagination imposed).
Example: http://demo.student.geocens.ca:8080/SWIOT_V0.9/Things
Usage 3: Address to an entity in a collection
Users can address to a specific entity in a collection by place the unique identifier of the entity between brace symbol “()” and put after the collection name. The service then returns the entity with all its properties.
URI Pattern: SERVICE_ROOT_URI/COLLECTION_NAME(KEY_OF_ENTITY)
Response: An entity (with all its properties) that holds the specified key in the collection.
Example: http://demo.student.geocens.ca:8080/SWIOT_V0.9/Things(1)
Usage 4: Address to a property of an entity
Users can address to a property of an entity by specifying the property name after the URI addressing to the entity. The service then returns the value of the specified property.
URI Pattern: SERVICE_ROOT_URI/COLLECTION_NAME(KEY_OF_ENTITY)/PROPERTY_NAME
Response: The specified property of an entity that holds the key in the collection.
Example: http://demo.student.geocens.ca:8080/SWIOT_V0.9/Observations(1)/Time
Usage 5: Address to a navigation property (link)
As the entities in different collections may hold some relationships, users can request the linked entities by addressing to a navigation property of an entity. The service then returns one or many entities that hold a certain relationship with the specified entity.
URI Pattern: SERVICE_ROOT_URI/COLLECTION_NAME(KEY_OF_ENTITY)/LINK_NAME
Response: One or many entities that holds a certain relationship with the specified entity.
Example: http://demo.student.geocens.ca:8080/SWIOT_V0.9/Datastreams(1)/Observations returns all the observations in the datastream.
Usage 6: Nested resource path
As users can use navigation properties to link from one collection to another, users can further extend the resource path with unique identifiers, properties, or links (i.e., Usage 3 to 5).
Example 1: http://demo.student.geocens.ca:8080/SWIOT_V0.9/Datastreams(1)/Observations(1) returns a specific observation in the datastream.
Example 2: http://demo.student.geocens.ca:8080/SWIOT_V0.9/Datastreams(1)/Observations(1)/Time turns the “Time” property of the specified observation in the datastream.
Example 3: http://demo.student.geocens.ca:8080/SWIOT_V0.9/Datastreams(1)/Observations(1)/FeatureOfInterest returns the feature of interest entity of the specified observation in the datastream.
When users perform the READ action on resource(s), users can apply query options to further process the addressed resources. As the read action is performed through HTTP GET method, the query options are specified as key-value pairs after the question symbol "?" in the request URI. Currently, many of the query options defined in the OGC IoT RESTful API are similar to the query options in the OData. Therefore, some of the following explanations can be referenced to the OData specification.
NOTE: As our reference implementation hasn't supported all the functions proposed here, we use some examples from the OData test service (http://services.odata.org) to illustrate these functions. As OData might change their demo service, you might find some of the following OData examples not working. And some of the functions, there are no examples available, and these examples uses a pseudo-service root URL (e.g., http://example.com/)
The $orderby query option is used to specify which properties are used to order the collection of entities identified by the resource path. This query option is only supported when the resource path identifies a collection of entries. The value of the $orderby query option contains a comma separated list of expressions whose primitive result values are used to sort the results. A special case of such an expression is a property path terminating on a primitive property.
The expression may include the suffix asc for ascending or desc for descending, separated from the property name by one or more spaces. If asc or desc is not specified, the service must order by the specified property in ascending order.
Null values come before non-null values when sorting in ascending order and after non-null values when sorting in descending order.
Example 1: http://services.odata.org/V3/OData/OData.svc/Products?$orderby=Rating returns all Products ordered by the Rating property in ascending order.
Example 2: http://services.odata.org/V3/OData/OData.svc/Products?$orderby=Rating,Category/Name desc returns all Products ordered by the Rating property in ascending order, then by the Name property of the linked Category entry in descending order.
The $top query option specifies a non-negative integer that limits the number of entities returned within a collection. The service must return the number of available entities up to, but not exceeding, the specified value.
Usually, the $top query option is used with the $orderby query option. However, while no ordering semantics are mandated, to ensure repeatable results, a data service must always use the same semantics to obtain a full ordering across requests.
In addition, if the $top value exceeds the service-driven pagination limitation (i.e., the largest number of entities the service can return in a single response), the $top query option should be discarded and the pagination limitation should be imposed.
Example 1: http://services.odata.org/V3/OData/OData.svc/Products?$top=5 returns only the first five entities in the Products collection.
Example 2: http://services.odata.org/V3/OData/OData.svc/Products?$top=5&$orderby=Name desc returns the first five Product entries returned in descending order when sorted by the Name property.
The $skip query option specifies that the result must not include the first n entities, where n is a non-negative integer value specified by the $skip query option.
Usually, the $skip query option is used with the $orderby query option. However, while no ordering semantics are mandated, to ensure repeatable results, a data service must always use the same semantics to obtain a full ordering across requests.
Where $top and $skip are used together, the $skip must be applied before the $top, regardless of the order in which they appear in the request.
Example 1: http://services.odata.org/V3/OData/OData.svc/Products?$skip=5 returns Product entities starting with the sixth Product entity in the Products collection.
Example 2: http://services.odata.org/V3/OData/OData.svc/Products?$skip=2&$top=2&$orderby=Rating returns the third and fourth Product entities from the collection of all Product entities when the collection is sorted by the Rating property in ascending order.
A URI with a $filter query option identifies a subset of the entries from the collection of entries identified by the resource path of the URI. The subset is determined by selecting only the entries that satisfy the predicate expression specified by the $filter. The value of the $filter option is a Boolean expression.
The expression language that is used in $filter operators supports references to properties and literals. The literal values can be strings enclosed in single quotes, numbers and boolean values (true or false) or datetime values represented as datetime‘ISO 8601 time string’.
Example: http://services.odata.org/V3/OData/OData.svc/Products?$filter=Price lt 10.00 returns all Products whose Price is less than 10.00.
In addition, users may use the properties of linked entities in the $filter predicate. The following are examples of the possible uses of the $filter in the data model of the IoT RESTful service.
Example 1: ttp://iot-rest.example.com/Observations?$filter=ObservedProperty/UnitOfMeasurement eq ‘Celsius’ returns all Observations whose UnitOfMeasurement (a property of ObservedProperty) is ‘Celsius’.
Example 2: http://iot-rest.example.com/Things?$filter=Locations/Geometry st_within(POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))) returns Things whose current or past locations were in the polygon defined in the Well-Known Text (WKT).
Example 3: http://iot-rest.example.com/ Things?$filter=Sensors/Observations/FeatureOfInterest/ID eq ‘FOI_1’ and Sensors/Observations/Time ge datetime‘2010-06-01T00:00:00Z’ and Sensors/Observations/Time le datetime‘2010-07-01T00:00:00Z’ returns Things that made any observations on a feature of interest with a unique identifier equals to “FOI_1” in June 2010.
The OGC IoT RESTful API supports a set of built-in filter operations, as described in the following table.
Operator | Description | Example |
---|---|---|
Logical Operators | ||
eq | Equal | /Suppliers?$filter=Address/City eq 'Redmond' |
ne | Not equal | /Suppliers?$filter=Address/City ne 'London' |
gt | Greater than | /Products?$filter=Price gt 20 |
ge | Greater than or equal | /Products?$filter=Price ge 10 |
lt | Less than | /Products?$filter=Price lt 20 |
le | Less than or equal | /Products?$filter=Price le 100 |
and | Logical and | /Products?$filter=Price le 200 and Price gt 3.5 |
or | Logical or | /Products?$filter=Price le 3.5 or Price gt 200 |
not | Logical negation | /Products?$filter=not endswith(Description,'milk') |
Arithmetic Operators | ||
add | Addition | /Products?$filter=Price add 5 gt 10 |
sub | Subtraction | /Products?$filter=Price sub 5 gt 10 |
mul | Multiplication | /Products?$filter=Price mul 2 gt 2000 |
div | Division | /Products?$filter=Price div 2 gt 4 |
mod | Modulo | /Products?$filter=Price mod 2 eq 0 |
Grouping Operators | ||
( ) | Precedence grouping | /Products?$filter=(Price sub 5) gt 10 |
IoT RESTful API supports a set of built-in functions that can be used within $filter operations. The following table lists the available functions. Most of these query functions are defined in the OData. However, in order to support more geospatial query functions, IoT RESTful API defines nine additional geospatial functions based on the spatial relationship between two geometry objects defined in the OGC Simple Feature Access specification (part 1, clause 6.1.2.3). The names of these nine functions start with a prefix “st_” as they are named in the OGC Simple Feature Access specification. In addition, for these eight functions, we define that users must specify input geometry in the Well-Known Text (WKT) format.
Function | Example |
---|---|
String Functions | |
bool substringof | substringof('Alfreds',CompanyName) |
bool endswith | endswith(CompanyName,'Futterkiste') |
bool startswith | startswith(CompanyName,'Alfr') |
int length | length(CompanyName) eq 19 |
int indexof | indexof(CompanyName,'lfreds') eq 1 |
string substring | substring(CompanyName,1) eq 'lfreds Futterkiste' |
string tolower | tolower(CompanyName) eq 'alfreds futterkiste' |
string toupper | toupper(CompanyName) eq 'ALFREDS FUTTERKISTE' |
string trim | trim(CompanyName) eq 'Alfreds Futterkiste' |
string concat | concat(concat(City,', '), Country) eq 'Berlin, Germany' |
Date Functions | |
DateTime now | StartTime ge now() |
int year | year(BirthDate) eq 0 |
int month | month(BirthDate) eq 12 |
int day | day(StartTime) eq 8 |
int hour | hour(StartTime) eq 1 |
int minute | minute(StartTime) eq 0 |
int second | second(StartTime) eq 0 |
double fractionalseconds | second(StartTime) eq 0 |
Date date | date(StartTime) ne date(EndTime) |
TimeOfDay time | time(StartTime) le StartOfDay |
int totaloffsetminutes | totaloffsetminutes(StartTime) eq 60 |
DateTimeOffset mindatetime | StartTime eq mindatetime() |
DateTimeOffset maxdatetime | EndTime eq maxdatetime() |
Math Functions | |
double round | round(Freight) eq 32 |
double floor | floor(Freight) eq 32 |
double ceiling | ceiling(Freight) eq 33 |
Type Functions | |
any cast | cast(ShipCountry,Edm.String) |
bool isof | isof(NorthwindModel.Order) |
bool isof | isof(ShipCountry,Edm.String) |
Geospatial Functions | |
double geo.distance | geo.distance(CurrentPosition, TargetPosition) |
double geo.length | geo.length(DirectRoute) |
bool geo.intersects | geo.intersects(Position, TargetArea) |
bool st_equals | st_equals(POINT (30 10)) |
bool st_disjoint | st_disjoint(POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))) |
bool st_touches | st_touches(LINESTRING (30 10, 10 30, 40 40)) |
bool st_within | st_within(POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))) |
bool st_overlaps | st_overlaps(POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))) |
bool st_crosses | st_crosses(LINESTRING (30 10, 10 30, 40 40)) |
bool st_intersects | st_intersects(LINESTRING (30 10, 10 30, 40 40)) |
bool st_contains | st_contains(POINT (30 10)) |
bool st_relate | st_relate(POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10)), ‘T********’) |
The $expand system query option indicates the related entities that must be represented inline. The value of the $expand query option must be a comma separated list of navigation property names. Additionally each navigation property can be followed by a forward slash and another navigation property to enable identifying a multi-level relationship.
Example 1: http://services.odata.org/V3/OData/OData.svc/Categories?$expand=Products returns the collection of Categories as well as each of the Products associated with each Category entity.
Example 2: http://services.odata.org/V3/OData/OData.svc/Categories?$expand=Products/Suppliers returns the collection of Categories, the Products associated with each Category, and the Suppliers associated with each Product.
Example 3: http://services.odata.org/V3/OData/OData.svc/Products?$expand=Category,Suppliers returns the collection of Products as well as the Category and Suppliers associated with each Product.
A data service URI with a $select query option identifies the same set of entries as a URI without a $select query option; however, the value of $select specifies that a response from an IoT RESTful service should return a subset of the properties. The value of a $select query option is a comma-separated list of selection clauses. Each selection clause may be a property name (including navigation property names).
Example 1: http://services.odata.org/V3/OData/OData.svc/Products?$select=Price,Name returns only the Price and Name properties for each Product entity.
Example 2: http://service.odata.org/Categories?$select=Name,Products&$expand=Products/Supplier returns the Name properties of the Category entries, and all the properties of the entries identified by the Products and Suppliers navigation properties.
The $count query option with a value of “true” specifies that the total count of entities within a collection matching the request must be returned along with the result. The $count system query option ignores any $top, $skip, or $expand query options, and returns the total count of results across all pages including only those results matching any specified $filter.
A $count with a value of “false” (or not specified) hints that the service should not return a count.
Example: http://services.odata.org/V3/OData/OData.svc/Products?$count=true returns, along with the results, the total number of products in the set.
This operation allows clients to retrieve the service metadata about a specific service instance. The $select is the only query option can be applied here. So users can only request for a partial of the capabilities document.
HTTP Method: GET
URI Pattern: SERVICE_ROOT_URI/$capabilities
Example: http://demo.student.geocens.ca:8080/SWIOT_V0.9/$capabilities
The capabilities document is a JSON encoded document. This document provides client with service metadata about a specific service instance. A service should use the capabilities document to advertise the supported functions and any constraints on using these functions. A service may also use the capabilities document to showcase the spatio-temporal coverage of the content. Finally, if a service provides any custom operations, the explanation of those operations should be included in the capabilities document as well.
The detail explanation about the capabilities document can be found in datamodel.html#capabilities.
The OGC IoT RESTful API sensing profile is based on the OGC SOS concept and designed specifically for the Internet of Things.
As most IoT devices are resource-constrained, the IoT RESTful API adopts the efficient RESTful web service style. That means the CRUD actions can be performed on Thing, Location, Datastream, Sensor, Observation, FeatureOfInterest, and ObservedProperty entity types. The following subsection explains the CRUD protocol for the Sensing Profile.
The detail explanation about the resources in the Sensing Profile can be found here.
To create an entity in a collection, the client sends a HTTP POST request to that collection's URL. The POST body must contain a single valid entity representation. In addition, the link between entities should be established upon creating an entity. Two use cases should be considered: (1) link to existing entities when creating an entity, and (2) create related entities when creating an entity. The requests for these two use cases are also described in the following subsection.
When users create resources in an IoT RESTful service, they need to follow some integrity constraints. For example, a Datastream entity must link to a Thing entity. So when a user wants to create a Datastream entity, he/she needs to either (1) create a linked Thing entity in the same request or (2) link to a already created Thing entity. The complete integrity constraints for creating resources are shown in the following table.
One special case is on creating an Observation entity that links to an FeatureOfInterest entity. Sometimes the FeatureOfInterest of an Observation is the Thing itself where the Observation is similar to an attribute of the Thing. In this cases, when a user creates an Observation entity, he/she can omit the link to a FeatureOfInterest entity in the POST body message. And if the service detects that there is no link to a FeatureOfInterst entity in a create-Observation entity request, the service must automatically create a FeatureOfInterest entity by interpolating the geometry property from the locations of the Thing entity.
Scenario | Integrity Constraints |
---|---|
Create a Thing entity | - |
Create a Location entity | Link to a Thing entity. |
Create a Datastream entity | Link to a Thing entity. |
Link to an ObservedProperty entity. | |
Create a Sensor entity | Link to a Datastream entity. |
Create an ObservedProperty entity | - |
Create an Observation entity | Link to a Datastream entity. |
Link to a FeatureOfInterest entity. (If no link specified, the service must create a FeatureOfInterest entity from the information of the Thing and Location entities.) |
|
Create a FeatureOfInterest entity | - |
Create a TaskingCapability entity | Link to a Thing entity. |
Create an Actuator entity | Link to a TaskingCapability entity. |
Create a Task entity | Link to a TaskingCapability entity. |
More examples about the POST body messages for different resources can be found in the Interactive SDK.
HTTP Method: POST
URI Pattern: SERVICE_ROOT_URI/COLLECTION_NAME
Header: Content-Type: application/json
Message Body: A single valid entity representation for the specified collection.
Example: Create a Thing entity
POST /SWIOT_V0.9/Things HTTP/1.1
Host: demo.student.geocens.ca:8080
Content-Type: application/json
{
"Description":"This thing is an oven."
}
A service should support linking new entities to existing entities upon creation. To create a new entity with links to existing entities in a single request, the client needs to include the unique identifiers of the related entities associated with the corresponding navigation properties in the request body.
Note: In the case of creating an observation that measures on the thing itself (i.e., the observation can be interpreted as an attribute of the thing), the request of creating the observation must not include a link to a feature of interest entity. The service will first automatically create a feature of interest entity from the thing and the locations of the thing and then link to the observation.
Example: Create an Observation entity, which links to an existing Sensor entity (whose ID is 1), an existing FeatureOfInterest entity (whose ID is 2), and an existing ObservedProperty entity (whose ID is 3).
POST /SWIOT_V0.9/Observations HTTP/1.1
Host: demo.student.geocens.ca:8080
Content-Type: application/json
{
"Datastream":{"ID":1},
"Time":"2013-04-18T16:15:00-0700",
"ResultValue":"124",
"ResultType" : "Measure",
"FeatureOfInterest":{"ID":1}
}
Upon successfully creating an entity, the service response must contain a Location header that contains the URL of the created entity. Upon successful completion the service must respond with 201 Created. Regarding all the HTTP status code, please refer to the HTTP Status Code section.
HTTP Method: GET
URI Pattern: Refer to the URIComponents section, including resource path and query options
The detail explanation about the encodings of resources in the datastream profile can be found in datamodel.html#DatastreamProfile.
Upon successfully retrieve resources, the service service must respond with 200 OK. Regarding all the HTTP status code, please refer to the HTTP Status Code section.
Services should support HTTP PATCH as the preferred means of updating an entity. HTTP PATCH provides more resiliencies between clients and services by directly modifying only those values specified by the client.
The semantics of PATCH, as defined in [RFC5789], are to merge the content in the request payload with the entity’s current state, applying the update only to those components specified in the request body. Collection properties and primitive properties provided in the payload must replace the value of the corresponding property in the entity or complex type. Missing properties of the containing entity or complex property, including dynamic properties, must not be directly altered.
Services may additionally support HTTP PUT, but should be aware of the potential for data-loss in round-tripping properties that the client may not know about in advance. Services that support HTTP PUT must replace all property values with those specified in the request body. Missing properties must be set to their default values. Missing optional properties must be removed.
Unique identifiers and other non-updatable properties may be omitted from the request. If the request contains a value for an identifier or other non-updatable property, the service must ignore that value when applying the update.
The entity must not contain related entities as inline content. Additional values for properties beyond those specified in the IoT RESTful data model should not be sent in the request body. The service should consider such a request malformed.
HTTP Method: PATCH or PUT
URI Pattern: An URI addressing to a single entity.
Header: Content-Type: application/json
Message Body: A single entity representation including a subset of properties for the specified collection.
Example: Update the Thing with key equals to 1
PATCH /SWIOT_V0.9/Things(1) HTTP1.1
Host: demo.student.geocens.ca:8080
Content-Type: application/json
{
"Description":"This thing is an oven own by the Toms."
}
On success, the response must be a valid success response. In addition, when the client sends an update request to a valid URL where an entity does not exist, the service must fail the request.
Upon successful completion, the service service must respond with 200 OK or 204 No Content. Regarding all the HTTP status code, please refer to the HTTP Status Code section.
To delete an existing entity, the client sends a HTTP DELETE request to that entity's edit URL. The request body should be empty.
HTTP Method: DELETE
URI Pattern: An URI addressing to a single entity.
Example: Delete the Thing with unique identifier equals to 1
HTTP DELETE http://demo.student.geocens.ca:8080/SWIOT_V0.9/Things(1)
Services must implicitly remove relations to and from an entity when deleting it; clients need not delete the relations explicitly.
Services should implicitly delete related entities if required by the following integrity constraints.
Scenario | Integrity Constraints |
---|---|
Delete a Thing entity | Delete all the Datastream entities linked to the Thing entity. |
Delete all the TaskingCapability entities linked to the Thing entity. | |
Delete the Location entities that will not link to any entities after deletion. | |
Delete a Location entity | - |
Delete a Datastream entity | Delete the Sensor entity linked to the Datastream entity. |
Delete all the Observation entities linked to the Datastream entity. | |
Delete a Sensor entity | - |
Delete an ObservedProperty entity | Delete all the Datastream entities linked to the ObservedProperty entity. |
Delete an Observation entity | - |
Delete a FeatureOfInterest entity | Delete all the Observation entities linked to the FeatureOfInterest entity. |
Delete a TaskingCapability entity | Delete the Actuator entity linked to the TaskingCapability entity. |
Delete an Actuator entity | - |
Delete a Task entity | - |
On successful completion of the delete, the response must be 200 OK, 202 Accepted, or 204 No Content and contain an empty body. Regarding all the HTTP status code, please refer to the HTTP Status Code section.
The IoT RESTful TaskingCapability profile is mainly for users to control actuators through a unified web service interface. There are two major parts in the TaskingCapability profile. The first part is to convey the acceptable parameters of an actuator and allow users to submit controlling tasks through a RESTful interface. The second part in the TaskingCapability profile is to allow IoT RESTful services to communicate with actuators that are attached to things. Therefore, the following two functionalities should be supported: (1) data producers describing and registering the protocol to communicate with the device; and (2) IoT RESTful services creating requests and understanding response with the registered device protocol.
The potential workflow would be (1) a thing (a data producer) creates itself and an actuator attached to it in an IoT RESTful service, (2) a user retrieves the capabilities document from the IoT RESTful service, (3) the user retrieves the actuator resource and understands the acceptable parameters of the actuator, (4) with the parameter information, the user creates a task entity (like creating other resources in the IoT RESTful servie) with specified input values for parameters and the time that the user wants this task to be executed, (5) after the user creates the task in the IoT RESTful service, the IoT RESTful service creates a request in the device protocol based on user’s input value and sends the request to the thing at the user-specified time, (6) the thing then executes the request and returns a response to the IoT RESTful service in the device protocol, and finally, (7) the IoT RESTful service parses the response and updates the status property in the user-created task entity.
To sum up, the concept of the IoT RESTful TaskingCapability profile is to provide a RESTful interface for users to submit tasks to control actuators. As IoT RESTful services handle the communication in device protocols, users can easily control Internet of Things with a simple and unified RESTful interface.
The detail explanation about the resources in the taskingCapability profile can be found in datamodel.html#TaskingCapabilityProfile.
The protocol of creating a resource in the taskingCapability profile is similar to that in the datastream profile (api.html#Create_DatastreamProfile). Only when creating a taskingCapability entity, the request needs to specify the device protocol (datamodel.html#TaskingCapability), which is not visible to users in the Read response.
Example: Create a TaskingCapability entity
POST /SWIOT_V0.9/TaskingCapabilities HTTP/1.1
Host: demo.student.geocens.ca:8080
Content-Type: application/json
{
"Thing":{"ID":1},
"Description":"this is TaskingCapability 1",
"Parameters":[
{
"ParameterID":"paramOn",
"Description":"on",
"Use":"mandatory",
"Definition":
{
"InputType":"Integer",
"UnitOfMeasurement":"brightness degrees",
"AllowedValues":[{"Min":"10","Max":"15"}]
}
}],
"Protocols":[
{
"HTTPMethod":"POST",
"AbsoluteResourcePath":"http://path.com",
"MessageBody":"lamp-id=1&on={paramOn}"
}],
"Actuator":
{
"Metadata":"This is an actuator"
}
}
In addition, the behavior of creating a task entity is the same as submitting a tasking job to the thing. The IoT RESTful service will compose a request in the device protocol (from the protocol property in the TaskingCapability entity), and send the request at the specified time point. While composing a request in a device protocol, the values of inputs in the task entity will be used to replace the "{ParameterID}" in the device protocol. For instance, the following example requires an IoT RESTful service to replace "{paramOn}" string in the device protocol with the input value 200. Therefore, with the above taskingCapability entity, the IoT RESTful service will send a HTTP POST reqeust to http://path.com with a messageBody as "lamp-id=1&on=200" at 2013-04-18T16:15:00-0700.
When creating a task entity, if the value of Time property is in the past, the task should be sent to the thing immediately.
Example: Create a Task entity
POST /SWIOT_V0.9/Tasks HTTP/1.1
Host: demo.student.geocens.ca:8080
Content-Type: application/json
{
"TaskingCapability":{"ID":5},
"Inputs":[
{
"ParameterID":"paramOn",
"Value":200
}],
"Time":"2013-04-18T16:15:00-0700"
}
Upon successfully creating an entity, the service response must contain a Location header that contains the URL of the created entity. Upon successful completion the service must respond with 201 Created. Regarding all the HTTP status code, please refer to the HTTP Status Code section.
The protocol of reading a resource in the TaskingCapability profile is the same as that in the Datastream profile (api.html#Read_DatastreamProfile). The only exception is that when reading a TaskingCapability entity, the device protocol is not visible to users in the response.
The detail explanation about the encodings of resources in the TaskingCapability profile can be found in datamodel.html#TaskingCapabilityProfile.
Upon successfully retrieve resources, the service service must respond with 200 OK. Regarding all the HTTP status code, please refer to the HTTP Status Code section.
The protocol of updating a resource in the TaskingCapability profile is the same as that in the Datastream profile (api.html#Update_DatastreamProfile). Only the Status property in a Task entity is a service-controlled property and cannot be updated by users. IoT RESTful service updates the status based on responses from the thing.
Upon successful completion, the service service must respond with 200 OK or 204 No Content. Regarding all the HTTP status code, please refer to the HTTP Status Code section.
The protocol of deleting a resource in the TaskingCapability profile is the same as that in the Datastream profile (api.html#Delete_DatastreamProfile). When deleting a Task entity that hasn't been triggered, the service must remove the corresponding thread of sending task as well.
On successful completion of the delete, the response must be 200 OK, 202 Accepted, or 204 No Content and contain an empty body. Regarding all the HTTP status code, please refer to the HTTP Status Code section.
The IoT RESTful service interface provides interfaces for users to perform CRUD actions on resources through different HTTP methods. However, as IoT devices are usually resource-constrained, handling a large number of communications may be not practical. This section describes how an IoT RESTful service can optionally support executing multiple operations sent in a single HTTP request through the use of batch processing. This section covers both how batch operations are represented and processed.
A batch request is represented as a Multipart MIME v1.0 message [RFC2046], a standard format allowing the representation of multiple parts, each of which may have a different content type, within a single request.
Batch requests allow grouping multiple operations, as described in DatastreamProfile and TaskingCapabilityProfile, into a single HTTP request payload.
Batch Requests are submitted as a single HTTP POST request to the $batch endpoint of a service. The batch request must contain a Content-Type header specifying a content type of "multipart/mixed" and a boundary specification. The example below shows a GUID as a boundary and "OData/OData.svc"for the URI of the service.
POST OData/OData.svc/$batch HTTP/1.1
Host: services.odata.org
DataServiceVersion: 1.0
MaxDataServiceVersion: 2.0
Content-Type: multipart/mixed; boundary=batch(36522ad7-fc75-4b56-8c71-56071383e77b)
BATCH_REQUEST_BODY
The batch request boundary must be quoted if it contains any of the following special characters:
( ) < > @ , ; : / " [ ] ? =
In addition to the Content-Type header, the Batch request may contain DataServiceVersion headers or other HTTP Headers which apply to the entire Batch Request as appropriate. In the example above, DataServiceVersion has a value of 1.0 representing the version of the OData specification used to generate the request, and MaxDataServiceVersion has a value of 2.0 representing the maximum response version the client is prepared to accept.
The body of a Batch Request is made up of an ordered series of retrieve operations (as described in [OData-Operations] ) and/or ChangeSets. A ChangeSet is an atomic unit of work that is made up of an unordered group of one or more of the insert, update or delete operations described in < a href="/developers/protocols/operations">[OData:Operations]. ChangeSets cannot contain retrieve requests and cannot be nested (i.e. a ChangeSet cannot contain a ChangeSet).
In the Batch request body, each retrieve request and ChangeSet is represented as a distinct MIME part and is separated by the boundary marker defined in the Content-Type header of the request. The contents of the MIME part which represents a ChangeSet is itself a multipart MIME document with one part for each operation that makes up the ChangeSet.
Each MIME part representing a retrieve request or ChangeSet within the Batch includes both Content-Type and Content-Transfer-Encoding MIME headers as seen in the examples below. The batch request boundary is the name specified in the Content-Type Header for the batch.
The example below shows a sample batch request that contains the following operations in the order listed:
Note: In the example, request bodies are excluded in favor of English descriptions inside '<>' brackets to simplify the example.
POST OData/OData.svc/$batch HTTP/1.1
Host: services.odata.org
Content-Type: multipart/mixed; boundary=batch(36522ad7-fc75-4b56-8c71-56071383e77b)
--batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: application/http
Content-Transfer-Encoding:binary
GET /service/Customers('ALFKI') HTTP/1.1
Host: host
--batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: multipart/mixed; boundary=changeset_77162fcd-b8da-41ac-a9f8-9357efbbd621
Content-Length: ###
--changeset(77162fcd-b8da-41ac-a9f8-9357efbbd621)
Content-Type: application/http
Content-Transfer-Encoding: binary
POST /service/Customers HTTP/1.1
Host: host
Content-Type: application/atom+xml;type=entry
Content-Length: ###
--changeset(77162fcd-b8da-41ac-a9f8-9357efbbd621)
Content-Type: application/http
Content-Transfer-Encoding:binary
PUT /service/Customers('ALFKI') HTTP/1.1
Host: host
Content-Type: application/json
If-Match: xxxxx
Content-Length: ###
--changeset(77162fcd-b8da-41ac-a9f8-9357efbbd621)--
--batch(36522ad7-fc75-4b56-8c71-56071383e77b)
Content-Type: application/http
Content-Transfer-Encoding:binary
GET service/Products HTTP/1.1
Host: host
--batch(36522ad7-fc75-4b56-8c71-56071383e77b)--
If a MIME part representing an Insert request within a ChangeSet includes a Content-ID header, then the new entity may be referenced by subsequent requests within the same ChangeSet by referring to the Content-ID value prefixed with a "$" character. When used in this way, $
Requests within a batch are evaluated according to the same semantics used when the request appears outside of the context of a batch.
The order of ChangeSets and retrieve requests within a Batch request is significant as it states the order in which the service processes the components of the Batch. The order of requests within a ChangeSet is not significant such that a service may process the requests within a ChangeSet in any order.
All operations in a ChangeSet represent a single change unit so the server must successfully process and apply all the requests in the ChangeSet or else apply none of the requests in the ChangeSet. It is up to the service to define rollback semantics to undo any requests within a ChangeSet that may have been applied before another request in that same ChangeSet failed and thereby honor this all-or-nothing requirement.
If the set of HTTP Request headers of a Batch request are valid (the Content-Type is set to multipart/mixed, etc.) the server returns a 202 Accepted HTTP response code to indicate that the request has been accepted for processing, but that the processing has not yet been completed. The requests within the Batch request body may subsequently fail or be malformed; however, this mechanism enables clients of a Batch implementation to stream the results of a Batch request without having to first wait for all requests to be processed.
If the service receives a Batch request with an invalid set of headers it returns a 4xx response code and no further processing of the batch is performed.
The batch response, as shown in the example below, must contain a Content-Type header specifying a content type of multipart/mixed and a batch boundary specification which may be different than the batch boundary that was used in the corresponding request.
HTTP/1.1 202 Accepted DataServiceVersion: 1.0 Content-Length: 1254 Content-Type: multipart/mixed; boundary=batch(36522ad7-fc75-4b56-8c71-56071383e77b)
Within the body of the batch response is a response for each retrieve request and ChangeSet that was in the associated Batch request. The order of responses in the response body must match the order of requests in the Batch request. Each response includes a Content-Type header with a value of "application/http", and a Content-Transfer-Encoding MIME header with a value of "binary".
A response to a retrieve request is formatted exactly as it would have appeared outside of a batch as described in [OData-Operations] .
The body of a ChangeSet response is either a response for all the successfully processed change request within the ChangeSet, formatted exactly as it would have appeared outside of a batch, as described in [OData-Operations] , or a single response indicating a failure of the entire ChangeSet, as described in [OData-Operations].
For example, referencing the batch request in section 2.2, assume all the requests except the final retrieve request succeed. In this case the Batch response body would be as shown in the following example.
HTTP/1.1 202 Accepted
DataServiceVersion: 1.0
Content-Length: ####
Content-Type: multipart/mixed; boundary=batch(36522ad7-fc75-4b56-8c71-56071383e77b)
--batch(36522ad7-fc75-4b56-8c71-56071383e77b)
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 200 Ok
Content-Type: application/atom+xml;type=entry
Content-Length: ###
--batch(36522ad7-fc75-4b56-8c71-56071383e77b)
Content-Type: multipart/mixed; boundary=changeset(77162fcd-b8da-41ac-a9f8-9357efbbd621)
Content-Length: ###
--changeset(77162fcd-b8da-41ac-a9f8-9357efbbd621)
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 201 Created
Content-Type: application/atom+xml;type=entry
Location: http://host/service.svc/Customer('POIUY')
Content-Length: ###
--changeset(77162fcd-b8da-41ac-a9f8-9357efbbd621)
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 204 No Content
Host: host
--changeset(77162fcd-b8da-41ac-a9f8-9357efbbd621)--
--batch(36522ad7-fc75-4b56-8c71-56071383e77b)
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 404 Not Found
Content-Type: application/xml
Content-Length: ###
--batch(36522ad7-fc75-4b56-8c71-56071383e77b)--
All status codes used in the IoT RESTful API follow the standard HTTP status codes. A high-level explanation of the status codes is as follows:
HTTP Status Code | Description |
---|---|
2xx | Success |
3xx | Redirection |
4xx | Client-side Error |
5xx | Server-side Error |