Thngs and products can have many properties, which are simple typed key/value pairs of data that are attached to a resource. What sets properties apart from custom fields, is the fact that each property update is timestamped and stored historically, which allows you to retrieve the set of all the values of a property during any specific time range and infer trends over time.

The most common use case of properties is to store dynamic information about a Thng, which is likely to change over time. This is particularly useful for items with frequently changing unique data, where such as sensor readings or item state. For longer-lived data that does not require a history, customFields should be used instead.


API Status
General Availability:
/thngs/:thngId/properties
/thngs/:thngId/properties/:key
/products/:productId/properties
/products/:productId/properties/:key


PropertyDocument Data Model

Thng or product properties take the form of an array of PropertyDocument objects that always contain a key (case-insensitive, gets converted to lowercase automatically by the API), a value, and a timestamp.

.key (string, required)
    The property key-value key.

.value (string, integer, boolean, required)
    The property key-value value.

.timestamp (integer, read-only)
    The timestamp of the property update.

.createdAt (integer, read-only)
    Timestamp when the resource was created.
{
  "type": "object",
  "description": "An object representing a single property update. The `value` property may be a string or integer.",
  "required": ["key", "value"],
  "properties": {
    "key": {
      "type": "string",
      "description": "The property key-value key."
    },
    "value": {
      "type": [ "string", "integer", "boolean" ],
      "description": "The property key-value value."
    },
    "timestamp": {
      "type": "integer",
      "description": "The timestamp of the property update.",
      "readOnly": true,
      "minimum": 0
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true,
      "minimum": 0
    }
  },
  "x-filterable-fields": [ "timestamp" ]
}
{
  "createdAt": 1507734727579,
  "timestamp": 1507734727579,
  "key": "scan_count",
  "value": 38
}

Property Units

If the property type is a number, then you can visualize it as a graph in the dashboard. For this reason, we suggest you don't include the unit in value, but rather encode it in the property name. For example:

{
  "key": "temperature_celsius",
  "value": 21
}

If the optional timestamp is not provided when updating the property, it is automatically set by the API at the moment when the request has been processed. If multiple values are being uploaded in one request, they should differentiate each other by specifying timestamps to accompany each object in the payload showing when each value was created.


Create/Update Properties

Create

When a resource is created, you can specify an initial set of properties and their values with a POST request.

POST /thngs/:thngId/properties
POST /products/:productId/properties

Update

To update those properties or to add new ones to the resource, use PUT on the following endpoints, with an array of PropertyDocument JSON object containing the changed fields.

PUT /thngs/:thngId/properties
PUT /products/:productId/properties

If the properties being updated do not exist, they are created automatically by this request and no error/notification will be returned.

📘

Note

Exceptionally, to cope with the limitations of mobile network providers you can use both POST and PUT to create/update multiple properties, which behave identically in this particular case.

However, only the PUT method may be used to update properties by name.

The returned payload is an array of the properties updated, not all of the Thng's the properties.

PUT /thngs/:thngId/properties
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

[ PropertyDocument, ... ]
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://$EVT_API_DOMAIN/thngs/UEqs3aV3NyYGVRxhmnphVGdh/properties' \
  -d '[{
    "key": "temperature_celsius",
    "value": 33
  }, {
    "key": "humidity_percent",
    "value": 71
  }]'
const thngId = 'UEqs3aV3NyYGVRxhmnphVGdh';

const payload = [{
  key: 'temperature_celsius',
  value: 33
}, {
  key: 'humidity_percent',
  value: 71
}];

user.thng(thngId).property()
  .update(payload)
  .then(console.log);
String thngId = "UEqs3aV3NyYGVRxhmnphVGdh";

List<Property<?>> properties = new ArrayList<>();
properties.add(new NumberProperty("temperature_celsius", 33));
properties.add(new NumberProperty("humidity_percent", 71));

// Add to Thng
apiManager.thngService().propertiesCreator(thngId, properties).execute();

// Add to product
apiManager.productService().propertiesCreator(productId, properties).execute();
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "timestamp": 1346860078782,
    "createdAt": 1346860078782,
    "key": "temperature_celsius",
    "value": 33
  },
  {
    "timestamp": 1346860078782,
    "createdAt": 1346860078782,
    "key": "humidity_percent",
    "value": 71
  }
]

Read All Properties

GET /thngs/:thngId/properties
GET /products/:productId/properties

Retrieve a list of all the properties of a Thng or product by its ID. The result may be paginated if there are more than 30 items.

GET /thngs/:thngId/properties
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://$EVT_API_DOMAIN/thngs/UEqs3aV3NyYGVRxhmnphVGdh/properties'
const thngId = 'UEqs3aV3NyYGVRxhmnphVGdh';

user.thng(thngId).property()
  .read()
  .then(console.log);
// thng from previous thngService reader invocation
Map<String, Object> properties = thng.getProperties();
for(Map.Entry<String, Object> entry : properties.entrySet()) {
    String key = entry.getKey();
    String value = entry.getValue().toString();
    System.out.println("key=" + key + " value=" + value);
}

The returned payload is the complete array of all the properties with the latest recorded value of each.

HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "key": "temperature_celsius",
    "value": 33,
    "timestamp": 1346856615704,
    "createdAt": 1346856615704
  }, 
  {
    "key": "humidity_percent",
    "value": 71,
    "timestamp": 1346856615704,
    "createdAt": 1346856615704
  }
]

Create/Update a Single Property

Properties can be updated individually using their dedicated endpoints. If the property does not exist, it will be created automatically by this request and no error/notification will be returned.

PUT /thngs/:thngId/properties/:key
PUT /products/:productId/properties/:key

It's also possible to upload cached values of a property in batches by specifying timestamps for each data item object.

PUT /thngs/:thngId/properties/:key
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

PropertyDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://$EVT_API_DOMAIN/thngs/UEqs3aV3NyYGVRxhmnphVGdh/properties/temperature_celsius' \
  -d '[{
    "value": 23
  }]'
const thngId = 'UEqs3aV3NyYGVRxhmnphVGdh';
const key = 'temperature_celsius';
const value = 23;

// in a single request
user.thng(thngId).property(key)
  .update(value)
  .then(console.log);
String value = "UEqs3aV3NyYGVRxhmnphVGdh";
String key = "temperature_celsius";
String value = 23;

apiManager.thngService().propertyUpdater(thngId, key, value).execute();
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "timestamp": 1347549410295,
    "createdAt": 1347549410295,
    "value": 23
  }
]

You can also provide data via three different payload types to the property() update method:

const thngId = 'UEqs3aV3NyYGVRxhmnphVGdh';
const key = 'temperature_celsius';

// 1. Just the value (simple values only - string, number, boolean. 
// (Use 2. for arrays and objects)
let payload = 23;

// or 2. An object (similar to the API), allowing you to specify the timestamp:
payload = {
  value: 23,
  timestamp: 142963528000,
};

// or 3. In bulk, with multiple objects
payload = [
  { value: 23, timestamp: 142963528000 },
  { value: 21, timestamp: 142963535600 }
];

// Send the update request
user.thng(thngId).property(key)
  .update(payload)
  .then(console.log);

Read a Property's History

To retrieve the history of values of a specific property, you must access the unique endpoint for each property. By default, all the updates of this property are returned using pagination in a reverse history fashion (the first element of the returned array is the latest update).

GET /thngs/:thngId/properties/:key
GET /products/:productId/properties/:key

If you attempt to fetch the history of values for a property that doesn't exist you will receive an HTTP 200 status code and an empty list as a result.

GET /thngs/:thngId/properties/:key[?from=Timestamp&to=Timestamp]
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://$EVT_API_DOMAIN/thngs/UEqs3aV3NyYGVRxhmnphVGdh/properties/temperature_celsius'
const thngId = 'UEqs3aV3NyYGVRxhmnphVGdh';
const key = 'temperature_celsius';

// in a single request
user.thng(thngId).property(key)
  .read()
  .then(console.log);
String key = "temperature_celsius";

// thng from previous thngService reader invocation
Map<String, Object> properties = thng.getProperties();
String value = properties.get(key).toString();

For accessing only a subset of all the updates to a single property, you can specify a time range using ?from=Timestamp and ?to=Timestamp to specify respectively the beginning and end of the time window. You will receive an array of all the property updates during this time.

HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "timestamp":1346860078782,
    "createdAt":1346860078782,
    "value":33
  },
  {
    "timestamp":1346856870595,
    "createdAt":1346856870595,
    "value":29
  },
  {
    "timestamp":1346856779570,
    "createdAt":1346856779570,
    "value":32
  }
]

Delete a Property

Property updates are not uniquely identified (do not have their own URL). Therefore, the only way to delete data points is to append the ?to=Timestamp URL query parameter to specify a point in time before which all the data points will be removed.

DELETE /thngs/:thngId/properties/:key
DELETE /products/:productId/properties/:key

❗️

Warning

If the ?to= parameter is not specified, ALL updates of this property will be deleted!

DELETE /thngs/:thngId/properties/:key[?to=Timestamp]
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://$EVT_API_DOMAIN/thngs/UEqs3aV3NyYGVRxhmnphVGdh/properties/temperature_celsius'
const thngId = 'UEqs3aV3NyYGVRxhmnphVGdh';
const key = 'temperature_celsius';

// in a single request
operator.thng(thngId).property(key)
  .delete();
String thngId = "UEqs3aV3NyYGVRxhmnphVGdh";
String key = "temperature_celsius";

apiManager.thngService().propertyDeleter(thngId, key).execute();
HTTP/1.1 200 OK

Delete all Properties

Delete all the properties of a Thng or product. Example below is for Thngs.

DELETE /thngs/:thngId/properties
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://$EVT_API_DOMAIN/thngs/UEqs3aV3NyYGVRxhmnphVGdh/properties'
const thngId = 'UEqs3aV3NyYGVRxhmnphVGdh';

operator.thng(thngId).property()
  .delete();
HTTP/1.1 200 OK

System Properties

In addition, the EVRYTHNG Platform automatically sets some system properties. They start with ~ (tilde) and are read-only. Apart from that they behave exactly like other properties.

The following lists the currently available system properties available on Thngs:

  • ~connected - Boolean

Set to true if the Thng is updated with its own API key and if it is currently connected to the pub/sub broker through MQTT or WebSockets. The property is created on the Thng first connection and updated every time the Thng disconnects or connects again.